fix(core): host bindings and host listeners for animations
Host bindings / listeners for animation properties should use the renderer of the component view.
This commit is contained in:
@ -7,9 +7,10 @@
|
||||
*/
|
||||
|
||||
import {isDevMode} from '../application_ref';
|
||||
import {RendererTypeV2, RendererV2} from '../render/api';
|
||||
import {SecurityContext} from '../security';
|
||||
|
||||
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementHandleEventFn, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData} from './types';
|
||||
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementHandleEventFn, NodeData, NodeDef, NodeFlags, NodeType, OutputDef, OutputType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData, asProviderData} from './types';
|
||||
import {checkAndUpdateBinding, dispatchEvent, elementEventFullName, filterQueryId, getParentRenderElement, resolveViewDefinition, sliceErrorStack, splitMatchedQueriesDsl, splitNamespace} from './util';
|
||||
|
||||
const NOOP: any = () => {};
|
||||
@ -34,20 +35,20 @@ export function anchorDef(
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount,
|
||||
bindings: [],
|
||||
disposableCount: 0,
|
||||
outputs: [],
|
||||
element: {
|
||||
ns: undefined,
|
||||
name: undefined,
|
||||
attrs: undefined,
|
||||
outputs: [], template, source,
|
||||
// will bet set by the view definition
|
||||
component: undefined,
|
||||
attrs: undefined, template, source,
|
||||
componentProvider: undefined,
|
||||
componentView: undefined,
|
||||
componentRendererType: undefined,
|
||||
publicProviders: undefined,
|
||||
allProviders: undefined, handleEvent
|
||||
},
|
||||
@ -65,8 +66,13 @@ export function elementDef(
|
||||
fixedAttrs: [string, string][] = [],
|
||||
bindings?:
|
||||
([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
|
||||
[BindingType.ElementAttribute | BindingType.ElementProperty, string, SecurityContext])[],
|
||||
outputs?: (string | [string, string])[], handleEvent?: ElementHandleEventFn): NodeDef {
|
||||
[
|
||||
BindingType.ElementAttribute | BindingType.ElementProperty |
|
||||
BindingType.ComponentHostProperty,
|
||||
string, SecurityContext
|
||||
])[],
|
||||
outputs?: ([string, string])[], handleEvent?: ElementHandleEventFn,
|
||||
componentView?: () => ViewDefinition, componentRendererType?: RendererTypeV2): NodeDef {
|
||||
if (!handleEvent) {
|
||||
handleEvent = NOOP;
|
||||
}
|
||||
@ -93,29 +99,35 @@ export function elementDef(
|
||||
break;
|
||||
case BindingType.ElementAttribute:
|
||||
case BindingType.ElementProperty:
|
||||
case BindingType.ComponentHostProperty:
|
||||
securityContext = <SecurityContext>entry[2];
|
||||
break;
|
||||
}
|
||||
bindingDefs[i] = {type: bindingType, ns, name, nonMinifiedName: name, securityContext, suffix};
|
||||
}
|
||||
outputs = outputs || [];
|
||||
const outputDefs: ElementOutputDef[] = new Array(outputs.length);
|
||||
const outputDefs: OutputDef[] = new Array(outputs.length);
|
||||
for (let i = 0; i < outputs.length; i++) {
|
||||
const output = outputs[i];
|
||||
let target: string;
|
||||
let eventName: string;
|
||||
if (Array.isArray(output)) {
|
||||
[target, eventName] = output;
|
||||
} else {
|
||||
eventName = output;
|
||||
}
|
||||
outputDefs[i] = {eventName: eventName, target: target};
|
||||
const [target, eventName] = outputs[i];
|
||||
outputDefs[i] = {
|
||||
type: OutputType.ElementOutput,
|
||||
target: <any>target, eventName,
|
||||
propName: undefined
|
||||
};
|
||||
}
|
||||
fixedAttrs = fixedAttrs || [];
|
||||
const attrs = <[string, string, string][]>fixedAttrs.map(([namespaceAndName, value]) => {
|
||||
const [ns, name] = splitNamespace(namespaceAndName);
|
||||
return [ns, name, value];
|
||||
});
|
||||
// This is needed as the jit compiler always uses an empty hash as default RendererTypeV2,
|
||||
// which is not filled for host views.
|
||||
if (componentRendererType && componentRendererType.encapsulation == null) {
|
||||
componentRendererType = null;
|
||||
}
|
||||
if (componentView) {
|
||||
flags |= NodeFlags.HasComponent;
|
||||
}
|
||||
return {
|
||||
type: NodeType.Element,
|
||||
// will bet set by the view definition
|
||||
@ -124,21 +136,21 @@ export function elementDef(
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount,
|
||||
bindings: bindingDefs,
|
||||
disposableCount: outputDefs.length,
|
||||
outputs: outputDefs,
|
||||
element: {
|
||||
ns,
|
||||
name,
|
||||
attrs,
|
||||
outputs: outputDefs, source,
|
||||
source,
|
||||
template: undefined,
|
||||
// will bet set by the view definition
|
||||
component: undefined,
|
||||
componentProvider: undefined, componentView, componentRendererType,
|
||||
publicProviders: undefined,
|
||||
allProviders: undefined, handleEvent,
|
||||
},
|
||||
@ -174,21 +186,24 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El
|
||||
renderer.setAttribute(el, name, value, ns);
|
||||
}
|
||||
}
|
||||
if (elDef.outputs.length) {
|
||||
for (let i = 0; i < elDef.outputs.length; i++) {
|
||||
const output = elDef.outputs[i];
|
||||
const handleEventClosure = renderEventHandlerClosure(
|
||||
view, def.index, elementEventFullName(output.target, output.eventName));
|
||||
const disposable =
|
||||
<any>renderer.listen(output.target || el, output.eventName, handleEventClosure);
|
||||
view.disposables[def.disposableIndex + i] = disposable;
|
||||
return el;
|
||||
}
|
||||
|
||||
export function listenToElementOutputs(view: ViewData, compView: ViewData, def: NodeDef, el: any) {
|
||||
for (let i = 0; i < def.outputs.length; i++) {
|
||||
const output = def.outputs[i];
|
||||
const handleEventClosure = renderEventHandlerClosure(
|
||||
view, def.index, elementEventFullName(output.target, output.eventName));
|
||||
let listenTarget = output.target;
|
||||
let listenerView = view;
|
||||
if (output.target === 'component') {
|
||||
listenTarget = null;
|
||||
listenerView = compView;
|
||||
}
|
||||
const disposable =
|
||||
<any>listenerView.renderer.listen(listenTarget || el, output.eventName, handleEventClosure);
|
||||
view.disposables[def.outputIndex + i] = disposable;
|
||||
}
|
||||
return {
|
||||
renderElement: el,
|
||||
embeddedViews: (def.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined,
|
||||
projectedViews: undefined
|
||||
};
|
||||
}
|
||||
|
||||
function renderEventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||
@ -223,7 +238,8 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
||||
return;
|
||||
}
|
||||
const binding = def.bindings[bindingIdx];
|
||||
const renderNode = asElementData(view, def.index).renderElement;
|
||||
const elData = asElementData(view, def.index);
|
||||
const renderNode = elData.renderElement;
|
||||
const name = binding.name;
|
||||
switch (binding.type) {
|
||||
case BindingType.ElementAttribute:
|
||||
@ -238,6 +254,9 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
||||
case BindingType.ElementProperty:
|
||||
setElementProperty(view, binding, renderNode, name, value);
|
||||
break;
|
||||
case BindingType.ComponentHostProperty:
|
||||
setElementProperty(elData.componentView, binding, renderNode, name, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
// regular values
|
||||
flags: 0,
|
||||
childFlags: 0,
|
||||
@ -28,7 +28,7 @@ export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
|
||||
references: {}, ngContentIndex,
|
||||
childCount: 0,
|
||||
bindings: [],
|
||||
disposableCount: 0,
|
||||
outputs: [],
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
|
@ -15,7 +15,7 @@ import {ViewEncapsulation} from '../metadata/view';
|
||||
import {Renderer as RendererV1, RendererFactoryV2, RendererTypeV2, RendererV2} from '../render/api';
|
||||
|
||||
import {createChangeDetectorRef, createInjector, createRendererV1, createTemplateRef, createViewContainerRef} from './refs';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, DirectiveOutputDef, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
||||
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, OutputDef, OutputType, ProviderData, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
||||
import {checkAndUpdateBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
|
||||
|
||||
const RendererV1TokenKey = tokenKey(RendererV1);
|
||||
@ -31,8 +31,7 @@ const NOT_CREATED = new Object();
|
||||
export function directiveDef(
|
||||
flags: NodeFlags, matchedQueries: [string | number, QueryValueType][], childCount: number,
|
||||
ctor: any, deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
||||
outputs?: {[name: string]: string}, component?: () => ViewDefinition,
|
||||
rendererType?: RendererTypeV2): NodeDef {
|
||||
outputs?: {[name: string]: string}): NodeDef {
|
||||
const bindings: BindingDef[] = [];
|
||||
if (props) {
|
||||
for (let prop in props) {
|
||||
@ -46,15 +45,16 @@ export function directiveDef(
|
||||
};
|
||||
}
|
||||
}
|
||||
const outputDefs: DirectiveOutputDef[] = [];
|
||||
const outputDefs: OutputDef[] = [];
|
||||
if (outputs) {
|
||||
for (let propName in outputs) {
|
||||
outputDefs.push({propName, eventName: outputs[propName]});
|
||||
outputDefs.push(
|
||||
{type: OutputType.DirectiveOutput, propName, target: null, eventName: outputs[propName]});
|
||||
}
|
||||
}
|
||||
return _def(
|
||||
NodeType.Directive, flags, matchedQueries, childCount, ProviderType.Class, ctor, ctor, deps,
|
||||
bindings, outputDefs, component, rendererType);
|
||||
bindings, outputDefs);
|
||||
}
|
||||
|
||||
export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef {
|
||||
@ -70,14 +70,8 @@ export function providerDef(
|
||||
export function _def(
|
||||
type: NodeType, flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
||||
childCount: number, providerType: ProviderType, token: any, value: any,
|
||||
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: DirectiveOutputDef[],
|
||||
component?: () => ViewDefinition, rendererType?: RendererTypeV2): NodeDef {
|
||||
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: OutputDef[]): NodeDef {
|
||||
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
||||
// This is needed as the jit compiler always uses an empty hash as default RendererTypeV2,
|
||||
// which is not filled for host views.
|
||||
if (rendererType && rendererType.encapsulation == null) {
|
||||
rendererType = null;
|
||||
}
|
||||
if (!outputs) {
|
||||
outputs = [];
|
||||
}
|
||||
@ -96,9 +90,6 @@ export function _def(
|
||||
}
|
||||
return {flags, token, tokenKey: tokenKey(token)};
|
||||
});
|
||||
if (component) {
|
||||
flags = flags | NodeFlags.HasComponent;
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
@ -108,20 +99,14 @@ export function _def(
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references,
|
||||
ngContentIndex: undefined, childCount, bindings,
|
||||
disposableCount: outputs.length,
|
||||
ngContentIndex: undefined, childCount, bindings, outputs,
|
||||
element: undefined,
|
||||
provider: {
|
||||
type: providerType,
|
||||
token,
|
||||
tokenKey: tokenKey(token), value,
|
||||
deps: depDefs, outputs, component, rendererType
|
||||
},
|
||||
provider: {type: providerType, token, tokenKey: tokenKey(token), value, deps: depDefs},
|
||||
text: undefined,
|
||||
pureExpression: undefined,
|
||||
query: undefined,
|
||||
@ -149,17 +134,17 @@ export function createPipeInstance(view: ViewData, def: NodeDef): any {
|
||||
|
||||
export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
|
||||
// components can see other private services, other directives can't.
|
||||
const allowPrivateServices = (def.flags & NodeFlags.HasComponent) > 0;
|
||||
const allowPrivateServices = (def.flags & NodeFlags.IsComponent) > 0;
|
||||
const providerDef = def.provider;
|
||||
// directives are always eager and classes!
|
||||
const instance =
|
||||
createClass(view, def.parent, allowPrivateServices, def.provider.value, def.provider.deps);
|
||||
if (providerDef.outputs.length) {
|
||||
for (let i = 0; i < providerDef.outputs.length; i++) {
|
||||
const output = providerDef.outputs[i];
|
||||
if (def.outputs.length) {
|
||||
for (let i = 0; i < def.outputs.length; i++) {
|
||||
const output = def.outputs[i];
|
||||
const subscription = instance[output.propName].subscribe(
|
||||
eventHandlerClosure(view, def.parent.index, output.eventName));
|
||||
view.disposables[def.disposableIndex + i] = subscription.unsubscribe.bind(subscription);
|
||||
view.disposables[def.outputIndex + i] = subscription.unsubscribe.bind(subscription);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
@ -371,7 +356,7 @@ export function resolveDep(
|
||||
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
|
||||
let compView: ViewData;
|
||||
if (allowPrivateServices) {
|
||||
compView = asProviderData(view, elDef.element.component.index).componentView;
|
||||
compView = asElementData(view, elDef.index).componentView;
|
||||
} else {
|
||||
compView = view;
|
||||
while (compView.parent && !isComponentView(compView)) {
|
||||
@ -396,8 +381,8 @@ function checkAndUpdateProp(
|
||||
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
||||
}
|
||||
if (changed) {
|
||||
if (def.flags & NodeFlags.HasComponent) {
|
||||
const compView = providerData.componentView;
|
||||
if (def.flags & NodeFlags.IsComponent) {
|
||||
const compView = asElementData(view, def.parent.index).componentView;
|
||||
if (compView.def.flags & ViewFlags.OnPush) {
|
||||
compView.state |= ViewState.ChecksEnabled;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ function _pureExpressionDef(type: PureExpressionType, propertyNames: string[]):
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
// regular values
|
||||
flags: 0,
|
||||
childFlags: 0,
|
||||
@ -53,7 +53,7 @@ function _pureExpressionDef(type: PureExpressionType, propertyNames: string[]):
|
||||
references: {},
|
||||
ngContentIndex: undefined,
|
||||
childCount: 0, bindings,
|
||||
disposableCount: 0,
|
||||
outputs: [],
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
|
@ -31,7 +31,7 @@ export function queryDef(
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
// regular values
|
||||
flags,
|
||||
childFlags: 0,
|
||||
@ -42,7 +42,7 @@ export function queryDef(
|
||||
references: {},
|
||||
childCount: 0,
|
||||
bindings: [],
|
||||
disposableCount: 0,
|
||||
outputs: [],
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: undefined,
|
||||
|
@ -42,7 +42,7 @@ class ComponentFactory_ extends ComponentFactory<any> {
|
||||
injector: Injector, projectableNodes: any[][] = null,
|
||||
rootSelectorOrNode: string|any = null): ComponentRef<any> {
|
||||
const viewDef = resolveViewDefinition(this._viewClass);
|
||||
const componentNodeIndex = viewDef.nodes[0].element.component.index;
|
||||
const componentNodeIndex = viewDef.nodes[0].element.componentProvider.index;
|
||||
const view = Services.createRootView(
|
||||
injector, projectableNodes || [], rootSelectorOrNode, viewDef, EMPTY_CONTEXT);
|
||||
const component = asProviderData(view, componentNodeIndex).instance;
|
||||
@ -255,7 +255,7 @@ export function createInjector(view: ViewData, elDef: NodeDef): Injector {
|
||||
class Injector_ implements Injector {
|
||||
constructor(private view: ViewData, private elDef: NodeDef) {}
|
||||
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
||||
const allowPrivateServices = !!this.elDef.element.component;
|
||||
const allowPrivateServices = (this.elDef.flags & NodeFlags.HasComponent) !== 0;
|
||||
return Services.resolveDep(
|
||||
this.view, this.elDef, allowPrivateServices,
|
||||
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);
|
||||
|
@ -205,6 +205,7 @@ function debugCheckFn(
|
||||
const binding = nodeDef.bindings[i];
|
||||
const value = values[i];
|
||||
if ((binding.type === BindingType.ElementProperty ||
|
||||
binding.type === BindingType.ComponentHostProperty ||
|
||||
binding.type === BindingType.DirectiveProperty) &&
|
||||
checkBinding(view, nodeDef, i, value)) {
|
||||
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] =
|
||||
@ -273,7 +274,6 @@ class DebugContext_ implements DebugContext {
|
||||
private nodeDef: NodeDef;
|
||||
private elView: ViewData;
|
||||
private elDef: NodeDef;
|
||||
private compProviderDef: NodeDef;
|
||||
constructor(public view: ViewData, public nodeIndex: number) {
|
||||
if (nodeIndex == null) {
|
||||
this.nodeIndex = nodeIndex = 0;
|
||||
@ -292,21 +292,14 @@ class DebugContext_ implements DebugContext {
|
||||
}
|
||||
this.elDef = elDef;
|
||||
this.elView = elView;
|
||||
this.compProviderDef = elView ? this.elDef.element.component : null;
|
||||
}
|
||||
private get elOrCompView() {
|
||||
// Has to be done lazily as we use the DebugContext also during creation of elements...
|
||||
return asElementData(this.elView, this.elDef.index).componentView || this.view;
|
||||
}
|
||||
get injector(): Injector { return createInjector(this.elView, this.elDef); }
|
||||
get component(): any {
|
||||
if (this.compProviderDef) {
|
||||
return asProviderData(this.elView, this.compProviderDef.index).instance;
|
||||
}
|
||||
return this.view.component;
|
||||
}
|
||||
get context(): any {
|
||||
if (this.compProviderDef) {
|
||||
return asProviderData(this.elView, this.compProviderDef.index).instance;
|
||||
}
|
||||
return this.view.context;
|
||||
}
|
||||
get component(): any { return this.elOrCompView.component; }
|
||||
get context(): any { return this.elOrCompView.context; }
|
||||
get providerTokens(): any[] {
|
||||
const tokens: any[] = [];
|
||||
if (this.elDef) {
|
||||
@ -343,10 +336,7 @@ class DebugContext_ implements DebugContext {
|
||||
}
|
||||
}
|
||||
get componentRenderElement() {
|
||||
const view = this.compProviderDef ?
|
||||
asProviderData(this.elView, this.compProviderDef.index).componentView :
|
||||
this.view;
|
||||
const elData = findHostElement(view);
|
||||
const elData = findHostElement(this.elOrCompView);
|
||||
return elData ? elData.renderElement : undefined;
|
||||
}
|
||||
get renderNode(): any {
|
||||
|
@ -34,7 +34,7 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||
parent: undefined,
|
||||
renderParent: undefined,
|
||||
bindingIndex: undefined,
|
||||
disposableIndex: undefined,
|
||||
outputIndex: undefined,
|
||||
// regular values
|
||||
flags: 0,
|
||||
childFlags: 0,
|
||||
@ -43,7 +43,7 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||
matchedQueryIds: 0,
|
||||
references: {}, ngContentIndex,
|
||||
childCount: 0, bindings,
|
||||
disposableCount: 0,
|
||||
outputs: [],
|
||||
element: undefined,
|
||||
provider: undefined,
|
||||
text: {prefix: constants[0], source},
|
||||
|
@ -40,7 +40,7 @@ export interface ViewDefinition {
|
||||
reverseChildNodes: NodeDef[];
|
||||
lastRenderRootNode: NodeDef;
|
||||
bindingCount: number;
|
||||
disposableCount: number;
|
||||
outputCount: number;
|
||||
/**
|
||||
* Binary or of all query ids that are matched by one of the nodes.
|
||||
* This includes query ids from templates as well.
|
||||
@ -99,8 +99,8 @@ export interface NodeDef {
|
||||
|
||||
bindingIndex: number;
|
||||
bindings: BindingDef[];
|
||||
disposableIndex: number;
|
||||
disposableCount: number;
|
||||
outputIndex: number;
|
||||
outputs: OutputDef[];
|
||||
/**
|
||||
* references that the user placed on the element
|
||||
*/
|
||||
@ -151,12 +151,13 @@ export enum NodeFlags {
|
||||
AfterViewChecked = 1 << 7,
|
||||
HasEmbeddedViews = 1 << 8,
|
||||
HasComponent = 1 << 9,
|
||||
HasContentQuery = 1 << 10,
|
||||
HasStaticQuery = 1 << 11,
|
||||
HasDynamicQuery = 1 << 12,
|
||||
HasViewQuery = 1 << 13,
|
||||
LazyProvider = 1 << 14,
|
||||
PrivateProvider = 1 << 15,
|
||||
IsComponent = 1 << 10,
|
||||
HasContentQuery = 1 << 11,
|
||||
HasStaticQuery = 1 << 12,
|
||||
HasDynamicQuery = 1 << 13,
|
||||
HasViewQuery = 1 << 14,
|
||||
LazyProvider = 1 << 15,
|
||||
PrivateProvider = 1 << 16,
|
||||
}
|
||||
|
||||
export interface BindingDef {
|
||||
@ -173,11 +174,24 @@ export enum BindingType {
|
||||
ElementClass,
|
||||
ElementStyle,
|
||||
ElementProperty,
|
||||
ComponentHostProperty,
|
||||
DirectiveProperty,
|
||||
TextInterpolation,
|
||||
PureExpressionProperty
|
||||
}
|
||||
|
||||
export interface OutputDef {
|
||||
type: OutputType;
|
||||
target: 'window'|'document'|'body'|'component';
|
||||
eventName: string;
|
||||
propName: string;
|
||||
}
|
||||
|
||||
export enum OutputType {
|
||||
ElementOutput,
|
||||
DirectiveOutput
|
||||
}
|
||||
|
||||
export enum QueryValueType {
|
||||
ElementRef,
|
||||
RenderElement,
|
||||
@ -191,9 +205,11 @@ export interface ElementDef {
|
||||
ns: string;
|
||||
/** ns, name, value */
|
||||
attrs: [string, string, string][];
|
||||
outputs: ElementOutputDef[];
|
||||
template: ViewDefinition;
|
||||
component: NodeDef;
|
||||
componentProvider: NodeDef;
|
||||
componentRendererType: RendererTypeV2;
|
||||
// closure to allow recursive components
|
||||
componentView: ViewDefinitionFactory;
|
||||
/**
|
||||
* visible public providers for DI in the view,
|
||||
* as see from this element. This does not include private providers.
|
||||
@ -208,11 +224,6 @@ export interface ElementDef {
|
||||
handleEvent: ElementHandleEventFn;
|
||||
}
|
||||
|
||||
export interface ElementOutputDef {
|
||||
target: string;
|
||||
eventName: string;
|
||||
}
|
||||
|
||||
export type ElementHandleEventFn = (view: ViewData, eventName: string, event: any) => boolean;
|
||||
|
||||
export interface ProviderDef {
|
||||
@ -221,10 +232,6 @@ export interface ProviderDef {
|
||||
tokenKey: string;
|
||||
value: any;
|
||||
deps: DepDef[];
|
||||
outputs: DirectiveOutputDef[];
|
||||
rendererType: RendererTypeV2;
|
||||
// closure to allow recursive components
|
||||
component: ViewDefinitionFactory;
|
||||
}
|
||||
|
||||
export enum ProviderType {
|
||||
@ -250,11 +257,6 @@ export enum DepFlags {
|
||||
Value = 2 << 2,
|
||||
}
|
||||
|
||||
export interface DirectiveOutputDef {
|
||||
propName: string;
|
||||
eventName: string;
|
||||
}
|
||||
|
||||
export interface TextDef {
|
||||
prefix: string;
|
||||
source: string;
|
||||
@ -369,6 +371,7 @@ export function asTextData(view: ViewData, index: number): TextData {
|
||||
*/
|
||||
export interface ElementData {
|
||||
renderElement: any;
|
||||
componentView: ViewData;
|
||||
embeddedViews: ViewData[];
|
||||
// views that have been created from the template
|
||||
// of this element,
|
||||
@ -389,10 +392,7 @@ export function asElementData(view: ViewData, index: number): ElementData {
|
||||
*
|
||||
* Attention: Adding fields to this is performance sensitive!
|
||||
*/
|
||||
export interface ProviderData {
|
||||
instance: any;
|
||||
componentView: ViewData;
|
||||
}
|
||||
export interface ProviderData { instance: any; }
|
||||
|
||||
/**
|
||||
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
|
||||
|
@ -172,10 +172,10 @@ export function getParentRenderElement(view: ViewData, renderHost: any, def: Nod
|
||||
let renderParent = def.renderParent;
|
||||
if (renderParent) {
|
||||
const parent = def.parent;
|
||||
if (parent && (parent.type !== NodeType.Element || !parent.element.component ||
|
||||
(parent.element.component.provider.rendererType &&
|
||||
parent.element.component.provider.rendererType.encapsulation ===
|
||||
ViewEncapsulation.Native))) {
|
||||
if (parent &&
|
||||
(parent.type !== NodeType.Element || (parent.flags & NodeFlags.HasComponent) === 0 ||
|
||||
(parent.element.componentRendererType &&
|
||||
parent.element.componentRendererType.encapsulation === ViewEncapsulation.Native))) {
|
||||
// only children of non components, or children of components with native encapsulation should
|
||||
// be attached.
|
||||
return asElementData(view, def.renderParent.index).renderElement;
|
||||
|
@ -9,14 +9,14 @@
|
||||
import {ViewEncapsulation} from '../metadata/view';
|
||||
import {RendererTypeV2, RendererV2} from '../render/api';
|
||||
|
||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement, listenToElementOutputs} from './element';
|
||||
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
||||
import {appendNgContent} from './ng_content';
|
||||
import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAndUpdateDirectiveInline, createDirectiveInstance, createPipeInstance, createProviderInstance} from './provider';
|
||||
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
||||
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
|
||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||
import {ArgumentType, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList, asTextData} from './types';
|
||||
import {ArgumentType, ElementData, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList, asTextData} from './types';
|
||||
import {checkBindingNoChanges, isComponentView, resolveViewDefinition, viewParentEl} from './util';
|
||||
|
||||
const NOOP = (): any => undefined;
|
||||
@ -51,7 +51,7 @@ export function viewDef(
|
||||
node.index = i;
|
||||
node.parent = currentParent;
|
||||
node.bindingIndex = viewBindingCount;
|
||||
node.disposableIndex = viewDisposableCount;
|
||||
node.outputIndex = viewDisposableCount;
|
||||
node.reverseChildIndex =
|
||||
calculateReverseChildIndex(currentParent, i, node.childCount, nodes.length);
|
||||
|
||||
@ -90,7 +90,7 @@ export function viewDef(
|
||||
}
|
||||
|
||||
viewBindingCount += node.bindings.length;
|
||||
viewDisposableCount += node.disposableCount;
|
||||
viewDisposableCount += node.outputs.length;
|
||||
|
||||
if (!currentRenderParent && (node.type === NodeType.Element || node.type === NodeType.Text)) {
|
||||
lastRenderRootNode = node;
|
||||
@ -104,7 +104,7 @@ export function viewDef(
|
||||
currentParent.element.allProviders = currentParent.element.publicProviders;
|
||||
}
|
||||
const isPrivateService = (node.flags & NodeFlags.PrivateProvider) !== 0;
|
||||
const isComponent = (node.flags & NodeFlags.HasComponent) !== 0;
|
||||
const isComponent = (node.flags & NodeFlags.IsComponent) !== 0;
|
||||
if (!isPrivateService || isComponent) {
|
||||
currentParent.element.publicProviders[node.provider.tokenKey] = node;
|
||||
} else {
|
||||
@ -116,7 +116,7 @@ export function viewDef(
|
||||
currentParent.element.allProviders[node.provider.tokenKey] = node;
|
||||
}
|
||||
if (isComponent) {
|
||||
currentParent.element.component = node;
|
||||
currentParent.element.componentProvider = node;
|
||||
}
|
||||
}
|
||||
if (node.childCount) {
|
||||
@ -141,7 +141,7 @@ export function viewDef(
|
||||
updateRenderer: updateRenderer || NOOP,
|
||||
handleEvent: handleEvent || NOOP,
|
||||
bindingCount: viewBindingCount,
|
||||
disposableCount: viewDisposableCount, lastRenderRootNode
|
||||
outputCount: viewDisposableCount, lastRenderRootNode
|
||||
};
|
||||
}
|
||||
|
||||
@ -242,7 +242,7 @@ function createView(
|
||||
root: RootData, renderer: RendererV2, parent: ViewData, parentNodeDef: NodeDef,
|
||||
def: ViewDefinition): ViewData {
|
||||
const nodes: NodeData[] = new Array(def.nodes.length);
|
||||
const disposables = def.disposableCount ? new Array(def.disposableCount) : undefined;
|
||||
const disposables = def.outputCount ? new Array(def.outputCount) : undefined;
|
||||
const view: ViewData = {
|
||||
def,
|
||||
parent,
|
||||
@ -271,63 +271,66 @@ function createViewNodes(view: ViewData) {
|
||||
for (let i = 0; i < def.nodes.length; i++) {
|
||||
const nodeDef = def.nodes[i];
|
||||
Services.setCurrentNode(view, i);
|
||||
let nodeData: any;
|
||||
switch (nodeDef.type) {
|
||||
case NodeType.Element:
|
||||
nodes[i] = createElement(view, renderHost, nodeDef) as any;
|
||||
break;
|
||||
case NodeType.Text:
|
||||
nodes[i] = createText(view, renderHost, nodeDef) as any;
|
||||
break;
|
||||
case NodeType.Provider: {
|
||||
const instance = createProviderInstance(view, nodeDef);
|
||||
const providerData = <ProviderData>{componentView: undefined, instance};
|
||||
nodes[i] = providerData as any;
|
||||
break;
|
||||
}
|
||||
case NodeType.Pipe: {
|
||||
const instance = createPipeInstance(view, nodeDef);
|
||||
const providerData = <ProviderData>{componentView: undefined, instance};
|
||||
nodes[i] = providerData as any;
|
||||
break;
|
||||
}
|
||||
case NodeType.Directive: {
|
||||
const el = createElement(view, renderHost, nodeDef) as any;
|
||||
let componentView: ViewData;
|
||||
if (nodeDef.flags & NodeFlags.HasComponent) {
|
||||
// Components can inject a ChangeDetectorRef that needs a references to
|
||||
// the component view. Therefore, we create the component view first
|
||||
// and set the ProviderData in ViewData, and then instantiate the provider.
|
||||
const compViewDef = resolveViewDefinition(nodeDef.provider.component);
|
||||
const rendererType = nodeDef.provider.rendererType;
|
||||
const compViewDef = resolveViewDefinition(nodeDef.element.componentView);
|
||||
const rendererType = nodeDef.element.componentRendererType;
|
||||
let compRenderer: RendererV2;
|
||||
if (!rendererType) {
|
||||
compRenderer = view.root.renderer;
|
||||
} else {
|
||||
const hostEl = asElementData(view, nodeDef.parent.index).renderElement;
|
||||
compRenderer = view.root.rendererFactory.createRenderer(hostEl, rendererType);
|
||||
compRenderer = view.root.rendererFactory.createRenderer(el, rendererType);
|
||||
}
|
||||
const componentView = createView(view.root, compRenderer, view, nodeDef, compViewDef);
|
||||
const providerData = <ProviderData>{componentView, instance: undefined};
|
||||
nodes[i] = providerData as any;
|
||||
const instance = providerData.instance = createDirectiveInstance(view, nodeDef);
|
||||
componentView = createView(
|
||||
view.root, compRenderer, view, nodeDef.element.componentProvider, compViewDef);
|
||||
}
|
||||
listenToElementOutputs(view, componentView, nodeDef, el);
|
||||
nodeData = <ElementData>{
|
||||
renderElement: el,
|
||||
componentView,
|
||||
embeddedViews: (nodeDef.flags & NodeFlags.HasEmbeddedViews) ? [] : undefined,
|
||||
projectedViews: undefined
|
||||
};
|
||||
break;
|
||||
case NodeType.Text:
|
||||
nodeData = createText(view, renderHost, nodeDef) as any;
|
||||
break;
|
||||
case NodeType.Provider: {
|
||||
const instance = createProviderInstance(view, nodeDef);
|
||||
nodeData = <ProviderData>{instance};
|
||||
break;
|
||||
}
|
||||
case NodeType.Pipe: {
|
||||
const instance = createPipeInstance(view, nodeDef);
|
||||
nodeData = <ProviderData>{instance};
|
||||
break;
|
||||
}
|
||||
case NodeType.Directive: {
|
||||
const instance = createDirectiveInstance(view, nodeDef);
|
||||
nodeData = <ProviderData>{instance};
|
||||
if (nodeDef.flags & NodeFlags.IsComponent) {
|
||||
const compView = asElementData(view, nodeDef.parent.index).componentView;
|
||||
initView(componentView, instance, instance);
|
||||
} else {
|
||||
const instance = createDirectiveInstance(view, nodeDef);
|
||||
const providerData = <ProviderData>{componentView: undefined, instance};
|
||||
nodes[i] = providerData as any;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeType.PureExpression:
|
||||
nodes[i] = createPureExpression(view, nodeDef) as any;
|
||||
nodeData = createPureExpression(view, nodeDef) as any;
|
||||
break;
|
||||
case NodeType.Query:
|
||||
nodes[i] = createQuery() as any;
|
||||
nodeData = createQuery() as any;
|
||||
break;
|
||||
case NodeType.NgContent:
|
||||
appendNgContent(view, renderHost, nodeDef);
|
||||
// no runtime data needed for NgContent...
|
||||
nodes[i] = undefined;
|
||||
nodeData = undefined;
|
||||
break;
|
||||
}
|
||||
nodes[i] = nodeData;
|
||||
}
|
||||
// Create the ViewData.nodes of component views after we created everything else,
|
||||
// so that e.g. ng-content works
|
||||
@ -479,7 +482,7 @@ export function destroyView(view: ViewData) {
|
||||
if (view.renderer.destroyNode) {
|
||||
destroyViewNodes(view);
|
||||
}
|
||||
if (view.parentNodeDef && view.parentNodeDef.flags & NodeFlags.HasComponent) {
|
||||
if (isComponentView(view)) {
|
||||
view.renderer.destroy();
|
||||
}
|
||||
view.state |= ViewState.Destroyed;
|
||||
@ -513,8 +516,7 @@ function execComponentViewsAction(view: ViewData, action: ViewAction) {
|
||||
const nodeDef = def.nodes[i];
|
||||
if (nodeDef.flags & NodeFlags.HasComponent) {
|
||||
// a leaf
|
||||
const providerData = asProviderData(view, i);
|
||||
callViewAction(providerData.componentView, action);
|
||||
callViewAction(asElementData(view, i).componentView, action);
|
||||
} else if ((nodeDef.childFlags & NodeFlags.HasComponent) === 0) {
|
||||
// a parent with leafs
|
||||
// no child is a component,
|
||||
|
Reference in New Issue
Block a user