parent
64516da6b0
commit
c8a4fb1faf
@ -58,4 +58,121 @@ describe('compiler compliance: listen()', () => {
|
|||||||
expectEmit(result.source, template, 'Incorrect template');
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create multiple listener instructions that share a view snapshot', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`
|
||||||
|
<div *ngIf="showing">
|
||||||
|
<div (click)="onClick(foo)"></div>
|
||||||
|
<button (click)="onClick2(bar)"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
\`
|
||||||
|
})
|
||||||
|
export class MyComponent {
|
||||||
|
onClick(name: any) {}
|
||||||
|
onClick2(name: any) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent], imports: [CommonModule]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
const $c0$ = ["ngIf",""];
|
||||||
|
|
||||||
|
function MyComponent_div_Template_0(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
const $s$ = $r3$.ɵgV();
|
||||||
|
$r3$.ɵE(0, "div");
|
||||||
|
$r3$.ɵE(1, "div");
|
||||||
|
$r3$.ɵL("click", function MyComponent_div_Template_0_div_click_listener($event) {
|
||||||
|
$r3$.ɵrV($s$);
|
||||||
|
const $comp$ = $r3$.ɵx();
|
||||||
|
return $comp$.onClick($comp$.foo);
|
||||||
|
});
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵE(2, "button");
|
||||||
|
$r3$.ɵL("click", function MyComponent_div_Template_0_button_click_listener($event) {
|
||||||
|
$r3$.ɵrV($s$);
|
||||||
|
const $comp2$ = $r3$.ɵx();
|
||||||
|
return $comp2$.onClick2($comp2$.bar);
|
||||||
|
});
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵC(0, MyComponent_div_Template_0, null, $c0$);
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
$i0$.ɵp(0, "ngIf", $i0$.ɵb(ctx.showing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('local refs in listeners defined before the local refs', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`
|
||||||
|
<button (click)="onClick(user.value)">Save</button>
|
||||||
|
<input #user>
|
||||||
|
\`
|
||||||
|
})
|
||||||
|
export class MyComponent {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const MyComponentDefinition = `
|
||||||
|
const $c0$ = ["user", ""];
|
||||||
|
…
|
||||||
|
MyComponent.ngComponentDef = $r3$.ɵdefineComponent({
|
||||||
|
type: MyComponent,
|
||||||
|
selectors: [["my-component"]],
|
||||||
|
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||||
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$r3$.ɵE(0, "button");
|
||||||
|
$r3$.ɵL("click", function MyComponent_Template_button_click_listener($event) {
|
||||||
|
const $user$ = $r3$.ɵr(3);
|
||||||
|
return ctx.onClick($user$.value);
|
||||||
|
});
|
||||||
|
$r3$.ɵT(1, "Save");
|
||||||
|
$r3$.ɵe();
|
||||||
|
$r3$.ɵEe(2, "input", null, $c0$);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
const source = result.source;
|
||||||
|
|
||||||
|
expectEmit(source, MyComponentDefinition, 'Incorrect MyComponent.ngComponentDef');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -55,12 +55,14 @@ describe('compiler compliance: template', () => {
|
|||||||
function MyComponent_ul_li_div_Template_1(rf, ctx) {
|
function MyComponent_ul_li_div_Template_1(rf, ctx) {
|
||||||
|
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
|
const $s$ = $i0$.ɵgV();
|
||||||
|
$i0$.ɵE(0, "div");
|
||||||
|
$i0$.ɵL("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
|
||||||
|
$i0$.ɵrV($s$);
|
||||||
const $inner$ = ctx.$implicit;
|
const $inner$ = ctx.$implicit;
|
||||||
const $middle$ = $i0$.ɵx().$implicit;
|
const $middle$ = $i0$.ɵx().$implicit;
|
||||||
const $outer$ = $i0$.ɵx().$implicit;
|
const $outer$ = $i0$.ɵx().$implicit;
|
||||||
const $myComp$ = $i0$.ɵx();
|
const $myComp$ = $i0$.ɵx();
|
||||||
$i0$.ɵE(0, "div");
|
|
||||||
$i0$.ɵL("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
|
|
||||||
return $myComp$.onClick($outer$, $middle$, $inner$);
|
return $myComp$.onClick($outer$, $middle$, $inner$);
|
||||||
});
|
});
|
||||||
$i0$.ɵT(1);
|
$i0$.ɵT(1);
|
||||||
|
@ -53,6 +53,10 @@ export class Identifiers {
|
|||||||
|
|
||||||
static bind: o.ExternalReference = {name: 'ɵb', moduleName: CORE};
|
static bind: o.ExternalReference = {name: 'ɵb', moduleName: CORE};
|
||||||
|
|
||||||
|
static getCurrentView: o.ExternalReference = {name: 'ɵgV', moduleName: CORE};
|
||||||
|
|
||||||
|
static restoreView: o.ExternalReference = {name: 'ɵrV', moduleName: CORE};
|
||||||
|
|
||||||
static interpolation1: o.ExternalReference = {name: 'ɵi1', moduleName: CORE};
|
static interpolation1: o.ExternalReference = {name: 'ɵi1', moduleName: CORE};
|
||||||
static interpolation2: o.ExternalReference = {name: 'ɵi2', moduleName: CORE};
|
static interpolation2: o.ExternalReference = {name: 'ɵi2', moduleName: CORE};
|
||||||
static interpolation3: o.ExternalReference = {name: 'ɵi3', moduleName: CORE};
|
static interpolation3: o.ExternalReference = {name: 'ɵi3', moduleName: CORE};
|
||||||
|
@ -56,7 +56,12 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
private _dataIndex = 0;
|
private _dataIndex = 0;
|
||||||
private _bindingContext = 0;
|
private _bindingContext = 0;
|
||||||
private _prefixCode: o.Statement[] = [];
|
private _prefixCode: o.Statement[] = [];
|
||||||
private _creationCode: o.Statement[] = [];
|
/**
|
||||||
|
* List of callbacks to generate creation mode instructions. We store them here as we process
|
||||||
|
* the template so bindings in listeners are resolved only once all nodes have been visited.
|
||||||
|
* This ensures all local refs and context variables are available for matching.
|
||||||
|
*/
|
||||||
|
private _creationCodeFns: (() => o.Statement)[] = [];
|
||||||
/**
|
/**
|
||||||
* List of callbacks to generate update mode instructions. We store them here as we process
|
* List of callbacks to generate update mode instructions. We store them here as we process
|
||||||
* the template so bindings are resolved only once all nodes have been visited. This ensures
|
* the template so bindings are resolved only once all nodes have been visited. This ensures
|
||||||
@ -76,12 +81,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
* This scope contains local variables declared in the update mode block of the template.
|
* This scope contains local variables declared in the update mode block of the template.
|
||||||
* (e.g. refs and context vars in bindings)
|
* (e.g. refs and context vars in bindings)
|
||||||
*/
|
*/
|
||||||
private _updateScope: BindingScope;
|
private _bindingScope: BindingScope;
|
||||||
/**
|
|
||||||
* This scope contains local variables declared in the creation mode block of the template
|
|
||||||
* (e.g. refs and context vars in listeners)
|
|
||||||
*/
|
|
||||||
private _creationScope: BindingScope;
|
|
||||||
private _valueConverter: ValueConverter;
|
private _valueConverter: ValueConverter;
|
||||||
private _unsupported = unsupported;
|
private _unsupported = unsupported;
|
||||||
|
|
||||||
@ -104,9 +104,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
// function)
|
// function)
|
||||||
this._dataIndex = viewQueries.length;
|
this._dataIndex = viewQueries.length;
|
||||||
|
|
||||||
// TODO(kara): generate restore instruction in listener to replace creation scope
|
this._bindingScope = parentBindingScope.nestedScope(level);
|
||||||
this._creationScope = parentBindingScope.nestedScope(level);
|
|
||||||
this._updateScope = parentBindingScope.nestedScope(level);
|
|
||||||
|
|
||||||
this._valueConverter = new ValueConverter(
|
this._valueConverter = new ValueConverter(
|
||||||
constantPool, () => this.allocateDataSlot(),
|
constantPool, () => this.allocateDataSlot(),
|
||||||
@ -116,17 +114,16 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
if (pipeType) {
|
if (pipeType) {
|
||||||
this.pipes.add(pipeType);
|
this.pipes.add(pipeType);
|
||||||
}
|
}
|
||||||
this._updateScope.set(this.level, localName, value);
|
this._bindingScope.set(this.level, localName, value);
|
||||||
this._creationCode.push(
|
this.creationInstruction(null, R3.pipe, [o.literal(slot), o.literal(name)]);
|
||||||
o.importExpr(R3.pipe).callFn([o.literal(slot), o.literal(name)]).toStmt());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerContextVariables(variable: t.Variable, retrievalScope: BindingScope) {
|
registerContextVariables(variable: t.Variable) {
|
||||||
const scopedName = retrievalScope.freshReferenceName();
|
const scopedName = this._bindingScope.freshReferenceName();
|
||||||
const retrievalLevel = this.level;
|
const retrievalLevel = this.level;
|
||||||
const lhs = o.variable(variable.name + scopedName);
|
const lhs = o.variable(variable.name + scopedName);
|
||||||
retrievalScope.set(
|
this._bindingScope.set(
|
||||||
retrievalLevel, variable.name, lhs, DeclarationPriority.CONTEXT,
|
retrievalLevel, variable.name, lhs, DeclarationPriority.CONTEXT,
|
||||||
(scope: BindingScope, relativeLevel: number) => {
|
(scope: BindingScope, relativeLevel: number) => {
|
||||||
let rhs: o.Expression;
|
let rhs: o.Expression;
|
||||||
@ -151,11 +148,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create variable bindings
|
// Create variable bindings
|
||||||
for (const variable of variables) {
|
variables.forEach(v => this.registerContextVariables(v));
|
||||||
// Add the reference to the local scope.
|
|
||||||
this.registerContextVariables(variable, this._creationScope);
|
|
||||||
this.registerContextVariables(variable, this._updateScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output a `ProjectionDef` instruction when some `<ng-content>` are present
|
// Output a `ProjectionDef` instruction when some `<ng-content>` are present
|
||||||
if (hasNgContent) {
|
if (hasNgContent) {
|
||||||
@ -170,34 +163,38 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
parameters.push(parsed, unParsed);
|
parameters.push(parsed, unParsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.creationInstruction(null, R3.projectionDef, ...parameters);
|
this.creationInstruction(null, R3.projectionDef, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the initial pass through the nodes of this template. In this pass, we
|
// This is the initial pass through the nodes of this template. In this pass, we
|
||||||
// generate all creation mode instructions & queue all update mode instructions for
|
// queue all creation mode and update mode instructions for generation in the second
|
||||||
// generation in the second pass. It's necessary to separate the passes to ensure
|
// pass. It's necessary to separate the passes to ensure local refs are defined before
|
||||||
// local refs are defined before resolving bindings.
|
// resolving bindings.
|
||||||
t.visitAll(this, nodes);
|
t.visitAll(this, nodes);
|
||||||
|
|
||||||
// Generate all the update mode instructions as the second pass (e.g. resolve bindings)
|
// Generate all the creation mode instructions (e.g. resolve bindings in listeners)
|
||||||
|
const creationStatements = this._creationCodeFns.map((fn: () => o.Statement) => fn());
|
||||||
|
|
||||||
|
// Generate all the update mode instructions (e.g. resolve property or text bindings)
|
||||||
const updateStatements = this._updateCodeFns.map((fn: () => o.Statement) => fn());
|
const updateStatements = this._updateCodeFns.map((fn: () => o.Statement) => fn());
|
||||||
|
|
||||||
// To count slots for the reserveSlots() instruction, all bindings must have been visited.
|
// To count slots for the reserveSlots() instruction, all bindings must have been visited.
|
||||||
if (this._pureFunctionSlots > 0) {
|
if (this._pureFunctionSlots > 0) {
|
||||||
this.creationInstruction(null, R3.reserveSlots, o.literal(this._pureFunctionSlots));
|
creationStatements.push(
|
||||||
|
instruction(null, R3.reserveSlots, [o.literal(this._pureFunctionSlots)]).toStmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
const creationCode = this._creationCode.length > 0 ?
|
// Variable declaration must occur after binding resolution so we can generate context
|
||||||
|
// instructions that build on each other. e.g. const b = x().$implicit(); const b = x();
|
||||||
|
const creationVariables = this._bindingScope.viewSnapshotStatements();
|
||||||
|
const updateVariables = this._bindingScope.variableDeclarations().concat(this._tempVariables);
|
||||||
|
|
||||||
|
const creationBlock = creationStatements.length > 0 ?
|
||||||
[renderFlagCheckIfStmt(
|
[renderFlagCheckIfStmt(
|
||||||
core.RenderFlags.Create,
|
core.RenderFlags.Create, creationVariables.concat(creationStatements))] :
|
||||||
this._creationScope.variableDeclarations().concat(this._creationCode))] :
|
|
||||||
[];
|
[];
|
||||||
|
|
||||||
// This must occur after binding resolution so we can generate context instructions that
|
const updateBlock = updateStatements.length > 0 ?
|
||||||
// build on each other. e.g. const row = x().$implicit; const table = x().$implicit();
|
|
||||||
const updateVariables = this._updateScope.variableDeclarations().concat(this._tempVariables);
|
|
||||||
|
|
||||||
const updateCode = this._updateCodeFns.length > 0 ?
|
|
||||||
[renderFlagCheckIfStmt(core.RenderFlags.Update, updateVariables.concat(updateStatements))] :
|
[renderFlagCheckIfStmt(core.RenderFlags.Update, updateVariables.concat(updateStatements))] :
|
||||||
[];
|
[];
|
||||||
|
|
||||||
@ -205,7 +202,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
// TODO(vicb): This is a WIP, not fully supported yet
|
// TODO(vicb): This is a WIP, not fully supported yet
|
||||||
for (const phToNodeIdx of this._phToNodeIdxes) {
|
for (const phToNodeIdx of this._phToNodeIdxes) {
|
||||||
if (Object.keys(phToNodeIdx).length > 0) {
|
if (Object.keys(phToNodeIdx).length > 0) {
|
||||||
const scopedName = this._updateScope.freshReferenceName();
|
const scopedName = this._bindingScope.freshReferenceName();
|
||||||
const phMap = o.variable(scopedName).set(mapToExpression(phToNodeIdx, true)).toConstDecl();
|
const phMap = o.variable(scopedName).set(mapToExpression(phToNodeIdx, true)).toConstDecl();
|
||||||
|
|
||||||
this._prefixCode.push(phMap);
|
this._prefixCode.push(phMap);
|
||||||
@ -221,15 +218,15 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
// Temporary variable declarations for query refresh (i.e. let _t: any;)
|
// Temporary variable declarations for query refresh (i.e. let _t: any;)
|
||||||
...this._prefixCode,
|
...this._prefixCode,
|
||||||
// Creating mode (i.e. if (rf & RenderFlags.Create) { ... })
|
// Creating mode (i.e. if (rf & RenderFlags.Create) { ... })
|
||||||
...creationCode,
|
...creationBlock,
|
||||||
// Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...})
|
// Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...})
|
||||||
...updateCode,
|
...updateBlock,
|
||||||
],
|
],
|
||||||
o.INFERRED_TYPE, null, this.templateName);
|
o.INFERRED_TYPE, null, this.templateName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalResolver
|
// LocalResolver
|
||||||
getLocal(name: string): o.Expression|null { return this._updateScope.get(name); }
|
getLocal(name: string): o.Expression|null { return this._bindingScope.get(name); }
|
||||||
|
|
||||||
visitContent(ngContent: t.Content) {
|
visitContent(ngContent: t.Content) {
|
||||||
const slot = this.allocateDataSlot();
|
const slot = this.allocateDataSlot();
|
||||||
@ -251,7 +248,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
parameters.push(o.literal(selectorIndex));
|
parameters.push(o.literal(selectorIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.creationInstruction(ngContent.sourceSpan, R3.projection, ...parameters);
|
this.creationInstruction(ngContent.sourceSpan, R3.projection, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -325,7 +322,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Add the attributes
|
// Add the attributes
|
||||||
const i18nMessages: o.Statement[] = [];
|
|
||||||
const attributes: o.Expression[] = [];
|
const attributes: o.Expression[] = [];
|
||||||
const initialStyleDeclarations: o.Expression[] = [];
|
const initialStyleDeclarations: o.Expression[] = [];
|
||||||
const initialClassDeclarations: o.Expression[] = [];
|
const initialClassDeclarations: o.Expression[] = [];
|
||||||
@ -460,10 +456,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
const references = flatten(element.references.map(reference => {
|
const references = flatten(element.references.map(reference => {
|
||||||
const slot = this.allocateDataSlot();
|
const slot = this.allocateDataSlot();
|
||||||
// Generate the update temporary.
|
// Generate the update temporary.
|
||||||
const variableName = this._updateScope.freshReferenceName();
|
const variableName = this._bindingScope.freshReferenceName();
|
||||||
const retrievalLevel = this.level;
|
const retrievalLevel = this.level;
|
||||||
const lhs = o.variable(variableName);
|
const lhs = o.variable(variableName);
|
||||||
this._updateScope.set(
|
this._bindingScope.set(
|
||||||
retrievalLevel, reference.name, lhs, DeclarationPriority.DEFAULT,
|
retrievalLevel, reference.name, lhs, DeclarationPriority.DEFAULT,
|
||||||
(scope: BindingScope, relativeLevel: number) => {
|
(scope: BindingScope, relativeLevel: number) => {
|
||||||
// e.g. x(2);
|
// e.g. x(2);
|
||||||
@ -481,11 +477,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
parameters.push(o.TYPED_NULL_EXPR);
|
parameters.push(o.TYPED_NULL_EXPR);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the instruction create element instruction
|
|
||||||
if (i18nMessages.length > 0) {
|
|
||||||
this._creationCode.push(...i18nMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wasInNamespace = this._namespace;
|
const wasInNamespace = this._namespace;
|
||||||
const currentNamespace = this.getNamespaceInstruction(namespaceKey);
|
const currentNamespace = this.getNamespaceInstruction(namespaceKey);
|
||||||
|
|
||||||
@ -501,14 +492,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
!hasStylingInstructions && element.children.length === 0 && element.outputs.length === 0;
|
!hasStylingInstructions && element.children.length === 0 && element.outputs.length === 0;
|
||||||
|
|
||||||
if (createSelfClosingInstruction) {
|
if (createSelfClosingInstruction) {
|
||||||
this.creationInstruction(element.sourceSpan, R3.element, ...trimTrailingNulls(parameters));
|
this.creationInstruction(element.sourceSpan, R3.element, trimTrailingNulls(parameters));
|
||||||
} else {
|
} else {
|
||||||
// Generate the instruction create element instruction
|
this.creationInstruction(element.sourceSpan, R3.elementStart, trimTrailingNulls(parameters));
|
||||||
if (i18nMessages.length > 0) {
|
|
||||||
this._creationCode.push(...i18nMessages);
|
|
||||||
}
|
|
||||||
this.creationInstruction(
|
|
||||||
element.sourceSpan, R3.elementStart, ...trimTrailingNulls(parameters));
|
|
||||||
|
|
||||||
// initial styling for static style="..." attributes
|
// initial styling for static style="..." attributes
|
||||||
if (hasStylingInstructions) {
|
if (hasStylingInstructions) {
|
||||||
@ -538,12 +524,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
paramsList.push(o.NULL_EXPR);
|
paramsList.push(o.NULL_EXPR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (useDefaultStyleSanitizer) {
|
if (useDefaultStyleSanitizer) {
|
||||||
paramsList.push(o.importExpr(R3.defaultStyleSanitizer));
|
paramsList.push(o.importExpr(R3.defaultStyleSanitizer));
|
||||||
}
|
}
|
||||||
|
|
||||||
this._creationCode.push(o.importExpr(R3.elementStyling).callFn(paramsList).toStmt());
|
this.creationInstruction(null, R3.elementStyling, paramsList);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate Listeners (outputs)
|
// Generate Listeners (outputs)
|
||||||
@ -551,14 +536,25 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
const elName = sanitizeIdentifier(element.name);
|
const elName = sanitizeIdentifier(element.name);
|
||||||
const evName = sanitizeIdentifier(outputAst.name);
|
const evName = sanitizeIdentifier(outputAst.name);
|
||||||
const functionName = `${this.templateName}_${elName}_${evName}_listener`;
|
const functionName = `${this.templateName}_${elName}_${evName}_listener`;
|
||||||
|
|
||||||
|
this.creationInstruction(outputAst.sourceSpan, R3.listener, () => {
|
||||||
|
const listenerScope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel);
|
||||||
|
|
||||||
const bindingExpr = convertActionBinding(
|
const bindingExpr = convertActionBinding(
|
||||||
this._creationScope, implicit, outputAst.handler, 'b',
|
listenerScope, implicit, outputAst.handler, 'b',
|
||||||
() => error('Unexpected interpolation'));
|
() => error('Unexpected interpolation'));
|
||||||
|
|
||||||
|
const statements = [
|
||||||
|
...listenerScope.restoreViewStatement(), ...listenerScope.variableDeclarations(),
|
||||||
|
...bindingExpr.render3Stmts
|
||||||
|
];
|
||||||
|
|
||||||
const handler = o.fn(
|
const handler = o.fn(
|
||||||
[new o.FnParam('$event', o.DYNAMIC_TYPE)], [...bindingExpr.render3Stmts],
|
[new o.FnParam('$event', o.DYNAMIC_TYPE)], statements, o.INFERRED_TYPE, null,
|
||||||
o.INFERRED_TYPE, null, functionName);
|
functionName);
|
||||||
this.creationInstruction(
|
|
||||||
outputAst.sourceSpan, R3.listener, o.literal(outputAst.name), handler);
|
return [o.literal(outputAst.name), handler];
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,8 +632,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
lastInputCommand = classInputs[classInputs.length - 1];
|
lastInputCommand = classInputs[classInputs.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateInstruction(
|
this.updateInstruction(lastInputCommand !.sourceSpan, R3.elementStylingApply, [indexLiteral]);
|
||||||
lastInputCommand !.sourceSpan, R3.elementStylingApply, () => [indexLiteral]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate element input bindings
|
// Generate element input bindings
|
||||||
@ -725,7 +720,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
|
|
||||||
// e.g. C(1, C1Template)
|
// e.g. C(1, C1Template)
|
||||||
this.creationInstruction(
|
this.creationInstruction(
|
||||||
template.sourceSpan, R3.containerCreate, ...trimTrailingNulls(parameters));
|
template.sourceSpan, R3.containerCreate, trimTrailingNulls(parameters));
|
||||||
|
|
||||||
// e.g. p(1, 'forOf', ɵb(ctx.items));
|
// e.g. p(1, 'forOf', ɵb(ctx.items));
|
||||||
const context = o.variable(CONTEXT_NAME);
|
const context = o.variable(CONTEXT_NAME);
|
||||||
@ -741,7 +736,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
|
|
||||||
// Create the template function
|
// Create the template function
|
||||||
const templateVisitor = new TemplateDefinitionBuilder(
|
const templateVisitor = new TemplateDefinitionBuilder(
|
||||||
this.constantPool, this._updateScope, this.level + 1, contextName, templateName, [],
|
this.constantPool, this._bindingScope, this.level + 1, contextName, templateName, [],
|
||||||
this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes, this._namespace);
|
this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes, this._namespace);
|
||||||
|
|
||||||
// Nested templates must not be visited until after their parent templates have completed
|
// Nested templates must not be visited until after their parent templates have completed
|
||||||
@ -765,7 +760,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
visitBoundText(text: t.BoundText) {
|
visitBoundText(text: t.BoundText) {
|
||||||
const nodeIndex = this.allocateDataSlot();
|
const nodeIndex = this.allocateDataSlot();
|
||||||
|
|
||||||
this.creationInstruction(text.sourceSpan, R3.text, o.literal(nodeIndex));
|
this.creationInstruction(text.sourceSpan, R3.text, [o.literal(nodeIndex)]);
|
||||||
|
|
||||||
const value = text.value.visit(this._valueConverter);
|
const value = text.value.visit(this._valueConverter);
|
||||||
this.updateInstruction(
|
this.updateInstruction(
|
||||||
@ -775,7 +770,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
|
|
||||||
visitText(text: t.Text) {
|
visitText(text: t.Text) {
|
||||||
this.creationInstruction(
|
this.creationInstruction(
|
||||||
text.sourceSpan, R3.text, o.literal(this.allocateDataSlot()), o.literal(text.value));
|
text.sourceSpan, R3.text, [o.literal(this.allocateDataSlot()), o.literal(text.value)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the content of the element is a single text node the translation can be inlined:
|
// When the content of the element is a single text node the translation can be inlined:
|
||||||
@ -794,30 +789,35 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||||||
const meta = parseI18nMeta(i18nMeta);
|
const meta = parseI18nMeta(i18nMeta);
|
||||||
const variable = this.constantPool.getTranslation(text.value, meta);
|
const variable = this.constantPool.getTranslation(text.value, meta);
|
||||||
this.creationInstruction(
|
this.creationInstruction(
|
||||||
text.sourceSpan, R3.text, o.literal(this.allocateDataSlot()), variable);
|
text.sourceSpan, R3.text, [o.literal(this.allocateDataSlot()), variable]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private allocateDataSlot() { return this._dataIndex++; }
|
private allocateDataSlot() { return this._dataIndex++; }
|
||||||
private bindingContext() { return `${this._bindingContext++}`; }
|
private bindingContext() { return `${this._bindingContext++}`; }
|
||||||
|
|
||||||
private instruction(
|
// Bindings must only be resolved after all local refs have been visited, so all
|
||||||
span: ParseSourceSpan|null, reference: o.ExternalReference,
|
|
||||||
params: o.Expression[]): o.Statement {
|
|
||||||
return o.importExpr(reference, null, span).callFn(params, span).toStmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
private creationInstruction(
|
|
||||||
span: ParseSourceSpan|null, reference: o.ExternalReference, ...params: o.Expression[]) {
|
|
||||||
this._creationCode.push(this.instruction(span, reference, params));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bindings must only be resolved after all local refs have been visited, so update mode
|
|
||||||
// instructions are queued in callbacks that execute once the initial pass has completed.
|
// instructions are queued in callbacks that execute once the initial pass has completed.
|
||||||
// Otherwise, we wouldn't be able to support local refs that are defined after their
|
// Otherwise, we wouldn't be able to support local refs that are defined after their
|
||||||
// bindings. e.g. {{ foo }} <div #foo></div>
|
// bindings. e.g. {{ foo }} <div #foo></div>
|
||||||
|
private instructionFn(
|
||||||
|
fns: (() => o.Statement)[], span: ParseSourceSpan|null, reference: o.ExternalReference,
|
||||||
|
paramsOrFn: o.Expression[]|(() => o.Expression[])): void {
|
||||||
|
fns.push(() => {
|
||||||
|
const params = Array.isArray(paramsOrFn) ? paramsOrFn : paramsOrFn();
|
||||||
|
return instruction(span, reference, params).toStmt();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private creationInstruction(
|
||||||
|
span: ParseSourceSpan|null, reference: o.ExternalReference,
|
||||||
|
paramsOrFn?: o.Expression[]|(() => o.Expression[])) {
|
||||||
|
this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || []);
|
||||||
|
}
|
||||||
|
|
||||||
private updateInstruction(
|
private updateInstruction(
|
||||||
span: ParseSourceSpan|null, reference: o.ExternalReference, paramsFn: () => o.Expression[]) {
|
span: ParseSourceSpan|null, reference: o.ExternalReference,
|
||||||
this._updateCodeFns.push(() => { return this.instruction(span, reference, paramsFn()); });
|
paramsOrFn?: o.Expression[]|(() => o.Expression[])) {
|
||||||
|
this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertPropertyBinding(implicit: o.Expression, value: AST, skipBindFn?: boolean):
|
private convertPropertyBinding(implicit: o.Expression, value: AST, skipBindFn?: boolean):
|
||||||
@ -915,6 +915,12 @@ function pureFunctionCallInfo(args: o.Expression[]) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function instruction(
|
||||||
|
span: ParseSourceSpan | null, reference: o.ExternalReference,
|
||||||
|
params: o.Expression[]): o.Expression {
|
||||||
|
return o.importExpr(reference, null, span).callFn(params, span);
|
||||||
|
}
|
||||||
|
|
||||||
// e.g. x(2);
|
// e.g. x(2);
|
||||||
function generateNextContextExpr(relativeLevelDiff: number): o.Expression {
|
function generateNextContextExpr(relativeLevelDiff: number): o.Expression {
|
||||||
return o.importExpr(R3.nextContext)
|
return o.importExpr(R3.nextContext)
|
||||||
@ -986,8 +992,9 @@ export class BindingScope implements LocalResolver {
|
|||||||
/** Keeps a map from local variables to their BindingData. */
|
/** Keeps a map from local variables to their BindingData. */
|
||||||
private map = new Map<string, BindingData>();
|
private map = new Map<string, BindingData>();
|
||||||
private referenceNameIndex = 0;
|
private referenceNameIndex = 0;
|
||||||
|
private restoreViewVariable: o.ReadVarExpr|null = null;
|
||||||
|
|
||||||
static ROOT_SCOPE = new BindingScope().set(-1, '$event', o.variable('$event'));
|
static ROOT_SCOPE = new BindingScope().set(0, '$event', o.variable('$event'));
|
||||||
|
|
||||||
private constructor(public bindingLevel: number = 0, private parent: BindingScope|null = null) {}
|
private constructor(public bindingLevel: number = 0, private parent: BindingScope|null = null) {}
|
||||||
|
|
||||||
@ -1010,6 +1017,7 @@ export class BindingScope implements LocalResolver {
|
|||||||
this.map.set(name, value);
|
this.map.set(name, value);
|
||||||
// Possibly generate a shared context var
|
// Possibly generate a shared context var
|
||||||
this.maybeGenerateSharedContextVar(value);
|
this.maybeGenerateSharedContextVar(value);
|
||||||
|
this.maybeRestoreView(value.retrievalLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.declareLocalCallback && !value.declare) {
|
if (value.declareLocalCallback && !value.declare) {
|
||||||
@ -1092,9 +1100,37 @@ export class BindingScope implements LocalResolver {
|
|||||||
getComponentProperty(name: string): o.Expression {
|
getComponentProperty(name: string): o.Expression {
|
||||||
const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0) !;
|
const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0) !;
|
||||||
componentValue.declare = true;
|
componentValue.declare = true;
|
||||||
|
this.maybeRestoreView(0);
|
||||||
return componentValue.lhs.prop(name);
|
return componentValue.lhs.prop(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybeRestoreView(retrievalLevel: number) {
|
||||||
|
if (this.isListenerScope() && retrievalLevel < this.bindingLevel) {
|
||||||
|
if (!this.parent !.restoreViewVariable) {
|
||||||
|
// parent saves variable to generate a shared `const $s$ = gV();` instruction
|
||||||
|
this.parent !.restoreViewVariable = o.variable(this.parent !.freshReferenceName());
|
||||||
|
}
|
||||||
|
this.restoreViewVariable = this.parent !.restoreViewVariable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreViewStatement(): o.Statement[] {
|
||||||
|
// rV($state$);
|
||||||
|
return this.restoreViewVariable ?
|
||||||
|
[instruction(null, R3.restoreView, [this.restoreViewVariable]).toStmt()] :
|
||||||
|
[];
|
||||||
|
}
|
||||||
|
|
||||||
|
viewSnapshotStatements(): o.Statement[] {
|
||||||
|
// const $state$ = gV();
|
||||||
|
const getCurrentViewInstruction = instruction(null, R3.getCurrentView, []);
|
||||||
|
return this.restoreViewVariable ?
|
||||||
|
[this.restoreViewVariable.set(getCurrentViewInstruction).toConstDecl()] :
|
||||||
|
[];
|
||||||
|
}
|
||||||
|
|
||||||
|
isListenerScope() { return this.parent && this.parent.bindingLevel === this.bindingLevel; }
|
||||||
|
|
||||||
variableDeclarations(): o.Statement[] {
|
variableDeclarations(): o.Statement[] {
|
||||||
let currentContextLevel = 0;
|
let currentContextLevel = 0;
|
||||||
return Array.from(this.map.values())
|
return Array.from(this.map.values())
|
||||||
|
@ -70,6 +70,8 @@ export {
|
|||||||
f7 as ɵf7,
|
f7 as ɵf7,
|
||||||
f8 as ɵf8,
|
f8 as ɵf8,
|
||||||
fV as ɵfV,
|
fV as ɵfV,
|
||||||
|
gV as ɵgV,
|
||||||
|
rV as ɵrV,
|
||||||
cR as ɵcR,
|
cR as ɵcR,
|
||||||
cr as ɵcr,
|
cr as ɵcr,
|
||||||
qR as ɵqR,
|
qR as ɵqR,
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {assertEqual, assertLessThan} from './assert';
|
import {assertEqual, assertLessThan} from './assert';
|
||||||
import {NO_CHANGE, bindingUpdated, bindingUpdated2, bindingUpdated4, createLNode, getPreviousOrParentNode, getRenderer, getViewData, load, resetApplicationState} from './instructions';
|
import {NO_CHANGE, _getViewData, bindingUpdated, bindingUpdated2, bindingUpdated4, createLNode, getPreviousOrParentNode, getRenderer, load, resetApplicationState} from './instructions';
|
||||||
import {RENDER_PARENT} from './interfaces/container';
|
import {RENDER_PARENT} from './interfaces/container';
|
||||||
import {LContainerNode, LNode, TContainerNode, TElementNode, TNodeType} from './interfaces/node';
|
import {LContainerNode, LNode, TContainerNode, TElementNode, TNodeType} from './interfaces/node';
|
||||||
import {BINDING_INDEX, HEADER_OFFSET, TVIEW} from './interfaces/view';
|
import {BINDING_INDEX, HEADER_OFFSET, TVIEW} from './interfaces/view';
|
||||||
@ -250,7 +250,7 @@ function appendI18nNode(node: LNode, parentNode: LNode, previousNode: LNode) {
|
|||||||
ngDevMode.rendererMoveNode++;
|
ngDevMode.rendererMoveNode++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewData = getViewData();
|
const viewData = _getViewData();
|
||||||
|
|
||||||
appendChild(parentNode, node.native || null, viewData);
|
appendChild(parentNode, node.native || null, viewData);
|
||||||
|
|
||||||
@ -291,7 +291,7 @@ function appendI18nNode(node: LNode, parentNode: LNode, previousNode: LNode) {
|
|||||||
* @param instructions The list of instructions to apply on the current view.
|
* @param instructions The list of instructions to apply on the current view.
|
||||||
*/
|
*/
|
||||||
export function i18nApply(startIndex: number, instructions: I18nInstruction[]): void {
|
export function i18nApply(startIndex: number, instructions: I18nInstruction[]): void {
|
||||||
const viewData = getViewData();
|
const viewData = _getViewData();
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
assertEqual(viewData[BINDING_INDEX], -1, 'i18nApply should be called before any binding');
|
assertEqual(viewData[BINDING_INDEX], -1, 'i18nApply should be called before any binding');
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,9 @@ export {
|
|||||||
elementStyleProp as sp,
|
elementStyleProp as sp,
|
||||||
elementStylingApply as sa,
|
elementStylingApply as sa,
|
||||||
|
|
||||||
|
getCurrentView as gV,
|
||||||
|
restoreView as rV,
|
||||||
|
|
||||||
listener as L,
|
listener as L,
|
||||||
store as st,
|
store as st,
|
||||||
load as ld,
|
load as ld,
|
||||||
|
@ -22,7 +22,7 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme
|
|||||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||||
import {LQueries} from './interfaces/query';
|
import {LQueries} from './interfaces/query';
|
||||||
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
import {ProceduralRenderer3, RComment, RElement, RText, Renderer3, RendererFactory3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
||||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
|
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TData, TVIEW, TView} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
import {appendChild, appendProjectedNode, canInsertNativeNode, createTextNode, findComponentHost, getChildLNode, getLViewChild, getNextLNode, getParentLNode, insertView, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||||
@ -108,11 +108,40 @@ export function getCurrentSanitizer(): Sanitizer|null {
|
|||||||
return viewData && viewData[SANITIZER];
|
return viewData && viewData[SANITIZER];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getViewData(): LViewData {
|
/**
|
||||||
|
* Returns the current OpaqueViewState instance.
|
||||||
|
*
|
||||||
|
* Used in conjunction with the restoreView() instruction to save a snapshot
|
||||||
|
* of the current view and restore it when listeners are invoked. This allows
|
||||||
|
* walking the declaration view tree in listeners to get vars from parent views.
|
||||||
|
*/
|
||||||
|
export function getCurrentView(): OpaqueViewState {
|
||||||
|
return (viewData as any) as OpaqueViewState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function that returns the current LViewData instance.
|
||||||
|
*
|
||||||
|
* The getCurrentView() instruction should be used for anything public.
|
||||||
|
*/
|
||||||
|
export function _getViewData(): LViewData {
|
||||||
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
// top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
||||||
return viewData;
|
return viewData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores `contextViewData` to the given OpaqueViewState instance.
|
||||||
|
*
|
||||||
|
* Used in conjunction with the getCurrentView() instruction to save a snapshot
|
||||||
|
* of the current view and restore it when listeners are invoked. This allows
|
||||||
|
* walking the declaration view tree in listeners to get vars from parent views.
|
||||||
|
*
|
||||||
|
* @param viewToRestore The LViewData instance to restore.
|
||||||
|
*/
|
||||||
|
export function restoreView(viewToRestore: OpaqueViewState) {
|
||||||
|
contextViewData = (viewToRestore as any) as LViewData;
|
||||||
|
}
|
||||||
|
|
||||||
/** Used to set the parent property when nodes are created. */
|
/** Used to set the parent property when nodes are created. */
|
||||||
let previousOrParentNode: LNode;
|
let previousOrParentNode: LNode;
|
||||||
|
|
||||||
@ -583,9 +612,9 @@ export function renderEmbeddedTemplate<T>(
|
|||||||
* @param level The relative level of the view from which to grab context compared to contextVewData
|
* @param level The relative level of the view from which to grab context compared to contextVewData
|
||||||
* @returns context
|
* @returns context
|
||||||
*/
|
*/
|
||||||
export function nextContext(level: number = 1): any {
|
export function nextContext<T = any>(level: number = 1): T {
|
||||||
contextViewData = walkUpViews(level, contextViewData !);
|
contextViewData = walkUpViews(level, contextViewData !);
|
||||||
return contextViewData[CONTEXT];
|
return contextViewData[CONTEXT] as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderComponentOrTemplate<T>(
|
export function renderComponentOrTemplate<T>(
|
||||||
|
@ -40,6 +40,14 @@ export const CONTAINER_INDEX = 14;
|
|||||||
export const CONTENT_QUERIES = 15;
|
export const CONTENT_QUERIES = 15;
|
||||||
export const DECLARATION_VIEW = 16;
|
export const DECLARATION_VIEW = 16;
|
||||||
|
|
||||||
|
// This interface replaces the real LViewData interface if it is an arg or a
|
||||||
|
// return value of a public instruction. This ensures we don't need to expose
|
||||||
|
// the actual interface, which should be kept private.
|
||||||
|
export interface OpaqueViewState {
|
||||||
|
'__brand__': 'Brand for OpaqueViewState that nothing will match';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `LViewData` stores all of the information needed to process the instructions as
|
* `LViewData` stores all of the information needed to process the instructions as
|
||||||
* they are invoked from the template. Each embedded view and component view has its
|
* they are invoked from the template. Each embedded view and component view has its
|
||||||
|
@ -57,6 +57,8 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
|||||||
'ɵf7': r3.f7,
|
'ɵf7': r3.f7,
|
||||||
'ɵf8': r3.f8,
|
'ɵf8': r3.f8,
|
||||||
'ɵfV': r3.fV,
|
'ɵfV': r3.fV,
|
||||||
|
'ɵgV': r3.gV,
|
||||||
|
'ɵrV': r3.rV,
|
||||||
'ɵi1': r3.i1,
|
'ɵi1': r3.i1,
|
||||||
'ɵi2': r3.i2,
|
'ɵi2': r3.i2,
|
||||||
'ɵi3': r3.i3,
|
'ɵi3': r3.i3,
|
||||||
|
@ -521,6 +521,9 @@
|
|||||||
{
|
{
|
||||||
"name": "getCurrentSanitizer"
|
"name": "getCurrentSanitizer"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getCurrentView"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getInitialIndex"
|
"name": "getInitialIndex"
|
||||||
},
|
},
|
||||||
@ -761,6 +764,9 @@
|
|||||||
{
|
{
|
||||||
"name": "readElementValue"
|
"name": "readElementValue"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "reference"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "refreshChildComponents"
|
"name": "refreshChildComponents"
|
||||||
},
|
},
|
||||||
@ -800,6 +806,9 @@
|
|||||||
{
|
{
|
||||||
"name": "resolveRendererType2"
|
"name": "resolveRendererType2"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "restoreView"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "sameHostView"
|
"name": "sameHostView"
|
||||||
},
|
},
|
||||||
|
@ -10,7 +10,7 @@ import {NgForOfContext} from '@angular/common';
|
|||||||
|
|
||||||
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
|
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
|
||||||
import {AttributeMarker, defineComponent} from '../../src/render3/index';
|
import {AttributeMarker, defineComponent} from '../../src/render3/index';
|
||||||
import {bind, container, elementEnd, elementProperty, elementStart, interpolation1, interpolation2, interpolation3, interpolationV, listener, load, nextContext, text, textBinding} from '../../src/render3/instructions';
|
import {bind, container, elementEnd, elementProperty, elementStart, getCurrentView, interpolation1, interpolation2, interpolation3, interpolationV, listener, load, nextContext, restoreView, text, textBinding} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
|
||||||
import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def';
|
import {NgForOf, NgIf, NgTemplateOutlet} from './common_with_def';
|
||||||
@ -337,13 +337,17 @@ describe('@angular/common integration', () => {
|
|||||||
|
|
||||||
function pTemplate(rf: RenderFlags, ctx: any) {
|
function pTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
const row = nextContext().$implicit as any;
|
const state = getCurrentView();
|
||||||
const app = nextContext();
|
|
||||||
elementStart(0, 'p');
|
elementStart(0, 'p');
|
||||||
{
|
{
|
||||||
elementStart(1, 'span');
|
elementStart(1, 'span');
|
||||||
{
|
{
|
||||||
listener('click', () => { app.onClick(row.value, app.name); });
|
listener('click', () => {
|
||||||
|
restoreView(state);
|
||||||
|
const row = nextContext().$implicit as any;
|
||||||
|
const app = nextContext();
|
||||||
|
app.onClick(row.value, app.name);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
elementEnd();
|
elementEnd();
|
||||||
text(2);
|
text(2);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user