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:
parent
6b7937f112
commit
5049a50bf6
|
@ -282,7 +282,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
elName = null;
|
elName = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {flags, usedEvents, queryMatchesExpr, hostBindings, hostEvents} =
|
const {flags, usedEvents, queryMatchesExpr, hostBindings, hostEvents} =
|
||||||
this._visitElementOrTemplate(nodeIndex, ast);
|
this._visitElementOrTemplate(nodeIndex, ast);
|
||||||
|
|
||||||
let inputDefs: o.Expression[] = [];
|
let inputDefs: o.Expression[] = [];
|
||||||
|
@ -296,33 +296,43 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
// Note: inputDefs have to be in the same order as hostBindings:
|
// Note: inputDefs have to be in the same order as hostBindings:
|
||||||
// - first the entries from the directives, then the ones from the element.
|
// - first the entries from the directives, then the ones from the element.
|
||||||
ast.directives.forEach(
|
ast.directives.forEach(
|
||||||
(dirAst, dirIndex) => inputDefs.push(...elementBindingDefs(dirAst.hostProperties)));
|
(dirAst, dirIndex) =>
|
||||||
inputDefs.push(...elementBindingDefs(ast.inputs));
|
inputDefs.push(...elementBindingDefs(dirAst.hostProperties, dirAst)));
|
||||||
|
inputDefs.push(...elementBindingDefs(ast.inputs, null));
|
||||||
|
|
||||||
outputDefs = usedEvents.map(([target, eventName]) => {
|
outputDefs = usedEvents.map(
|
||||||
return target ? o.literalArr([o.literal(target), o.literal(eventName)]) :
|
([target, eventName]) => o.literalArr([o.literal(target), o.literal(eventName)]));
|
||||||
o.literal(eventName);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templateVisitAll(this, ast.children);
|
templateVisitAll(this, ast.children);
|
||||||
|
|
||||||
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
||||||
|
|
||||||
|
const compAst = ast.directives.find(dirAst => dirAst.directive.isComponent);
|
||||||
|
let compRendererType = o.NULL_EXPR;
|
||||||
|
let compView = o.NULL_EXPR;
|
||||||
|
if (compAst) {
|
||||||
|
compView = o.importExpr({reference: compAst.directive.componentViewType});
|
||||||
|
compRendererType = o.importExpr({reference: compAst.directive.rendererType});
|
||||||
|
}
|
||||||
|
|
||||||
// elementDef(
|
// elementDef(
|
||||||
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
|
// flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
||||||
// childCount: number, name: string, fixedAttrs: {[name: string]: string} = {},
|
// ngContentIndex: number, childCount: number, namespaceAndName: string,
|
||||||
|
// fixedAttrs: [string, string][] = [],
|
||||||
// bindings?:
|
// bindings?:
|
||||||
// ([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
|
// ([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
|
||||||
// [BindingType.ElementAttribute | BindingType.ElementProperty, string,
|
// [BindingType.ElementAttribute | BindingType.ElementProperty |
|
||||||
// SecurityContext])[],
|
// BindingType.DirectiveHostProperty, string, SecurityContext])[],
|
||||||
// outputs?: (string | [string, string])[], eventHandlerFn: ElementHandleEventFn): NodeDef;
|
// outputs?: ([OutputType.ElementOutput | OutputType.DirectiveHostOutput, string, string])[],
|
||||||
|
// handleEvent?: ElementHandleEventFn,
|
||||||
|
// componentView?: () => ViewDefinition, componentRendererType?: RendererTypeV2): NodeDef;
|
||||||
const nodeDef = () => o.importExpr(createIdentifier(Identifiers.elementDef)).callFn([
|
const nodeDef = () => o.importExpr(createIdentifier(Identifiers.elementDef)).callFn([
|
||||||
o.literal(flags), queryMatchesExpr, o.literal(ast.ngContentIndex), o.literal(childCount),
|
o.literal(flags), queryMatchesExpr, o.literal(ast.ngContentIndex), o.literal(childCount),
|
||||||
o.literal(elName), elName ? fixedAttrsDef(ast) : o.NULL_EXPR,
|
o.literal(elName), elName ? fixedAttrsDef(ast) : o.NULL_EXPR,
|
||||||
inputDefs.length ? o.literalArr(inputDefs) : o.NULL_EXPR,
|
inputDefs.length ? o.literalArr(inputDefs) : o.NULL_EXPR,
|
||||||
outputDefs.length ? o.literalArr(outputDefs) : o.NULL_EXPR,
|
outputDefs.length ? o.literalArr(outputDefs) : o.NULL_EXPR,
|
||||||
this._createElementHandleEventFn(nodeIndex, hostEvents)
|
this._createElementHandleEventFn(nodeIndex, hostEvents), compView, compRendererType
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.nodeDefs[nodeIndex] = nodeDef;
|
this.nodeDefs[nodeIndex] = nodeDef;
|
||||||
|
@ -336,11 +346,11 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
references: ReferenceAst[],
|
references: ReferenceAst[],
|
||||||
queryMatches: QueryMatch[]
|
queryMatches: QueryMatch[]
|
||||||
}): {
|
}): {
|
||||||
flags: number,
|
flags: NodeFlags,
|
||||||
usedEvents: [string, string][],
|
usedEvents: [string, string][],
|
||||||
queryMatchesExpr: o.Expression,
|
queryMatchesExpr: o.Expression,
|
||||||
hostBindings: {value: AST, context: o.Expression}[],
|
hostBindings: {value: AST, context: o.Expression}[],
|
||||||
hostEvents: {context: o.Expression, eventAst: BoundEventAst}[],
|
hostEvents: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[],
|
||||||
} {
|
} {
|
||||||
let flags = NodeFlags.None;
|
let flags = NodeFlags.None;
|
||||||
if (ast.hasViewContainer) {
|
if (ast.hasViewContainer) {
|
||||||
|
@ -348,17 +358,17 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
}
|
}
|
||||||
const usedEvents = new Map<string, [string, string]>();
|
const usedEvents = new Map<string, [string, string]>();
|
||||||
ast.outputs.forEach((event) => {
|
ast.outputs.forEach((event) => {
|
||||||
const en = eventName(event);
|
const {name, target} = elementEventNameAndTarget(event, null);
|
||||||
usedEvents.set(elementEventFullName(event.target, en), [event.target, en]);
|
usedEvents.set(elementEventFullName(target, name), [target, name]);
|
||||||
});
|
});
|
||||||
ast.directives.forEach((dirAst) => {
|
ast.directives.forEach((dirAst) => {
|
||||||
dirAst.hostEvents.forEach((event) => {
|
dirAst.hostEvents.forEach((event) => {
|
||||||
const en = eventName(event);
|
const {name, target} = elementEventNameAndTarget(event, dirAst);
|
||||||
usedEvents.set(elementEventFullName(event.target, en), [event.target, en]);
|
usedEvents.set(elementEventFullName(target, name), [target, name]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const hostBindings: {value: AST, context: o.Expression}[] = [];
|
const hostBindings: {value: AST, context: o.Expression}[] = [];
|
||||||
const hostEvents: {context: o.Expression, eventAst: BoundEventAst}[] = [];
|
const hostEvents: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[] = [];
|
||||||
const componentFactoryResolverProvider = createComponentFactoryResolver(ast.directives);
|
const componentFactoryResolverProvider = createComponentFactoryResolver(ast.directives);
|
||||||
if (componentFactoryResolverProvider) {
|
if (componentFactoryResolverProvider) {
|
||||||
this._visitProvider(componentFactoryResolverProvider, ast.queryMatches);
|
this._visitProvider(componentFactoryResolverProvider, ast.queryMatches);
|
||||||
|
@ -386,7 +396,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
|
|
||||||
let queryMatchExprs: o.Expression[] = [];
|
let queryMatchExprs: o.Expression[] = [];
|
||||||
ast.queryMatches.forEach((match) => {
|
ast.queryMatches.forEach((match) => {
|
||||||
let valueType: number;
|
let valueType: QueryValueType;
|
||||||
if (tokenReference(match.value) === resolveIdentifier(Identifiers.ElementRef)) {
|
if (tokenReference(match.value) === resolveIdentifier(Identifiers.ElementRef)) {
|
||||||
valueType = QueryValueType.ElementRef;
|
valueType = QueryValueType.ElementRef;
|
||||||
} else if (tokenReference(match.value) === resolveIdentifier(Identifiers.ViewContainerRef)) {
|
} else if (tokenReference(match.value) === resolveIdentifier(Identifiers.ViewContainerRef)) {
|
||||||
|
@ -399,7 +409,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ast.references.forEach((ref) => {
|
ast.references.forEach((ref) => {
|
||||||
let valueType: number;
|
let valueType: QueryValueType;
|
||||||
if (!ref.value) {
|
if (!ref.value) {
|
||||||
valueType = QueryValueType.RenderElement;
|
valueType = QueryValueType.RenderElement;
|
||||||
} else if (tokenReference(ref.value) === resolveIdentifier(Identifiers.TemplateRef)) {
|
} else if (tokenReference(ref.value) === resolveIdentifier(Identifiers.TemplateRef)) {
|
||||||
|
@ -410,8 +420,9 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
queryMatchExprs.push(o.literalArr([o.literal(ref.name), o.literal(valueType)]));
|
queryMatchExprs.push(o.literalArr([o.literal(ref.name), o.literal(valueType)]));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ast.outputs.forEach(
|
ast.outputs.forEach((outputAst) => {
|
||||||
(outputAst) => { hostEvents.push({context: COMP_VAR, eventAst: outputAst}); });
|
hostEvents.push({context: COMP_VAR, eventAst: outputAst, dirAst: null});
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
flags,
|
flags,
|
||||||
|
@ -423,19 +434,19 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visitDirective(
|
private _visitDirective(
|
||||||
providerAst: ProviderAst, directiveAst: DirectiveAst, directiveIndex: number,
|
providerAst: ProviderAst, dirAst: DirectiveAst, directiveIndex: number,
|
||||||
elementNodeIndex: number, refs: ReferenceAst[], queryMatches: QueryMatch[],
|
elementNodeIndex: number, refs: ReferenceAst[], queryMatches: QueryMatch[],
|
||||||
usedEvents: Map<string, any>, queryIds: StaticAndDynamicQueryIds): {
|
usedEvents: Map<string, any>, queryIds: StaticAndDynamicQueryIds): {
|
||||||
hostBindings: {value: AST, context: o.Expression}[],
|
hostBindings: {value: AST, context: o.Expression}[],
|
||||||
hostEvents: {context: o.Expression, eventAst: BoundEventAst}[]
|
hostEvents: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[]
|
||||||
} {
|
} {
|
||||||
const nodeIndex = this.nodeDefs.length;
|
const nodeIndex = this.nodeDefs.length;
|
||||||
// reserve the space in the nodeDefs array so we can add children
|
// reserve the space in the nodeDefs array so we can add children
|
||||||
this.nodeDefs.push(null);
|
this.nodeDefs.push(null);
|
||||||
|
|
||||||
directiveAst.directive.queries.forEach((query, queryIndex) => {
|
dirAst.directive.queries.forEach((query, queryIndex) => {
|
||||||
let flags = NodeFlags.HasContentQuery;
|
let flags = NodeFlags.HasContentQuery;
|
||||||
const queryId = directiveAst.contentQueryStartId + queryIndex;
|
const queryId = dirAst.contentQueryStartId + queryIndex;
|
||||||
if (queryIds.staticQueryIds.has(queryId)) {
|
if (queryIds.staticQueryIds.has(queryId)) {
|
||||||
flags |= NodeFlags.HasStaticQuery;
|
flags |= NodeFlags.HasStaticQuery;
|
||||||
} else {
|
} else {
|
||||||
|
@ -454,7 +465,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
// I.e. we only allow queries as children of directives nodes.
|
// I.e. we only allow queries as children of directives nodes.
|
||||||
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
const childCount = this.nodeDefs.length - nodeIndex - 1;
|
||||||
|
|
||||||
const {flags, queryMatchExprs, providerExpr, providerType, depsExpr} =
|
let {flags, queryMatchExprs, providerExpr, providerType, depsExpr} =
|
||||||
this._visitProviderOrDirective(providerAst, queryMatches);
|
this._visitProviderOrDirective(providerAst, queryMatches);
|
||||||
|
|
||||||
refs.forEach((ref) => {
|
refs.forEach((ref) => {
|
||||||
|
@ -465,21 +476,18 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let rendererType = o.NULL_EXPR;
|
if (dirAst.directive.isComponent) {
|
||||||
let compView = o.NULL_EXPR;
|
flags |= NodeFlags.IsComponent;
|
||||||
if (directiveAst.directive.isComponent) {
|
|
||||||
compView = o.importExpr({reference: directiveAst.directive.componentViewType});
|
|
||||||
rendererType = o.importExpr({reference: directiveAst.directive.rendererType});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputDefs = directiveAst.inputs.map((inputAst, inputIndex) => {
|
const inputDefs = dirAst.inputs.map((inputAst, inputIndex) => {
|
||||||
const mapValue = o.literalArr([o.literal(inputIndex), o.literal(inputAst.directiveName)]);
|
const mapValue = o.literalArr([o.literal(inputIndex), o.literal(inputAst.directiveName)]);
|
||||||
// Note: it's important to not quote the key so that we can capture renames by minifiers!
|
// Note: it's important to not quote the key so that we can capture renames by minifiers!
|
||||||
return new o.LiteralMapEntry(inputAst.directiveName, mapValue, false);
|
return new o.LiteralMapEntry(inputAst.directiveName, mapValue, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
const outputDefs: o.LiteralMapEntry[] = [];
|
const outputDefs: o.LiteralMapEntry[] = [];
|
||||||
const dirMeta = directiveAst.directive;
|
const dirMeta = dirAst.directive;
|
||||||
Object.keys(dirMeta.outputs).forEach((propName) => {
|
Object.keys(dirMeta.outputs).forEach((propName) => {
|
||||||
const eventName = dirMeta.outputs[propName];
|
const eventName = dirMeta.outputs[propName];
|
||||||
if (usedEvents.has(eventName)) {
|
if (usedEvents.has(eventName)) {
|
||||||
|
@ -487,24 +495,24 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
outputDefs.push(new o.LiteralMapEntry(propName, o.literal(eventName), false));
|
outputDefs.push(new o.LiteralMapEntry(propName, o.literal(eventName), false));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (directiveAst.inputs.length || (flags & (NodeFlags.DoCheck | NodeFlags.OnInit)) > 0) {
|
if (dirAst.inputs.length || (flags & (NodeFlags.DoCheck | NodeFlags.OnInit)) > 0) {
|
||||||
this._addUpdateExpressions(
|
this._addUpdateExpressions(
|
||||||
nodeIndex,
|
nodeIndex,
|
||||||
directiveAst.inputs.map((input) => { return {context: COMP_VAR, value: input.value}; }),
|
dirAst.inputs.map((input) => { return {context: COMP_VAR, value: input.value}; }),
|
||||||
this.updateDirectivesExpressions);
|
this.updateDirectivesExpressions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dirContextExpr = o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
const dirContextExpr = o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
|
||||||
VIEW_VAR, o.literal(nodeIndex)
|
VIEW_VAR, o.literal(nodeIndex)
|
||||||
]);
|
]);
|
||||||
const hostBindings = directiveAst.hostProperties.map((hostBindingAst) => {
|
const hostBindings = dirAst.hostProperties.map((hostBindingAst) => {
|
||||||
return {
|
return {
|
||||||
value: (<ASTWithSource>hostBindingAst.value).ast,
|
value: (<ASTWithSource>hostBindingAst.value).ast,
|
||||||
context: dirContextExpr,
|
context: dirContextExpr,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
const hostEvents = directiveAst.hostEvents.map(
|
const hostEvents = dirAst.hostEvents.map(
|
||||||
(hostEventAst) => { return {context: dirContextExpr, eventAst: hostEventAst}; });
|
(hostEventAst) => { return {context: dirContextExpr, eventAst: hostEventAst, dirAst}; });
|
||||||
|
|
||||||
|
|
||||||
// directiveDef(
|
// directiveDef(
|
||||||
|
@ -516,7 +524,7 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
|
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
|
||||||
o.literal(childCount), providerExpr, depsExpr,
|
o.literal(childCount), providerExpr, depsExpr,
|
||||||
inputDefs.length ? new o.LiteralMapExpr(inputDefs) : o.NULL_EXPR,
|
inputDefs.length ? new o.LiteralMapExpr(inputDefs) : o.NULL_EXPR,
|
||||||
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR, compView, rendererType
|
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR
|
||||||
]);
|
]);
|
||||||
this.nodeDefs[nodeIndex] = nodeDef;
|
this.nodeDefs[nodeIndex] = nodeDef;
|
||||||
|
|
||||||
|
@ -543,10 +551,10 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
private _visitProviderOrDirective(providerAst: ProviderAst, queryMatches: QueryMatch[]): {
|
private _visitProviderOrDirective(providerAst: ProviderAst, queryMatches: QueryMatch[]): {
|
||||||
flags: number,
|
flags: NodeFlags,
|
||||||
queryMatchExprs: o.Expression[],
|
queryMatchExprs: o.Expression[],
|
||||||
providerExpr: o.Expression,
|
providerExpr: o.Expression,
|
||||||
providerType: number,
|
providerType: ProviderType,
|
||||||
depsExpr: o.Expression
|
depsExpr: o.Expression
|
||||||
} {
|
} {
|
||||||
let flags = NodeFlags.None;
|
let flags = NodeFlags.None;
|
||||||
|
@ -702,10 +710,11 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createElementHandleEventFn(
|
private _createElementHandleEventFn(
|
||||||
nodeIndex: number, handlers: {context: o.Expression, eventAst: BoundEventAst}[]) {
|
nodeIndex: number,
|
||||||
|
handlers: {context: o.Expression, eventAst: BoundEventAst, dirAst: DirectiveAst}[]) {
|
||||||
const handleEventStmts: o.Statement[] = [];
|
const handleEventStmts: o.Statement[] = [];
|
||||||
let handleEventBindingCount = 0;
|
let handleEventBindingCount = 0;
|
||||||
handlers.forEach(({context, eventAst}) => {
|
handlers.forEach(({context, eventAst, dirAst}) => {
|
||||||
const bindingId = `${handleEventBindingCount++}`;
|
const bindingId = `${handleEventBindingCount++}`;
|
||||||
const nameResolver = context === COMP_VAR ? this : null;
|
const nameResolver = context === COMP_VAR ? this : null;
|
||||||
const expression =
|
const expression =
|
||||||
|
@ -716,7 +725,8 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
if (allowDefault) {
|
if (allowDefault) {
|
||||||
trueStmts.push(ALLOW_DEFAULT_VAR.set(allowDefault.and(ALLOW_DEFAULT_VAR)).toStmt());
|
trueStmts.push(ALLOW_DEFAULT_VAR.set(allowDefault.and(ALLOW_DEFAULT_VAR)).toStmt());
|
||||||
}
|
}
|
||||||
const fullEventName = elementEventFullName(eventAst.target, eventName(eventAst));
|
const {target: eventTarget, name: eventName} = elementEventNameAndTarget(eventAst, dirAst);
|
||||||
|
const fullEventName = elementEventFullName(eventTarget, eventName);
|
||||||
handleEventStmts.push(
|
handleEventStmts.push(
|
||||||
new o.IfStmt(o.literal(fullEventName).identical(EVENT_NAME_VAR), trueStmts));
|
new o.IfStmt(o.literal(fullEventName).identical(EVENT_NAME_VAR), trueStmts));
|
||||||
});
|
});
|
||||||
|
@ -751,13 +761,13 @@ class ViewBuilder implements TemplateAstVisitor, LocalResolver, BuiltinConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
function providerDef(providerAst: ProviderAst):
|
function providerDef(providerAst: ProviderAst):
|
||||||
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
|
{providerExpr: o.Expression, providerType: ProviderType, depsExpr: o.Expression} {
|
||||||
return providerAst.multiProvider ? multiProviderDef(providerAst.providers) :
|
return providerAst.multiProvider ? multiProviderDef(providerAst.providers) :
|
||||||
singleProviderDef(providerAst.providers[0]);
|
singleProviderDef(providerAst.providers[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function multiProviderDef(providers: CompileProviderMetadata[]):
|
function multiProviderDef(providers: CompileProviderMetadata[]):
|
||||||
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
|
{providerExpr: o.Expression, providerType: ProviderType, depsExpr: o.Expression} {
|
||||||
const allDepDefs: o.Expression[] = [];
|
const allDepDefs: o.Expression[] = [];
|
||||||
const allParams: o.FnParam[] = [];
|
const allParams: o.FnParam[] = [];
|
||||||
const exprs = providers.map((provider, providerIndex) => {
|
const exprs = providers.map((provider, providerIndex) => {
|
||||||
|
@ -791,9 +801,9 @@ function multiProviderDef(providers: CompileProviderMetadata[]):
|
||||||
}
|
}
|
||||||
|
|
||||||
function singleProviderDef(providerMeta: CompileProviderMetadata):
|
function singleProviderDef(providerMeta: CompileProviderMetadata):
|
||||||
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
|
{providerExpr: o.Expression, providerType: ProviderType, depsExpr: o.Expression} {
|
||||||
let providerExpr: o.Expression;
|
let providerExpr: o.Expression;
|
||||||
let providerType: number;
|
let providerType: ProviderType;
|
||||||
let deps: CompileDiDependencyMetadata[];
|
let deps: CompileDiDependencyMetadata[];
|
||||||
if (providerMeta.useClass) {
|
if (providerMeta.useClass) {
|
||||||
providerExpr = o.importExpr(providerMeta.useClass);
|
providerExpr = o.importExpr(providerMeta.useClass);
|
||||||
|
@ -852,7 +862,7 @@ function needsAdditionalRootNode(ast: TemplateAst): boolean {
|
||||||
return ast instanceof NgContentAst;
|
return ast instanceof NgContentAst;
|
||||||
}
|
}
|
||||||
|
|
||||||
function lifecycleHookToNodeFlag(lifecycleHook: LifecycleHooks): number {
|
function lifecycleHookToNodeFlag(lifecycleHook: LifecycleHooks): NodeFlags {
|
||||||
let nodeFlag = NodeFlags.None;
|
let nodeFlag = NodeFlags.None;
|
||||||
switch (lifecycleHook) {
|
switch (lifecycleHook) {
|
||||||
case LifecycleHooks.AfterContentChecked:
|
case LifecycleHooks.AfterContentChecked:
|
||||||
|
@ -883,7 +893,8 @@ function lifecycleHookToNodeFlag(lifecycleHook: LifecycleHooks): number {
|
||||||
return nodeFlag;
|
return nodeFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
function elementBindingDefs(inputAsts: BoundElementPropertyAst[]): o.Expression[] {
|
function elementBindingDefs(
|
||||||
|
inputAsts: BoundElementPropertyAst[], dirAst: DirectiveAst): o.Expression[] {
|
||||||
return inputAsts.map((inputAst) => {
|
return inputAsts.map((inputAst) => {
|
||||||
switch (inputAst.type) {
|
switch (inputAst.type) {
|
||||||
case PropertyBindingType.Attribute:
|
case PropertyBindingType.Attribute:
|
||||||
|
@ -897,8 +908,11 @@ function elementBindingDefs(inputAsts: BoundElementPropertyAst[]): o.Expression[
|
||||||
o.literal(inputAst.securityContext)
|
o.literal(inputAst.securityContext)
|
||||||
]);
|
]);
|
||||||
case PropertyBindingType.Animation:
|
case PropertyBindingType.Animation:
|
||||||
|
const bindingType = dirAst && dirAst.directive.isComponent ?
|
||||||
|
BindingType.ComponentHostProperty :
|
||||||
|
BindingType.ElementProperty;
|
||||||
return o.literalArr([
|
return o.literalArr([
|
||||||
o.literal(BindingType.ElementProperty), o.literal('@' + inputAst.name),
|
o.literal(bindingType), o.literal('@' + inputAst.name),
|
||||||
o.literal(inputAst.securityContext)
|
o.literal(inputAst.securityContext)
|
||||||
]);
|
]);
|
||||||
case PropertyBindingType.Class:
|
case PropertyBindingType.Class:
|
||||||
|
@ -1025,6 +1039,14 @@ function createComponentFactoryResolver(directives: DirectiveAst[]): ProviderAst
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function eventName(eventAst: BoundEventAst): string {
|
function elementEventNameAndTarget(
|
||||||
return eventAst.isAnimation ? `@${eventAst.name}.${eventAst.phase}` : eventAst.name;
|
eventAst: BoundEventAst, dirAst: DirectiveAst): {name: string, target: string} {
|
||||||
|
if (eventAst.isAnimation) {
|
||||||
|
return {
|
||||||
|
name: `@${eventAst.name}.${eventAst.phase}`,
|
||||||
|
target: dirAst && dirAst.directive.isComponent ? 'component' : null
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return eventAst;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {isDevMode} from '../application_ref';
|
import {isDevMode} from '../application_ref';
|
||||||
|
import {RendererTypeV2, RendererV2} from '../render/api';
|
||||||
import {SecurityContext} from '../security';
|
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';
|
import {checkAndUpdateBinding, dispatchEvent, elementEventFullName, filterQueryId, getParentRenderElement, resolveViewDefinition, sliceErrorStack, splitMatchedQueriesDsl, splitNamespace} from './util';
|
||||||
|
|
||||||
const NOOP: any = () => {};
|
const NOOP: any = () => {};
|
||||||
|
@ -34,20 +35,20 @@ export function anchorDef(
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
renderParent: undefined,
|
renderParent: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
disposableIndex: undefined,
|
outputIndex: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags,
|
flags,
|
||||||
childFlags: 0,
|
childFlags: 0,
|
||||||
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount,
|
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount,
|
||||||
bindings: [],
|
bindings: [],
|
||||||
disposableCount: 0,
|
outputs: [],
|
||||||
element: {
|
element: {
|
||||||
ns: undefined,
|
ns: undefined,
|
||||||
name: undefined,
|
name: undefined,
|
||||||
attrs: undefined,
|
attrs: undefined, template, source,
|
||||||
outputs: [], template, source,
|
componentProvider: undefined,
|
||||||
// will bet set by the view definition
|
componentView: undefined,
|
||||||
component: undefined,
|
componentRendererType: undefined,
|
||||||
publicProviders: undefined,
|
publicProviders: undefined,
|
||||||
allProviders: undefined, handleEvent
|
allProviders: undefined, handleEvent
|
||||||
},
|
},
|
||||||
|
@ -65,8 +66,13 @@ export function elementDef(
|
||||||
fixedAttrs: [string, string][] = [],
|
fixedAttrs: [string, string][] = [],
|
||||||
bindings?:
|
bindings?:
|
||||||
([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
|
([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) {
|
if (!handleEvent) {
|
||||||
handleEvent = NOOP;
|
handleEvent = NOOP;
|
||||||
}
|
}
|
||||||
|
@ -93,29 +99,35 @@ export function elementDef(
|
||||||
break;
|
break;
|
||||||
case BindingType.ElementAttribute:
|
case BindingType.ElementAttribute:
|
||||||
case BindingType.ElementProperty:
|
case BindingType.ElementProperty:
|
||||||
|
case BindingType.ComponentHostProperty:
|
||||||
securityContext = <SecurityContext>entry[2];
|
securityContext = <SecurityContext>entry[2];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
bindingDefs[i] = {type: bindingType, ns, name, nonMinifiedName: name, securityContext, suffix};
|
bindingDefs[i] = {type: bindingType, ns, name, nonMinifiedName: name, securityContext, suffix};
|
||||||
}
|
}
|
||||||
outputs = outputs || [];
|
outputs = outputs || [];
|
||||||
const outputDefs: ElementOutputDef[] = new Array(outputs.length);
|
const outputDefs: OutputDef[] = new Array(outputs.length);
|
||||||
for (let i = 0; i < outputs.length; i++) {
|
for (let i = 0; i < outputs.length; i++) {
|
||||||
const output = outputs[i];
|
const [target, eventName] = outputs[i];
|
||||||
let target: string;
|
outputDefs[i] = {
|
||||||
let eventName: string;
|
type: OutputType.ElementOutput,
|
||||||
if (Array.isArray(output)) {
|
target: <any>target, eventName,
|
||||||
[target, eventName] = output;
|
propName: undefined
|
||||||
} else {
|
};
|
||||||
eventName = output;
|
|
||||||
}
|
|
||||||
outputDefs[i] = {eventName: eventName, target: target};
|
|
||||||
}
|
}
|
||||||
fixedAttrs = fixedAttrs || [];
|
fixedAttrs = fixedAttrs || [];
|
||||||
const attrs = <[string, string, string][]>fixedAttrs.map(([namespaceAndName, value]) => {
|
const attrs = <[string, string, string][]>fixedAttrs.map(([namespaceAndName, value]) => {
|
||||||
const [ns, name] = splitNamespace(namespaceAndName);
|
const [ns, name] = splitNamespace(namespaceAndName);
|
||||||
return [ns, name, value];
|
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 {
|
return {
|
||||||
type: NodeType.Element,
|
type: NodeType.Element,
|
||||||
// will bet set by the view definition
|
// will bet set by the view definition
|
||||||
|
@ -124,21 +136,21 @@ export function elementDef(
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
renderParent: undefined,
|
renderParent: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
disposableIndex: undefined,
|
outputIndex: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags,
|
flags,
|
||||||
childFlags: 0,
|
childFlags: 0,
|
||||||
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount,
|
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references, ngContentIndex, childCount,
|
||||||
bindings: bindingDefs,
|
bindings: bindingDefs,
|
||||||
disposableCount: outputDefs.length,
|
outputs: outputDefs,
|
||||||
element: {
|
element: {
|
||||||
ns,
|
ns,
|
||||||
name,
|
name,
|
||||||
attrs,
|
attrs,
|
||||||
outputs: outputDefs, source,
|
source,
|
||||||
template: undefined,
|
template: undefined,
|
||||||
// will bet set by the view definition
|
// will bet set by the view definition
|
||||||
component: undefined,
|
componentProvider: undefined, componentView, componentRendererType,
|
||||||
publicProviders: undefined,
|
publicProviders: undefined,
|
||||||
allProviders: undefined, handleEvent,
|
allProviders: undefined, handleEvent,
|
||||||
},
|
},
|
||||||
|
@ -174,21 +186,24 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El
|
||||||
renderer.setAttribute(el, name, value, ns);
|
renderer.setAttribute(el, name, value, ns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (elDef.outputs.length) {
|
return el;
|
||||||
for (let i = 0; i < elDef.outputs.length; i++) {
|
}
|
||||||
const output = elDef.outputs[i];
|
|
||||||
const handleEventClosure = renderEventHandlerClosure(
|
export function listenToElementOutputs(view: ViewData, compView: ViewData, def: NodeDef, el: any) {
|
||||||
view, def.index, elementEventFullName(output.target, output.eventName));
|
for (let i = 0; i < def.outputs.length; i++) {
|
||||||
const disposable =
|
const output = def.outputs[i];
|
||||||
<any>renderer.listen(output.target || el, output.eventName, handleEventClosure);
|
const handleEventClosure = renderEventHandlerClosure(
|
||||||
view.disposables[def.disposableIndex + i] = disposable;
|
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) {
|
function renderEventHandlerClosure(view: ViewData, index: number, eventName: string) {
|
||||||
|
@ -223,7 +238,8 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const binding = def.bindings[bindingIdx];
|
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;
|
const name = binding.name;
|
||||||
switch (binding.type) {
|
switch (binding.type) {
|
||||||
case BindingType.ElementAttribute:
|
case BindingType.ElementAttribute:
|
||||||
|
@ -238,6 +254,9 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
|
||||||
case BindingType.ElementProperty:
|
case BindingType.ElementProperty:
|
||||||
setElementProperty(view, binding, renderNode, name, value);
|
setElementProperty(view, binding, renderNode, name, value);
|
||||||
break;
|
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,
|
parent: undefined,
|
||||||
renderParent: undefined,
|
renderParent: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
disposableIndex: undefined,
|
outputIndex: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags: 0,
|
flags: 0,
|
||||||
childFlags: 0,
|
childFlags: 0,
|
||||||
|
@ -28,7 +28,7 @@ export function ngContentDef(ngContentIndex: number, index: number): NodeDef {
|
||||||
references: {}, ngContentIndex,
|
references: {}, ngContentIndex,
|
||||||
childCount: 0,
|
childCount: 0,
|
||||||
bindings: [],
|
bindings: [],
|
||||||
disposableCount: 0,
|
outputs: [],
|
||||||
element: undefined,
|
element: undefined,
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
text: undefined,
|
text: undefined,
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {ViewEncapsulation} from '../metadata/view';
|
||||||
import {Renderer as RendererV1, RendererFactoryV2, RendererTypeV2, RendererV2} from '../render/api';
|
import {Renderer as RendererV1, RendererFactoryV2, RendererTypeV2, RendererV2} from '../render/api';
|
||||||
|
|
||||||
import {createChangeDetectorRef, createInjector, createRendererV1, createTemplateRef, createViewContainerRef} from './refs';
|
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';
|
import {checkAndUpdateBinding, dispatchEvent, filterQueryId, isComponentView, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
|
||||||
|
|
||||||
const RendererV1TokenKey = tokenKey(RendererV1);
|
const RendererV1TokenKey = tokenKey(RendererV1);
|
||||||
|
@ -31,8 +31,7 @@ const NOT_CREATED = new Object();
|
||||||
export function directiveDef(
|
export function directiveDef(
|
||||||
flags: NodeFlags, matchedQueries: [string | number, QueryValueType][], childCount: number,
|
flags: NodeFlags, matchedQueries: [string | number, QueryValueType][], childCount: number,
|
||||||
ctor: any, deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
ctor: any, deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
|
||||||
outputs?: {[name: string]: string}, component?: () => ViewDefinition,
|
outputs?: {[name: string]: string}): NodeDef {
|
||||||
rendererType?: RendererTypeV2): NodeDef {
|
|
||||||
const bindings: BindingDef[] = [];
|
const bindings: BindingDef[] = [];
|
||||||
if (props) {
|
if (props) {
|
||||||
for (let prop in props) {
|
for (let prop in props) {
|
||||||
|
@ -46,15 +45,16 @@ export function directiveDef(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const outputDefs: DirectiveOutputDef[] = [];
|
const outputDefs: OutputDef[] = [];
|
||||||
if (outputs) {
|
if (outputs) {
|
||||||
for (let propName in 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(
|
return _def(
|
||||||
NodeType.Directive, flags, matchedQueries, childCount, ProviderType.Class, ctor, ctor, deps,
|
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 {
|
export function pipeDef(flags: NodeFlags, ctor: any, deps: ([DepFlags, any] | any)[]): NodeDef {
|
||||||
|
@ -70,14 +70,8 @@ export function providerDef(
|
||||||
export function _def(
|
export function _def(
|
||||||
type: NodeType, flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
type: NodeType, flags: NodeFlags, matchedQueriesDsl: [string | number, QueryValueType][],
|
||||||
childCount: number, providerType: ProviderType, token: any, value: any,
|
childCount: number, providerType: ProviderType, token: any, value: any,
|
||||||
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: DirectiveOutputDef[],
|
deps: ([DepFlags, any] | any)[], bindings?: BindingDef[], outputs?: OutputDef[]): NodeDef {
|
||||||
component?: () => ViewDefinition, rendererType?: RendererTypeV2): NodeDef {
|
|
||||||
const {matchedQueries, references, matchedQueryIds} = splitMatchedQueriesDsl(matchedQueriesDsl);
|
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) {
|
if (!outputs) {
|
||||||
outputs = [];
|
outputs = [];
|
||||||
}
|
}
|
||||||
|
@ -96,9 +90,6 @@ export function _def(
|
||||||
}
|
}
|
||||||
return {flags, token, tokenKey: tokenKey(token)};
|
return {flags, token, tokenKey: tokenKey(token)};
|
||||||
});
|
});
|
||||||
if (component) {
|
|
||||||
flags = flags | NodeFlags.HasComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
|
@ -108,20 +99,14 @@ export function _def(
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
renderParent: undefined,
|
renderParent: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
disposableIndex: undefined,
|
outputIndex: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags,
|
flags,
|
||||||
childFlags: 0,
|
childFlags: 0,
|
||||||
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references,
|
childMatchedQueries: 0, matchedQueries, matchedQueryIds, references,
|
||||||
ngContentIndex: undefined, childCount, bindings,
|
ngContentIndex: undefined, childCount, bindings, outputs,
|
||||||
disposableCount: outputs.length,
|
|
||||||
element: undefined,
|
element: undefined,
|
||||||
provider: {
|
provider: {type: providerType, token, tokenKey: tokenKey(token), value, deps: depDefs},
|
||||||
type: providerType,
|
|
||||||
token,
|
|
||||||
tokenKey: tokenKey(token), value,
|
|
||||||
deps: depDefs, outputs, component, rendererType
|
|
||||||
},
|
|
||||||
text: undefined,
|
text: undefined,
|
||||||
pureExpression: undefined,
|
pureExpression: undefined,
|
||||||
query: undefined,
|
query: undefined,
|
||||||
|
@ -149,17 +134,17 @@ export function createPipeInstance(view: ViewData, def: NodeDef): any {
|
||||||
|
|
||||||
export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
|
export function createDirectiveInstance(view: ViewData, def: NodeDef): any {
|
||||||
// components can see other private services, other directives can't.
|
// 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;
|
const providerDef = def.provider;
|
||||||
// directives are always eager and classes!
|
// directives are always eager and classes!
|
||||||
const instance =
|
const instance =
|
||||||
createClass(view, def.parent, allowPrivateServices, def.provider.value, def.provider.deps);
|
createClass(view, def.parent, allowPrivateServices, def.provider.value, def.provider.deps);
|
||||||
if (providerDef.outputs.length) {
|
if (def.outputs.length) {
|
||||||
for (let i = 0; i < providerDef.outputs.length; i++) {
|
for (let i = 0; i < def.outputs.length; i++) {
|
||||||
const output = providerDef.outputs[i];
|
const output = def.outputs[i];
|
||||||
const subscription = instance[output.propName].subscribe(
|
const subscription = instance[output.propName].subscribe(
|
||||||
eventHandlerClosure(view, def.parent.index, output.eventName));
|
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;
|
return instance;
|
||||||
|
@ -371,7 +356,7 @@ export function resolveDep(
|
||||||
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
|
function findCompView(view: ViewData, elDef: NodeDef, allowPrivateServices: boolean) {
|
||||||
let compView: ViewData;
|
let compView: ViewData;
|
||||||
if (allowPrivateServices) {
|
if (allowPrivateServices) {
|
||||||
compView = asProviderData(view, elDef.element.component.index).componentView;
|
compView = asElementData(view, elDef.index).componentView;
|
||||||
} else {
|
} else {
|
||||||
compView = view;
|
compView = view;
|
||||||
while (compView.parent && !isComponentView(compView)) {
|
while (compView.parent && !isComponentView(compView)) {
|
||||||
|
@ -396,8 +381,8 @@ function checkAndUpdateProp(
|
||||||
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
changed = checkAndUpdateBinding(view, def, bindingIdx, value);
|
||||||
}
|
}
|
||||||
if (changed) {
|
if (changed) {
|
||||||
if (def.flags & NodeFlags.HasComponent) {
|
if (def.flags & NodeFlags.IsComponent) {
|
||||||
const compView = providerData.componentView;
|
const compView = asElementData(view, def.parent.index).componentView;
|
||||||
if (compView.def.flags & ViewFlags.OnPush) {
|
if (compView.def.flags & ViewFlags.OnPush) {
|
||||||
compView.state |= ViewState.ChecksEnabled;
|
compView.state |= ViewState.ChecksEnabled;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ function _pureExpressionDef(type: PureExpressionType, propertyNames: string[]):
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
renderParent: undefined,
|
renderParent: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
disposableIndex: undefined,
|
outputIndex: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags: 0,
|
flags: 0,
|
||||||
childFlags: 0,
|
childFlags: 0,
|
||||||
|
@ -53,7 +53,7 @@ function _pureExpressionDef(type: PureExpressionType, propertyNames: string[]):
|
||||||
references: {},
|
references: {},
|
||||||
ngContentIndex: undefined,
|
ngContentIndex: undefined,
|
||||||
childCount: 0, bindings,
|
childCount: 0, bindings,
|
||||||
disposableCount: 0,
|
outputs: [],
|
||||||
element: undefined,
|
element: undefined,
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
text: undefined,
|
text: undefined,
|
||||||
|
|
|
@ -31,7 +31,7 @@ export function queryDef(
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
renderParent: undefined,
|
renderParent: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
disposableIndex: undefined,
|
outputIndex: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags,
|
flags,
|
||||||
childFlags: 0,
|
childFlags: 0,
|
||||||
|
@ -42,7 +42,7 @@ export function queryDef(
|
||||||
references: {},
|
references: {},
|
||||||
childCount: 0,
|
childCount: 0,
|
||||||
bindings: [],
|
bindings: [],
|
||||||
disposableCount: 0,
|
outputs: [],
|
||||||
element: undefined,
|
element: undefined,
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
text: undefined,
|
text: undefined,
|
||||||
|
|
|
@ -42,7 +42,7 @@ class ComponentFactory_ extends ComponentFactory<any> {
|
||||||
injector: Injector, projectableNodes: any[][] = null,
|
injector: Injector, projectableNodes: any[][] = null,
|
||||||
rootSelectorOrNode: string|any = null): ComponentRef<any> {
|
rootSelectorOrNode: string|any = null): ComponentRef<any> {
|
||||||
const viewDef = resolveViewDefinition(this._viewClass);
|
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(
|
const view = Services.createRootView(
|
||||||
injector, projectableNodes || [], rootSelectorOrNode, viewDef, EMPTY_CONTEXT);
|
injector, projectableNodes || [], rootSelectorOrNode, viewDef, EMPTY_CONTEXT);
|
||||||
const component = asProviderData(view, componentNodeIndex).instance;
|
const component = asProviderData(view, componentNodeIndex).instance;
|
||||||
|
@ -255,7 +255,7 @@ export function createInjector(view: ViewData, elDef: NodeDef): Injector {
|
||||||
class Injector_ implements Injector {
|
class Injector_ implements Injector {
|
||||||
constructor(private view: ViewData, private elDef: NodeDef) {}
|
constructor(private view: ViewData, private elDef: NodeDef) {}
|
||||||
get(token: any, notFoundValue: any = Injector.THROW_IF_NOT_FOUND): any {
|
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(
|
return Services.resolveDep(
|
||||||
this.view, this.elDef, allowPrivateServices,
|
this.view, this.elDef, allowPrivateServices,
|
||||||
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);
|
{flags: DepFlags.None, token, tokenKey: tokenKey(token)}, notFoundValue);
|
||||||
|
|
|
@ -205,6 +205,7 @@ function debugCheckFn(
|
||||||
const binding = nodeDef.bindings[i];
|
const binding = nodeDef.bindings[i];
|
||||||
const value = values[i];
|
const value = values[i];
|
||||||
if ((binding.type === BindingType.ElementProperty ||
|
if ((binding.type === BindingType.ElementProperty ||
|
||||||
|
binding.type === BindingType.ComponentHostProperty ||
|
||||||
binding.type === BindingType.DirectiveProperty) &&
|
binding.type === BindingType.DirectiveProperty) &&
|
||||||
checkBinding(view, nodeDef, i, value)) {
|
checkBinding(view, nodeDef, i, value)) {
|
||||||
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] =
|
bindingValues[normalizeDebugBindingName(binding.nonMinifiedName)] =
|
||||||
|
@ -273,7 +274,6 @@ class DebugContext_ implements DebugContext {
|
||||||
private nodeDef: NodeDef;
|
private nodeDef: NodeDef;
|
||||||
private elView: ViewData;
|
private elView: ViewData;
|
||||||
private elDef: NodeDef;
|
private elDef: NodeDef;
|
||||||
private compProviderDef: NodeDef;
|
|
||||||
constructor(public view: ViewData, public nodeIndex: number) {
|
constructor(public view: ViewData, public nodeIndex: number) {
|
||||||
if (nodeIndex == null) {
|
if (nodeIndex == null) {
|
||||||
this.nodeIndex = nodeIndex = 0;
|
this.nodeIndex = nodeIndex = 0;
|
||||||
|
@ -292,21 +292,14 @@ class DebugContext_ implements DebugContext {
|
||||||
}
|
}
|
||||||
this.elDef = elDef;
|
this.elDef = elDef;
|
||||||
this.elView = elView;
|
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 injector(): Injector { return createInjector(this.elView, this.elDef); }
|
||||||
get component(): any {
|
get component(): any { return this.elOrCompView.component; }
|
||||||
if (this.compProviderDef) {
|
get context(): any { return this.elOrCompView.context; }
|
||||||
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 providerTokens(): any[] {
|
get providerTokens(): any[] {
|
||||||
const tokens: any[] = [];
|
const tokens: any[] = [];
|
||||||
if (this.elDef) {
|
if (this.elDef) {
|
||||||
|
@ -343,10 +336,7 @@ class DebugContext_ implements DebugContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
get componentRenderElement() {
|
get componentRenderElement() {
|
||||||
const view = this.compProviderDef ?
|
const elData = findHostElement(this.elOrCompView);
|
||||||
asProviderData(this.elView, this.compProviderDef.index).componentView :
|
|
||||||
this.view;
|
|
||||||
const elData = findHostElement(view);
|
|
||||||
return elData ? elData.renderElement : undefined;
|
return elData ? elData.renderElement : undefined;
|
||||||
}
|
}
|
||||||
get renderNode(): any {
|
get renderNode(): any {
|
||||||
|
|
|
@ -34,7 +34,7 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||||
parent: undefined,
|
parent: undefined,
|
||||||
renderParent: undefined,
|
renderParent: undefined,
|
||||||
bindingIndex: undefined,
|
bindingIndex: undefined,
|
||||||
disposableIndex: undefined,
|
outputIndex: undefined,
|
||||||
// regular values
|
// regular values
|
||||||
flags: 0,
|
flags: 0,
|
||||||
childFlags: 0,
|
childFlags: 0,
|
||||||
|
@ -43,7 +43,7 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
|
||||||
matchedQueryIds: 0,
|
matchedQueryIds: 0,
|
||||||
references: {}, ngContentIndex,
|
references: {}, ngContentIndex,
|
||||||
childCount: 0, bindings,
|
childCount: 0, bindings,
|
||||||
disposableCount: 0,
|
outputs: [],
|
||||||
element: undefined,
|
element: undefined,
|
||||||
provider: undefined,
|
provider: undefined,
|
||||||
text: {prefix: constants[0], source},
|
text: {prefix: constants[0], source},
|
||||||
|
|
|
@ -40,7 +40,7 @@ export interface ViewDefinition {
|
||||||
reverseChildNodes: NodeDef[];
|
reverseChildNodes: NodeDef[];
|
||||||
lastRenderRootNode: NodeDef;
|
lastRenderRootNode: NodeDef;
|
||||||
bindingCount: number;
|
bindingCount: number;
|
||||||
disposableCount: number;
|
outputCount: number;
|
||||||
/**
|
/**
|
||||||
* Binary or of all query ids that are matched by one of the nodes.
|
* Binary or of all query ids that are matched by one of the nodes.
|
||||||
* This includes query ids from templates as well.
|
* This includes query ids from templates as well.
|
||||||
|
@ -99,8 +99,8 @@ export interface NodeDef {
|
||||||
|
|
||||||
bindingIndex: number;
|
bindingIndex: number;
|
||||||
bindings: BindingDef[];
|
bindings: BindingDef[];
|
||||||
disposableIndex: number;
|
outputIndex: number;
|
||||||
disposableCount: number;
|
outputs: OutputDef[];
|
||||||
/**
|
/**
|
||||||
* references that the user placed on the element
|
* references that the user placed on the element
|
||||||
*/
|
*/
|
||||||
|
@ -151,12 +151,13 @@ export enum NodeFlags {
|
||||||
AfterViewChecked = 1 << 7,
|
AfterViewChecked = 1 << 7,
|
||||||
HasEmbeddedViews = 1 << 8,
|
HasEmbeddedViews = 1 << 8,
|
||||||
HasComponent = 1 << 9,
|
HasComponent = 1 << 9,
|
||||||
HasContentQuery = 1 << 10,
|
IsComponent = 1 << 10,
|
||||||
HasStaticQuery = 1 << 11,
|
HasContentQuery = 1 << 11,
|
||||||
HasDynamicQuery = 1 << 12,
|
HasStaticQuery = 1 << 12,
|
||||||
HasViewQuery = 1 << 13,
|
HasDynamicQuery = 1 << 13,
|
||||||
LazyProvider = 1 << 14,
|
HasViewQuery = 1 << 14,
|
||||||
PrivateProvider = 1 << 15,
|
LazyProvider = 1 << 15,
|
||||||
|
PrivateProvider = 1 << 16,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BindingDef {
|
export interface BindingDef {
|
||||||
|
@ -173,11 +174,24 @@ export enum BindingType {
|
||||||
ElementClass,
|
ElementClass,
|
||||||
ElementStyle,
|
ElementStyle,
|
||||||
ElementProperty,
|
ElementProperty,
|
||||||
|
ComponentHostProperty,
|
||||||
DirectiveProperty,
|
DirectiveProperty,
|
||||||
TextInterpolation,
|
TextInterpolation,
|
||||||
PureExpressionProperty
|
PureExpressionProperty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OutputDef {
|
||||||
|
type: OutputType;
|
||||||
|
target: 'window'|'document'|'body'|'component';
|
||||||
|
eventName: string;
|
||||||
|
propName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum OutputType {
|
||||||
|
ElementOutput,
|
||||||
|
DirectiveOutput
|
||||||
|
}
|
||||||
|
|
||||||
export enum QueryValueType {
|
export enum QueryValueType {
|
||||||
ElementRef,
|
ElementRef,
|
||||||
RenderElement,
|
RenderElement,
|
||||||
|
@ -191,9 +205,11 @@ export interface ElementDef {
|
||||||
ns: string;
|
ns: string;
|
||||||
/** ns, name, value */
|
/** ns, name, value */
|
||||||
attrs: [string, string, string][];
|
attrs: [string, string, string][];
|
||||||
outputs: ElementOutputDef[];
|
|
||||||
template: ViewDefinition;
|
template: ViewDefinition;
|
||||||
component: NodeDef;
|
componentProvider: NodeDef;
|
||||||
|
componentRendererType: RendererTypeV2;
|
||||||
|
// closure to allow recursive components
|
||||||
|
componentView: ViewDefinitionFactory;
|
||||||
/**
|
/**
|
||||||
* visible public providers for DI in the view,
|
* visible public providers for DI in the view,
|
||||||
* as see from this element. This does not include private providers.
|
* as see from this element. This does not include private providers.
|
||||||
|
@ -208,11 +224,6 @@ export interface ElementDef {
|
||||||
handleEvent: ElementHandleEventFn;
|
handleEvent: ElementHandleEventFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ElementOutputDef {
|
|
||||||
target: string;
|
|
||||||
eventName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ElementHandleEventFn = (view: ViewData, eventName: string, event: any) => boolean;
|
export type ElementHandleEventFn = (view: ViewData, eventName: string, event: any) => boolean;
|
||||||
|
|
||||||
export interface ProviderDef {
|
export interface ProviderDef {
|
||||||
|
@ -221,10 +232,6 @@ export interface ProviderDef {
|
||||||
tokenKey: string;
|
tokenKey: string;
|
||||||
value: any;
|
value: any;
|
||||||
deps: DepDef[];
|
deps: DepDef[];
|
||||||
outputs: DirectiveOutputDef[];
|
|
||||||
rendererType: RendererTypeV2;
|
|
||||||
// closure to allow recursive components
|
|
||||||
component: ViewDefinitionFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ProviderType {
|
export enum ProviderType {
|
||||||
|
@ -250,11 +257,6 @@ export enum DepFlags {
|
||||||
Value = 2 << 2,
|
Value = 2 << 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DirectiveOutputDef {
|
|
||||||
propName: string;
|
|
||||||
eventName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TextDef {
|
export interface TextDef {
|
||||||
prefix: string;
|
prefix: string;
|
||||||
source: string;
|
source: string;
|
||||||
|
@ -369,6 +371,7 @@ export function asTextData(view: ViewData, index: number): TextData {
|
||||||
*/
|
*/
|
||||||
export interface ElementData {
|
export interface ElementData {
|
||||||
renderElement: any;
|
renderElement: any;
|
||||||
|
componentView: ViewData;
|
||||||
embeddedViews: ViewData[];
|
embeddedViews: ViewData[];
|
||||||
// views that have been created from the template
|
// views that have been created from the template
|
||||||
// of this element,
|
// of this element,
|
||||||
|
@ -389,10 +392,7 @@ export function asElementData(view: ViewData, index: number): ElementData {
|
||||||
*
|
*
|
||||||
* Attention: Adding fields to this is performance sensitive!
|
* Attention: Adding fields to this is performance sensitive!
|
||||||
*/
|
*/
|
||||||
export interface ProviderData {
|
export interface ProviderData { instance: any; }
|
||||||
instance: any;
|
|
||||||
componentView: ViewData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
|
* 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;
|
let renderParent = def.renderParent;
|
||||||
if (renderParent) {
|
if (renderParent) {
|
||||||
const parent = def.parent;
|
const parent = def.parent;
|
||||||
if (parent && (parent.type !== NodeType.Element || !parent.element.component ||
|
if (parent &&
|
||||||
(parent.element.component.provider.rendererType &&
|
(parent.type !== NodeType.Element || (parent.flags & NodeFlags.HasComponent) === 0 ||
|
||||||
parent.element.component.provider.rendererType.encapsulation ===
|
(parent.element.componentRendererType &&
|
||||||
ViewEncapsulation.Native))) {
|
parent.element.componentRendererType.encapsulation === ViewEncapsulation.Native))) {
|
||||||
// only children of non components, or children of components with native encapsulation should
|
// only children of non components, or children of components with native encapsulation should
|
||||||
// be attached.
|
// be attached.
|
||||||
return asElementData(view, def.renderParent.index).renderElement;
|
return asElementData(view, def.renderParent.index).renderElement;
|
||||||
|
|
|
@ -9,14 +9,14 @@
|
||||||
import {ViewEncapsulation} from '../metadata/view';
|
import {ViewEncapsulation} from '../metadata/view';
|
||||||
import {RendererTypeV2, RendererV2} from '../render/api';
|
import {RendererTypeV2, RendererV2} from '../render/api';
|
||||||
|
|
||||||
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
|
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement, listenToElementOutputs} from './element';
|
||||||
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
|
||||||
import {appendNgContent} from './ng_content';
|
import {appendNgContent} from './ng_content';
|
||||||
import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAndUpdateDirectiveInline, createDirectiveInstance, createPipeInstance, createProviderInstance} from './provider';
|
import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAndUpdateDirectiveInline, createDirectiveInstance, createPipeInstance, createProviderInstance} from './provider';
|
||||||
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
|
||||||
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
|
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
|
||||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
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';
|
import {checkBindingNoChanges, isComponentView, resolveViewDefinition, viewParentEl} from './util';
|
||||||
|
|
||||||
const NOOP = (): any => undefined;
|
const NOOP = (): any => undefined;
|
||||||
|
@ -51,7 +51,7 @@ export function viewDef(
|
||||||
node.index = i;
|
node.index = i;
|
||||||
node.parent = currentParent;
|
node.parent = currentParent;
|
||||||
node.bindingIndex = viewBindingCount;
|
node.bindingIndex = viewBindingCount;
|
||||||
node.disposableIndex = viewDisposableCount;
|
node.outputIndex = viewDisposableCount;
|
||||||
node.reverseChildIndex =
|
node.reverseChildIndex =
|
||||||
calculateReverseChildIndex(currentParent, i, node.childCount, nodes.length);
|
calculateReverseChildIndex(currentParent, i, node.childCount, nodes.length);
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ export function viewDef(
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBindingCount += node.bindings.length;
|
viewBindingCount += node.bindings.length;
|
||||||
viewDisposableCount += node.disposableCount;
|
viewDisposableCount += node.outputs.length;
|
||||||
|
|
||||||
if (!currentRenderParent && (node.type === NodeType.Element || node.type === NodeType.Text)) {
|
if (!currentRenderParent && (node.type === NodeType.Element || node.type === NodeType.Text)) {
|
||||||
lastRenderRootNode = node;
|
lastRenderRootNode = node;
|
||||||
|
@ -104,7 +104,7 @@ export function viewDef(
|
||||||
currentParent.element.allProviders = currentParent.element.publicProviders;
|
currentParent.element.allProviders = currentParent.element.publicProviders;
|
||||||
}
|
}
|
||||||
const isPrivateService = (node.flags & NodeFlags.PrivateProvider) !== 0;
|
const isPrivateService = (node.flags & NodeFlags.PrivateProvider) !== 0;
|
||||||
const isComponent = (node.flags & NodeFlags.HasComponent) !== 0;
|
const isComponent = (node.flags & NodeFlags.IsComponent) !== 0;
|
||||||
if (!isPrivateService || isComponent) {
|
if (!isPrivateService || isComponent) {
|
||||||
currentParent.element.publicProviders[node.provider.tokenKey] = node;
|
currentParent.element.publicProviders[node.provider.tokenKey] = node;
|
||||||
} else {
|
} else {
|
||||||
|
@ -116,7 +116,7 @@ export function viewDef(
|
||||||
currentParent.element.allProviders[node.provider.tokenKey] = node;
|
currentParent.element.allProviders[node.provider.tokenKey] = node;
|
||||||
}
|
}
|
||||||
if (isComponent) {
|
if (isComponent) {
|
||||||
currentParent.element.component = node;
|
currentParent.element.componentProvider = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (node.childCount) {
|
if (node.childCount) {
|
||||||
|
@ -141,7 +141,7 @@ export function viewDef(
|
||||||
updateRenderer: updateRenderer || NOOP,
|
updateRenderer: updateRenderer || NOOP,
|
||||||
handleEvent: handleEvent || NOOP,
|
handleEvent: handleEvent || NOOP,
|
||||||
bindingCount: viewBindingCount,
|
bindingCount: viewBindingCount,
|
||||||
disposableCount: viewDisposableCount, lastRenderRootNode
|
outputCount: viewDisposableCount, lastRenderRootNode
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ function createView(
|
||||||
root: RootData, renderer: RendererV2, parent: ViewData, parentNodeDef: NodeDef,
|
root: RootData, renderer: RendererV2, parent: ViewData, parentNodeDef: NodeDef,
|
||||||
def: ViewDefinition): ViewData {
|
def: ViewDefinition): ViewData {
|
||||||
const nodes: NodeData[] = new Array(def.nodes.length);
|
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 = {
|
const view: ViewData = {
|
||||||
def,
|
def,
|
||||||
parent,
|
parent,
|
||||||
|
@ -271,63 +271,66 @@ function createViewNodes(view: ViewData) {
|
||||||
for (let i = 0; i < def.nodes.length; i++) {
|
for (let i = 0; i < def.nodes.length; i++) {
|
||||||
const nodeDef = def.nodes[i];
|
const nodeDef = def.nodes[i];
|
||||||
Services.setCurrentNode(view, i);
|
Services.setCurrentNode(view, i);
|
||||||
|
let nodeData: any;
|
||||||
switch (nodeDef.type) {
|
switch (nodeDef.type) {
|
||||||
case NodeType.Element:
|
case NodeType.Element:
|
||||||
nodes[i] = createElement(view, renderHost, nodeDef) as any;
|
const el = createElement(view, renderHost, nodeDef) as any;
|
||||||
break;
|
let componentView: ViewData;
|
||||||
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: {
|
|
||||||
if (nodeDef.flags & NodeFlags.HasComponent) {
|
if (nodeDef.flags & NodeFlags.HasComponent) {
|
||||||
// Components can inject a ChangeDetectorRef that needs a references to
|
const compViewDef = resolveViewDefinition(nodeDef.element.componentView);
|
||||||
// the component view. Therefore, we create the component view first
|
const rendererType = nodeDef.element.componentRendererType;
|
||||||
// and set the ProviderData in ViewData, and then instantiate the provider.
|
|
||||||
const compViewDef = resolveViewDefinition(nodeDef.provider.component);
|
|
||||||
const rendererType = nodeDef.provider.rendererType;
|
|
||||||
let compRenderer: RendererV2;
|
let compRenderer: RendererV2;
|
||||||
if (!rendererType) {
|
if (!rendererType) {
|
||||||
compRenderer = view.root.renderer;
|
compRenderer = view.root.renderer;
|
||||||
} else {
|
} else {
|
||||||
const hostEl = asElementData(view, nodeDef.parent.index).renderElement;
|
compRenderer = view.root.rendererFactory.createRenderer(el, rendererType);
|
||||||
compRenderer = view.root.rendererFactory.createRenderer(hostEl, rendererType);
|
|
||||||
}
|
}
|
||||||
const componentView = createView(view.root, compRenderer, view, nodeDef, compViewDef);
|
componentView = createView(
|
||||||
const providerData = <ProviderData>{componentView, instance: undefined};
|
view.root, compRenderer, view, nodeDef.element.componentProvider, compViewDef);
|
||||||
nodes[i] = providerData as any;
|
}
|
||||||
const instance = providerData.instance = createDirectiveInstance(view, nodeDef);
|
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);
|
initView(componentView, instance, instance);
|
||||||
} else {
|
|
||||||
const instance = createDirectiveInstance(view, nodeDef);
|
|
||||||
const providerData = <ProviderData>{componentView: undefined, instance};
|
|
||||||
nodes[i] = providerData as any;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NodeType.PureExpression:
|
case NodeType.PureExpression:
|
||||||
nodes[i] = createPureExpression(view, nodeDef) as any;
|
nodeData = createPureExpression(view, nodeDef) as any;
|
||||||
break;
|
break;
|
||||||
case NodeType.Query:
|
case NodeType.Query:
|
||||||
nodes[i] = createQuery() as any;
|
nodeData = createQuery() as any;
|
||||||
break;
|
break;
|
||||||
case NodeType.NgContent:
|
case NodeType.NgContent:
|
||||||
appendNgContent(view, renderHost, nodeDef);
|
appendNgContent(view, renderHost, nodeDef);
|
||||||
// no runtime data needed for NgContent...
|
// no runtime data needed for NgContent...
|
||||||
nodes[i] = undefined;
|
nodeData = undefined;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
nodes[i] = nodeData;
|
||||||
}
|
}
|
||||||
// Create the ViewData.nodes of component views after we created everything else,
|
// Create the ViewData.nodes of component views after we created everything else,
|
||||||
// so that e.g. ng-content works
|
// so that e.g. ng-content works
|
||||||
|
@ -479,7 +482,7 @@ export function destroyView(view: ViewData) {
|
||||||
if (view.renderer.destroyNode) {
|
if (view.renderer.destroyNode) {
|
||||||
destroyViewNodes(view);
|
destroyViewNodes(view);
|
||||||
}
|
}
|
||||||
if (view.parentNodeDef && view.parentNodeDef.flags & NodeFlags.HasComponent) {
|
if (isComponentView(view)) {
|
||||||
view.renderer.destroy();
|
view.renderer.destroy();
|
||||||
}
|
}
|
||||||
view.state |= ViewState.Destroyed;
|
view.state |= ViewState.Destroyed;
|
||||||
|
@ -513,8 +516,7 @@ function execComponentViewsAction(view: ViewData, action: ViewAction) {
|
||||||
const nodeDef = def.nodes[i];
|
const nodeDef = def.nodes[i];
|
||||||
if (nodeDef.flags & NodeFlags.HasComponent) {
|
if (nodeDef.flags & NodeFlags.HasComponent) {
|
||||||
// a leaf
|
// a leaf
|
||||||
const providerData = asProviderData(view, i);
|
callViewAction(asElementData(view, i).componentView, action);
|
||||||
callViewAction(providerData.componentView, action);
|
|
||||||
} else if ((nodeDef.childFlags & NodeFlags.HasComponent) === 0) {
|
} else if ((nodeDef.childFlags & NodeFlags.HasComponent) === 0) {
|
||||||
// a parent with leafs
|
// a parent with leafs
|
||||||
// no child is a component,
|
// no child is a component,
|
||||||
|
|
|
@ -81,42 +81,41 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('should trigger a state change animation from void => state on the component host element',
|
it('should trigger a state change animation from void => state on the component host element',
|
||||||
() => {
|
() => {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-cmp',
|
selector: 'my-cmp',
|
||||||
template: '...',
|
template: '...',
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'myAnimation',
|
'myAnimation',
|
||||||
[transition(
|
[transition(
|
||||||
'a => b',
|
'a => b', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
|
||||||
[style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])],
|
})
|
||||||
})
|
class Cmp {
|
||||||
class Cmp {
|
@HostBinding('@myAnimation')
|
||||||
@HostBinding('@myAnimation')
|
get binding() { return this.exp ? 'b' : 'a'; }
|
||||||
get binding() { return this.exp ? 'b' : 'a'; }
|
exp: any = false;
|
||||||
exp: any = false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||||
|
|
||||||
const engine = TestBed.get(ɵAnimationEngine);
|
const engine = TestBed.get(ɵAnimationEngine);
|
||||||
const fixture = TestBed.createComponent(Cmp);
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
const cmp = fixture.componentInstance;
|
const cmp = fixture.componentInstance;
|
||||||
cmp.exp = false;
|
cmp.exp = false;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
engine.flush();
|
engine.flush();
|
||||||
expect(getLog().length).toEqual(0);
|
expect(getLog().length).toEqual(0);
|
||||||
|
|
||||||
cmp.exp = true;
|
cmp.exp = true;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
engine.flush();
|
engine.flush();
|
||||||
expect(getLog().length).toEqual(1);
|
expect(getLog().length).toEqual(1);
|
||||||
|
|
||||||
const data = getLog().pop();
|
const data = getLog().pop();
|
||||||
expect(data.element).toEqual(fixture.elementRef.nativeElement);
|
expect(data.element).toEqual(fixture.elementRef.nativeElement);
|
||||||
expect(data.keyframes).toEqual([{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}]);
|
expect(data.keyframes).toEqual([{offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should cancel and merge in mid-animation styles into the follow-up animation', () => {
|
it('should cancel and merge in mid-animation styles into the follow-up animation', () => {
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -516,42 +515,42 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
expect(cmp.event2.triggerName).toBeTruthy('ani2');
|
expect(cmp.event2.triggerName).toBeTruthy('ani2');
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('should trigger a state change listener for when the animation changes state from void => state on the host element',
|
it('should trigger a state change listener for when the animation changes state from void => state on the host element',
|
||||||
() => {
|
() => {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-cmp',
|
selector: 'my-cmp',
|
||||||
template: `...`,
|
template: `...`,
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'myAnimation2',
|
'myAnimation2',
|
||||||
[transition(
|
[transition(
|
||||||
'void => *',
|
'void => *',
|
||||||
[style({'opacity': '0'}), animate(1000, style({'opacity': '1'}))])])],
|
[style({'opacity': '0'}), animate(1000, style({'opacity': '1'}))])])],
|
||||||
})
|
})
|
||||||
class Cmp {
|
class Cmp {
|
||||||
event: AnimationEvent;
|
event: AnimationEvent;
|
||||||
|
|
||||||
@HostBinding('@myAnimation2')
|
@HostBinding('@myAnimation2')
|
||||||
exp: any = false;
|
exp: any = false;
|
||||||
|
|
||||||
@HostListener('@myAnimation2.start')
|
@HostListener('@myAnimation2.start', ['$event'])
|
||||||
callback = (event: any) => { this.event = event; };
|
callback = (event: any) => { this.event = event; };
|
||||||
}
|
}
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||||
|
|
||||||
const engine = TestBed.get(ɵAnimationEngine);
|
const engine = TestBed.get(ɵAnimationEngine);
|
||||||
const fixture = TestBed.createComponent(Cmp);
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
const cmp = fixture.componentInstance;
|
const cmp = fixture.componentInstance;
|
||||||
cmp.exp = 'TRUE';
|
cmp.exp = 'TRUE';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
engine.flush();
|
engine.flush();
|
||||||
|
|
||||||
expect(cmp.event.triggerName).toEqual('myAnimation2');
|
expect(cmp.event.triggerName).toEqual('myAnimation2');
|
||||||
expect(cmp.event.phaseName).toEqual('start');
|
expect(cmp.event.phaseName).toEqual('start');
|
||||||
expect(cmp.event.totalTime).toEqual(1000);
|
expect(cmp.event.totalTime).toEqual(1000);
|
||||||
expect(cmp.event.fromState).toEqual('void');
|
expect(cmp.event.fromState).toEqual('void');
|
||||||
expect(cmp.event.toState).toEqual('TRUE');
|
expect(cmp.event.toState).toEqual('TRUE');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,294 +7,306 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||||
import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, anchorDef, asProviderData, directiveDef, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, OutputType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, anchorDef, asElementData, asProviderData, directiveDef, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {createRootView, isBrowser, removeNodes} from './helper';
|
import {createRootView, isBrowser, removeNodes} from './helper';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe(`Component Views`, () => {
|
describe(
|
||||||
function compViewDef(
|
`Component Views`, () => {
|
||||||
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
function compViewDef(
|
||||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
|
||||||
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer);
|
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||||
}
|
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer);
|
||||||
|
|
||||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
|
||||||
const view = createRootView(viewDef);
|
|
||||||
const rootNodes = rootRenderNodes(view);
|
|
||||||
return {rootNodes, view};
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should create and attach component views', () => {
|
|
||||||
let instance: AComp;
|
|
||||||
class AComp {
|
|
||||||
constructor() { instance = this; }
|
|
||||||
}
|
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
|
||||||
directiveDef(
|
|
||||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
|
||||||
() => compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
|
||||||
])),
|
|
||||||
]));
|
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
|
||||||
|
|
||||||
expect(compView.context).toBe(instance);
|
|
||||||
expect(compView.component).toBe(instance);
|
|
||||||
|
|
||||||
const compRootEl = getDOM().childNodes(rootNodes[0])[0];
|
|
||||||
expect(getDOM().nodeName(compRootEl).toLowerCase()).toBe('span');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isBrowser()) {
|
|
||||||
describe('root views', () => {
|
|
||||||
let rootNode: HTMLElement;
|
|
||||||
beforeEach(() => {
|
|
||||||
rootNode = document.createElement('root');
|
|
||||||
document.body.appendChild(rootNode);
|
|
||||||
removeNodes.push(rootNode);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select root elements based on a selector', () => {
|
|
||||||
const view = createRootView(
|
|
||||||
compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
|
||||||
]),
|
|
||||||
{}, [], 'root');
|
|
||||||
const rootNodes = rootRenderNodes(view);
|
|
||||||
expect(rootNodes).toEqual([rootNode]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should select root elements based on a node', () => {
|
|
||||||
const view = createRootView(
|
|
||||||
compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
|
||||||
]),
|
|
||||||
{}, [], rootNode);
|
|
||||||
const rootNodes = rootRenderNodes(view);
|
|
||||||
expect(rootNodes).toEqual([rootNode]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set attributes on the root node', () => {
|
|
||||||
const view = createRootView(
|
|
||||||
compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'div', [['a', 'b']]),
|
|
||||||
]),
|
|
||||||
{}, [], rootNode);
|
|
||||||
expect(rootNode.getAttribute('a')).toBe('b');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should clear the content of the root node', () => {
|
|
||||||
rootNode.appendChild(document.createElement('div'));
|
|
||||||
const view = createRootView(
|
|
||||||
compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'div', [['a', 'b']]),
|
|
||||||
]),
|
|
||||||
{}, [], rootNode);
|
|
||||||
expect(rootNode.childNodes.length).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('data binding', () => {
|
|
||||||
it('should dirty check component views', () => {
|
|
||||||
let value: any;
|
|
||||||
class AComp {
|
|
||||||
a: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const update =
|
function createAndGetRootNodes(viewDef: ViewDefinition):
|
||||||
jasmine.createSpy('updater').and.callFake((check: NodeCheckFn, view: ViewData) => {
|
{rootNodes: any[], view: ViewData} {
|
||||||
check(view, 0, ArgumentType.Inline, value);
|
const view = createRootView(viewDef);
|
||||||
|
const rootNodes = rootRenderNodes(view);
|
||||||
|
return {rootNodes, view};
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should create and attach component views', () => {
|
||||||
|
let instance: AComp;
|
||||||
|
class AComp {
|
||||||
|
constructor() { instance = this; }
|
||||||
|
}
|
||||||
|
|
||||||
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
|
elementDef(
|
||||||
|
NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
||||||
|
() => compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
||||||
|
])),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, AComp, []),
|
||||||
|
]));
|
||||||
|
|
||||||
|
const compView = asElementData(view, 0).componentView;
|
||||||
|
|
||||||
|
expect(compView.context).toBe(instance);
|
||||||
|
expect(compView.component).toBe(instance);
|
||||||
|
|
||||||
|
const compRootEl = getDOM().childNodes(rootNodes[0])[0];
|
||||||
|
expect(getDOM().nodeName(compRootEl).toLowerCase()).toBe('span');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isBrowser()) {
|
||||||
|
describe('root views', () => {
|
||||||
|
let rootNode: HTMLElement;
|
||||||
|
beforeEach(() => {
|
||||||
|
rootNode = document.createElement('root');
|
||||||
|
document.body.appendChild(rootNode);
|
||||||
|
removeNodes.push(rootNode);
|
||||||
});
|
});
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(
|
it('should select root elements based on a selector', () => {
|
||||||
|
const view = createRootView(
|
||||||
|
compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
||||||
|
]),
|
||||||
|
{}, [], 'root');
|
||||||
|
const rootNodes = rootRenderNodes(view);
|
||||||
|
expect(rootNodes).toEqual([rootNode]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select root elements based on a node', () => {
|
||||||
|
const view = createRootView(
|
||||||
|
compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
||||||
|
]),
|
||||||
|
{}, [], rootNode);
|
||||||
|
const rootNodes = rootRenderNodes(view);
|
||||||
|
expect(rootNodes).toEqual([rootNode]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set attributes on the root node', () => {
|
||||||
|
const view = createRootView(
|
||||||
|
compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 0, 'div', [['a', 'b']]),
|
||||||
|
]),
|
||||||
|
{}, [], rootNode);
|
||||||
|
expect(rootNode.getAttribute('a')).toBe('b');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear the content of the root node', () => {
|
||||||
|
rootNode.appendChild(document.createElement('div'));
|
||||||
|
const view = createRootView(
|
||||||
|
compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 0, 'div', [['a', 'b']]),
|
||||||
|
]),
|
||||||
|
{}, [], rootNode);
|
||||||
|
expect(rootNode.childNodes.length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe(
|
||||||
|
'data binding', () => {
|
||||||
|
it('should dirty check component views',
|
||||||
|
() => {
|
||||||
|
let value: any;
|
||||||
|
class AComp {
|
||||||
|
a: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const update = jasmine.createSpy('updater').and.callFake(
|
||||||
|
(check: NodeCheckFn, view: ViewData) => {
|
||||||
|
check(view, 0, ArgumentType.Inline, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const {view, rootNodes} = createAndGetRootNodes(
|
||||||
compViewDef([
|
compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(NodeFlags.None, null, null, 1, 'div', null, null, null, null, () => compViewDef(
|
||||||
directiveDef(NodeFlags.None, null, 0, AComp, [], null, null, () => compViewDef(
|
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
||||||
], null, update
|
], null, update
|
||||||
)),
|
)),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, AComp, []),
|
||||||
]));
|
]));
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
|
|
||||||
value = 'v1';
|
value = 'v1';
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
||||||
|
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
Services.checkNoChangesView(view);
|
Services.checkNoChangesView(view);
|
||||||
|
|
||||||
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
||||||
|
|
||||||
value = 'v2';
|
value = 'v2';
|
||||||
expect(() => Services.checkNoChangesView(view))
|
expect(() => Services.checkNoChangesView(view))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support detaching and attaching component views for dirty checking', () => {
|
it('should support detaching and attaching component views for dirty checking',
|
||||||
class AComp {
|
() => {
|
||||||
a: any;
|
class AComp {
|
||||||
}
|
a: any;
|
||||||
|
}
|
||||||
|
|
||||||
const update = jasmine.createSpy('updater');
|
const update = jasmine.createSpy('updater');
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(
|
||||||
directiveDef(
|
NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
||||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
() => compViewDef(
|
||||||
() => compViewDef(
|
[
|
||||||
[
|
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
],
|
||||||
],
|
update)),
|
||||||
update)),
|
directiveDef(NodeFlags.IsComponent, null, 0, AComp, [], null, null),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
|
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
|
|
||||||
compView.state &= ~ViewState.ChecksEnabled;
|
compView.state &= ~ViewState.ChecksEnabled;
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
|
|
||||||
compView.state |= ViewState.ChecksEnabled;
|
compView.state |= ViewState.ChecksEnabled;
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
it('should support OnPush components', () => {
|
it('should support OnPush components', () => {
|
||||||
let compInputValue: any;
|
let compInputValue: any;
|
||||||
class AComp {
|
class AComp {
|
||||||
a: any;
|
a: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const update = jasmine.createSpy('updater');
|
const update = jasmine.createSpy('updater');
|
||||||
|
|
||||||
const addListenerSpy = spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
|
const addListenerSpy =
|
||||||
|
spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
|
||||||
|
|
||||||
const {view} = createAndGetRootNodes(compViewDef(
|
const {view} = createAndGetRootNodes(compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(
|
||||||
directiveDef(
|
NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
||||||
NodeFlags.None, null, 0, AComp, [], {a: [0, 'a']}, null,
|
() => {
|
||||||
() => compViewDef(
|
return compViewDef(
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span', null, null, ['click']),
|
elementDef(
|
||||||
],
|
NodeFlags.None, null, null, 0, 'span', null, null,
|
||||||
update, null, ViewFlags.OnPush)),
|
[[null, 'click']]),
|
||||||
],
|
],
|
||||||
(check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); }));
|
update, null, ViewFlags.OnPush);
|
||||||
|
}),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, AComp, [], {a: [0, 'a']}),
|
||||||
|
],
|
||||||
|
(check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); }));
|
||||||
|
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
// auto detach
|
// auto detach
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
|
|
||||||
// auto attach on input changes
|
// auto attach on input changes
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
compInputValue = 'v1';
|
compInputValue = 'v1';
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
|
|
||||||
// auto detach
|
// auto detach
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
|
|
||||||
// auto attach on events
|
// auto attach on events
|
||||||
addListenerSpy.calls.mostRecent().args[1]('SomeEvent');
|
addListenerSpy.calls.mostRecent().args[1]('SomeEvent');
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).toHaveBeenCalled();
|
expect(update).toHaveBeenCalled();
|
||||||
|
|
||||||
// auto detach
|
// auto detach
|
||||||
update.calls.reset();
|
update.calls.reset();
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
expect(update).not.toHaveBeenCalled();
|
expect(update).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should stop dirty checking views that threw errors in change detection', () => {
|
it('should stop dirty checking views that threw errors in change detection',
|
||||||
class AComp {
|
() => {
|
||||||
a: any;
|
class AComp {
|
||||||
}
|
a: any;
|
||||||
|
}
|
||||||
|
|
||||||
const update = jasmine.createSpy('updater');
|
const update = jasmine.createSpy('updater');
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(NodeFlags.None, null, null, 1, 'div', null, null, null, null, () => compViewDef(
|
||||||
directiveDef(
|
|
||||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
|
||||||
() => compViewDef(
|
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
||||||
],
|
],
|
||||||
null, update)),
|
null, update)),
|
||||||
]));
|
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
|
||||||
|
|
||||||
update.and.callFake((check: NodeCheckFn, view: ViewData) => { throw new Error('Test'); });
|
|
||||||
expect(() => Services.checkAndUpdateView(view)).toThrowError('Test');
|
|
||||||
expect(update).toHaveBeenCalled();
|
|
||||||
|
|
||||||
update.calls.reset();
|
|
||||||
Services.checkAndUpdateView(view);
|
|
||||||
expect(update).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('destroy', () => {
|
|
||||||
it('should destroy component views', () => {
|
|
||||||
const log: string[] = [];
|
|
||||||
|
|
||||||
class AComp {}
|
|
||||||
|
|
||||||
class ChildProvider {
|
|
||||||
ngOnDestroy() { log.push('ngOnDestroy'); };
|
|
||||||
}
|
|
||||||
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
|
||||||
directiveDef(
|
directiveDef(
|
||||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
NodeFlags.IsComponent, null, 0, AComp, [], null, null,
|
||||||
() => compViewDef([
|
),
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
|
||||||
directiveDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
|
|
||||||
])),
|
|
||||||
]));
|
]));
|
||||||
|
|
||||||
Services.destroyView(view);
|
const compView = asElementData(view, 0).componentView;
|
||||||
|
|
||||||
|
update.and.callFake(
|
||||||
|
(check: NodeCheckFn, view: ViewData) => { throw new Error('Test'); });
|
||||||
|
expect(() => Services.checkAndUpdateView(view)).toThrowError('Test');
|
||||||
|
expect(update).toHaveBeenCalled();
|
||||||
|
|
||||||
|
update.calls.reset();
|
||||||
|
Services.checkAndUpdateView(view);
|
||||||
|
expect(update).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('destroy', () => {
|
||||||
|
it('should destroy component views', () => {
|
||||||
|
const log: string[] = [];
|
||||||
|
|
||||||
|
class AComp {}
|
||||||
|
|
||||||
|
class ChildProvider {
|
||||||
|
ngOnDestroy() { log.push('ngOnDestroy'); };
|
||||||
|
}
|
||||||
|
|
||||||
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
|
elementDef(
|
||||||
|
NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
||||||
|
() => compViewDef([
|
||||||
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
|
directiveDef(NodeFlags.OnDestroy, null, 0, ChildProvider, [])
|
||||||
|
])),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, AComp, [], null, null, ),
|
||||||
|
]));
|
||||||
|
|
||||||
|
Services.destroyView(view);
|
||||||
|
|
||||||
|
expect(log).toEqual(['ngOnDestroy']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw on dirty checking destroyed views', () => {
|
||||||
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||||
|
[
|
||||||
|
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
||||||
|
],
|
||||||
|
(view) => {}));
|
||||||
|
|
||||||
|
Services.destroyView(view);
|
||||||
|
|
||||||
|
expect(() => Services.checkAndUpdateView(view))
|
||||||
|
.toThrowError('ViewDestroyedError: Attempt to use a destroyed view: detectChanges');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
expect(log).toEqual(['ngOnDestroy']);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw on dirty checking destroyed views', () => {
|
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
|
||||||
[
|
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
|
||||||
],
|
|
||||||
(view) => {}));
|
|
||||||
|
|
||||||
Services.destroyView(view);
|
|
||||||
|
|
||||||
expect(() => Services.checkAndUpdateView(view))
|
|
||||||
.toThrowError('ViewDestroyedError: Attempt to use a destroyed view: detectChanges');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||||
import {getDebugContext} from '@angular/core/src/errors';
|
import {getDebugContext} from '@angular/core/src/errors';
|
||||||
import {ArgumentType, BindingType, DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
import {ArgumentType, BindingType, DebugContext, NodeDef, NodeFlags, OutputType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
|
|
||||||
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser, removeNodes} from './helper';
|
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser, removeNodes} from './helper';
|
||||||
|
@ -190,7 +190,8 @@ export function main() {
|
||||||
const removeListenerSpy =
|
const removeListenerSpy =
|
||||||
spyOn(HTMLElement.prototype, 'removeEventListener').and.callThrough();
|
spyOn(HTMLElement.prototype, 'removeEventListener').and.callThrough();
|
||||||
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef(
|
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef(
|
||||||
NodeFlags.None, null, null, 0, 'button', null, null, ['click'], handleEventSpy)]));
|
NodeFlags.None, null, null, 0, 'button', null, null, [[null, 'click']],
|
||||||
|
handleEventSpy)]));
|
||||||
|
|
||||||
rootNodes[0].click();
|
rootNodes[0].click();
|
||||||
|
|
||||||
|
@ -256,7 +257,7 @@ export function main() {
|
||||||
let preventDefaultSpy: jasmine.Spy;
|
let preventDefaultSpy: jasmine.Spy;
|
||||||
|
|
||||||
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef(
|
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef(
|
||||||
NodeFlags.None, null, null, 0, 'button', null, null, ['click'],
|
NodeFlags.None, null, null, 0, 'button', null, null, [[null, 'click']],
|
||||||
(view, eventName, event) => {
|
(view, eventName, event) => {
|
||||||
preventDefaultSpy = spyOn(event, 'preventDefault').and.callThrough();
|
preventDefaultSpy = spyOn(event, 'preventDefault').and.callThrough();
|
||||||
return eventHandlerResult;
|
return eventHandlerResult;
|
||||||
|
@ -281,10 +282,9 @@ export function main() {
|
||||||
|
|
||||||
it('should report debug info on event errors', () => {
|
it('should report debug info on event errors', () => {
|
||||||
const addListenerSpy = spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
|
const addListenerSpy = spyOn(HTMLElement.prototype, 'addEventListener').and.callThrough();
|
||||||
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef(
|
const {view, rootNodes} = createAndAttachAndGetRootNodes(compViewDef([elementDef(
|
||||||
[elementDef(NodeFlags.None, null, null, 0, 'button', null, null, ['click'], () => {
|
NodeFlags.None, null, null, 0, 'button', null, null, [[null, 'click']],
|
||||||
throw new Error('Test');
|
() => { throw new Error('Test'); })]));
|
||||||
})]));
|
|
||||||
|
|
||||||
let err: any;
|
let err: any;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -30,9 +30,10 @@ export function main() {
|
||||||
const aCompViewDef = compViewDef(viewNodes);
|
const aCompViewDef = compViewDef(viewNodes);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
elementDef(NodeFlags.None, null, null, 1 + contentNodes.length, 'acomp'),
|
elementDef(
|
||||||
directiveDef(NodeFlags.None, null, 0, AComp, [], null, null, () => aCompViewDef),
|
NodeFlags.None, null, null, 1 + contentNodes.length, 'acomp', null, null, null, null,
|
||||||
...contentNodes
|
() => aCompViewDef),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, AComp, []), ...contentNodes
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +111,7 @@ export function main() {
|
||||||
])),
|
])),
|
||||||
])));
|
])));
|
||||||
|
|
||||||
const componentView = asProviderData(view, 1).componentView;
|
const componentView = asElementData(view, 0).componentView;
|
||||||
const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]);
|
const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]);
|
||||||
|
|
||||||
attachEmbeddedView(asElementData(componentView, 1), 0, view0);
|
attachEmbeddedView(asElementData(componentView, 1), 0, view0);
|
||||||
|
|
|
@ -113,10 +113,10 @@ export function main() {
|
||||||
try {
|
try {
|
||||||
createRootView(
|
createRootView(
|
||||||
compViewDef([
|
compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(
|
||||||
directiveDef(
|
NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
||||||
NodeFlags.None, null, 0, SomeService, [], null, null,
|
() => compViewDef([textDef(null, ['a'])])),
|
||||||
() => compViewDef([textDef(null, ['a'])]))
|
directiveDef(NodeFlags.IsComponent, null, 0, SomeService, [])
|
||||||
]),
|
]),
|
||||||
TestBed.get(Injector), [], getDOM().createElement('div'));
|
TestBed.get(Injector), [], getDOM().createElement('div'));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -174,13 +174,13 @@ export function main() {
|
||||||
|
|
||||||
it('should inject from a parent elment in a parent view', () => {
|
it('should inject from a parent elment in a parent view', () => {
|
||||||
createAndGetRootNodes(compViewDef([
|
createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(
|
||||||
directiveDef(
|
NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
||||||
NodeFlags.None, null, 0, Dep, [], null, null,
|
|
||||||
() => compViewDef([
|
() => compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
directiveDef(NodeFlags.None, null, 0, SomeService, [Dep])
|
directiveDef(NodeFlags.None, null, 0, SomeService, [Dep])
|
||||||
])),
|
])),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, Dep, []),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
expect(instance.dep instanceof Dep).toBeTruthy();
|
expect(instance.dep instanceof Dep).toBeTruthy();
|
||||||
|
@ -275,24 +275,24 @@ export function main() {
|
||||||
|
|
||||||
it('should inject ChangeDetectorRef for component providers', () => {
|
it('should inject ChangeDetectorRef for component providers', () => {
|
||||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(
|
||||||
directiveDef(
|
NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
||||||
NodeFlags.None, null, 0, SomeService, [ChangeDetectorRef], null, null,
|
|
||||||
() => compViewDef([
|
() => compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
||||||
])),
|
])),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, SomeService, [ChangeDetectorRef]),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
expect(instance.dep._view).toBe(compView);
|
expect(instance.dep._view).toBe(compView);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should inject RendererV1', () => {
|
it('should inject RendererV1', () => {
|
||||||
createAndGetRootNodes(compViewDef([
|
createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
elementDef(
|
||||||
directiveDef(
|
NodeFlags.None, null, null, 1, 'span', null, null, null, null,
|
||||||
NodeFlags.None, null, 0, SomeService, [Renderer], null, null,
|
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)])),
|
||||||
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)]))
|
directiveDef(NodeFlags.IsComponent, null, 0, SomeService, [Renderer])
|
||||||
]));
|
]));
|
||||||
|
|
||||||
expect(instance.dep.createElement).toBeTruthy();
|
expect(instance.dep.createElement).toBeTruthy();
|
||||||
|
@ -300,10 +300,10 @@ export function main() {
|
||||||
|
|
||||||
it('should inject RendererV2', () => {
|
it('should inject RendererV2', () => {
|
||||||
createAndGetRootNodes(compViewDef([
|
createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
elementDef(
|
||||||
directiveDef(
|
NodeFlags.None, null, null, 1, 'span', null, null, null, null,
|
||||||
NodeFlags.None, null, 0, SomeService, [RendererV2], null, null,
|
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)])),
|
||||||
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)]))
|
directiveDef(NodeFlags.IsComponent, null, 0, SomeService, [RendererV2])
|
||||||
]));
|
]));
|
||||||
|
|
||||||
expect(instance.dep.createElement).toBeTruthy();
|
expect(instance.dep.createElement).toBeTruthy();
|
||||||
|
|
|
@ -50,16 +50,17 @@ export function main() {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewQueryProviders(nodes: NodeDef[]) {
|
function compViewQueryProviders(extraChildCount: number, nodes: NodeDef[]) {
|
||||||
return [
|
return [
|
||||||
directiveDef(
|
elementDef(
|
||||||
NodeFlags.None, null, 0, QueryService, [], null, null,
|
NodeFlags.None, null, null, 1 + extraChildCount, 'div', null, null, null, null,
|
||||||
() => compViewDef([
|
() => compViewDef([
|
||||||
queryDef(
|
queryDef(
|
||||||
NodeFlags.HasViewQuery | NodeFlags.HasDynamicQuery, someQueryId,
|
NodeFlags.HasViewQuery | NodeFlags.HasDynamicQuery, someQueryId,
|
||||||
{'a': QueryBindingType.All}),
|
{'a': QueryBindingType.All}),
|
||||||
...nodes
|
...nodes
|
||||||
])),
|
])),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, QueryService, [], null, null, ),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,27 +111,29 @@ export function main() {
|
||||||
describe('view queries', () => {
|
describe('view queries', () => {
|
||||||
it('should query providers in the view', () => {
|
it('should query providers in the view', () => {
|
||||||
const {view} = createAndGetRootNodes(compViewDef([
|
const {view} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
...compViewQueryProviders(
|
||||||
...viewQueryProviders([
|
0,
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
[
|
||||||
aServiceProvider(),
|
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||||
]),
|
aServiceProvider(),
|
||||||
|
]),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
||||||
const comp: QueryService = asProviderData(view, 1).instance;
|
const comp: QueryService = asProviderData(view, 1).instance;
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
expect(comp.a.length).toBe(1);
|
expect(comp.a.length).toBe(1);
|
||||||
expect(comp.a.first).toBe(asProviderData(compView, 2).instance);
|
expect(comp.a.first).toBe(asProviderData(compView, 2).instance);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not query providers on the host element', () => {
|
it('should not query providers on the host element', () => {
|
||||||
const {view} = createAndGetRootNodes(compViewDef([
|
const {view} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 2, 'div'),
|
...compViewQueryProviders(
|
||||||
...viewQueryProviders([
|
1,
|
||||||
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
[
|
||||||
]),
|
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
||||||
|
]),
|
||||||
aServiceProvider(),
|
aServiceProvider(),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
@ -225,13 +228,14 @@ export function main() {
|
||||||
|
|
||||||
it('should update view queries if embedded views are added or removed', () => {
|
it('should update view queries if embedded views are added or removed', () => {
|
||||||
const {view} = createAndGetRootNodes(compViewDef([
|
const {view} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
...compViewQueryProviders(
|
||||||
...viewQueryProviders([
|
0,
|
||||||
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, null, embeddedViewDef([
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, null, embeddedViewDef([
|
||||||
aServiceProvider(),
|
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||||
])),
|
aServiceProvider(),
|
||||||
]),
|
])),
|
||||||
|
]),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
@ -239,7 +243,7 @@ export function main() {
|
||||||
const comp: QueryService = asProviderData(view, 1).instance;
|
const comp: QueryService = asProviderData(view, 1).instance;
|
||||||
expect(comp.a.length).toBe(0);
|
expect(comp.a.length).toBe(0);
|
||||||
|
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
const childView = Services.createEmbeddedView(compView, compView.def.nodes[1]);
|
const childView = Services.createEmbeddedView(compView, compView.def.nodes[1]);
|
||||||
attachEmbeddedView(asElementData(compView, 1), 0, childView);
|
attachEmbeddedView(asElementData(compView, 1), 0, childView);
|
||||||
Services.checkAndUpdateView(view);
|
Services.checkAndUpdateView(view);
|
||||||
|
|
|
@ -35,20 +35,20 @@ export function main() {
|
||||||
|
|
||||||
function createViewWithData() {
|
function createViewWithData() {
|
||||||
const {view} = createAndGetRootNodes(compViewDef([
|
const {view} = createAndGetRootNodes(compViewDef([
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
elementDef(
|
||||||
directiveDef(
|
NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
||||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
|
||||||
() => compViewDef([
|
() => compViewDef([
|
||||||
elementDef(NodeFlags.None, [['ref', QueryValueType.ElementRef]], null, 2, 'span'),
|
elementDef(NodeFlags.None, [['ref', QueryValueType.ElementRef]], null, 2, 'span'),
|
||||||
directiveDef(NodeFlags.None, null, 0, AService, []), textDef(null, ['a'])
|
directiveDef(NodeFlags.None, null, 0, AService, []), textDef(null, ['a'])
|
||||||
])),
|
])),
|
||||||
|
directiveDef(NodeFlags.IsComponent, null, 0, AComp, []),
|
||||||
]));
|
]));
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should provide data for elements', () => {
|
it('should provide data for elements', () => {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
|
|
||||||
const debugCtx = Services.createDebugContext(compView, 0);
|
const debugCtx = Services.createDebugContext(compView, 0);
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ export function main() {
|
||||||
|
|
||||||
it('should provide data for text nodes', () => {
|
it('should provide data for text nodes', () => {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
|
|
||||||
const debugCtx = Services.createDebugContext(compView, 2);
|
const debugCtx = Services.createDebugContext(compView, 2);
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ export function main() {
|
||||||
|
|
||||||
it('should provide data for other nodes based on the nearest element parent', () => {
|
it('should provide data for other nodes based on the nearest element parent', () => {
|
||||||
const view = createViewWithData();
|
const view = createViewWithData();
|
||||||
const compView = asProviderData(view, 1).componentView;
|
const compView = asElementData(view, 0).componentView;
|
||||||
|
|
||||||
const debugCtx = Services.createDebugContext(compView, 1);
|
const debugCtx = Services.createDebugContext(compView, 1);
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ let viewFlags = ViewFlags.None;
|
||||||
|
|
||||||
function TreeComponent_Host(): ViewDefinition {
|
function TreeComponent_Host(): ViewDefinition {
|
||||||
return viewDef(viewFlags, [
|
return viewDef(viewFlags, [
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'tree'),
|
elementDef(NodeFlags.None, null, null, 1, 'tree', null, null, null, null, TreeComponent_0),
|
||||||
directiveDef(NodeFlags.None, null, 0, TreeComponent, [], null, null, TreeComponent_0),
|
directiveDef(NodeFlags.IsComponent, null, 0, TreeComponent, []),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,9 +35,8 @@ function TreeComponent_1() {
|
||||||
return viewDef(
|
return viewDef(
|
||||||
viewFlags,
|
viewFlags,
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'tree'),
|
elementDef(NodeFlags.None, null, null, 1, 'tree', null, null, null, null, TreeComponent_0),
|
||||||
directiveDef(
|
directiveDef(NodeFlags.IsComponent, null, 0, TreeComponent, [], {data: [0, 'data']}),
|
||||||
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
|
|
||||||
],
|
],
|
||||||
(check, view) => {
|
(check, view) => {
|
||||||
const cmp = view.component;
|
const cmp = view.component;
|
||||||
|
@ -49,9 +48,8 @@ function TreeComponent_2() {
|
||||||
return viewDef(
|
return viewDef(
|
||||||
viewFlags,
|
viewFlags,
|
||||||
[
|
[
|
||||||
elementDef(NodeFlags.None, null, null, 1, 'tree'),
|
elementDef(NodeFlags.None, null, null, 1, 'tree', null, null, null, null, TreeComponent_0),
|
||||||
directiveDef(
|
directiveDef(NodeFlags.IsComponent, null, 0, TreeComponent, [], {data: [0, 'data']}),
|
||||||
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
|
|
||||||
],
|
],
|
||||||
(check, view) => {
|
(check, view) => {
|
||||||
const cmp = view.component;
|
const cmp = view.component;
|
||||||
|
|
Loading…
Reference in New Issue