fix(ivy): convert context code into a tree-shakable instruction (#24943)
PR Close #24943
This commit is contained in:
parent
fe14f180a6
commit
2ef777b0b2
|
@ -1 +1,3 @@
|
||||||
Tests in this directory are excluded from running in the browser and only run in node.
|
Tests in this directory should be run with:
|
||||||
|
|
||||||
|
bazel test --define=compile=local packages/compiler-cli/test/compliance:compliance
|
||||||
|
|
|
@ -541,15 +541,16 @@ describe('compiler compliance', () => {
|
||||||
const MyComponentDefinition = `
|
const MyComponentDefinition = `
|
||||||
const $c1$ = ["foo", ""];
|
const $c1$ = ["foo", ""];
|
||||||
const $c2$ = ["if", ""];
|
const $c2$ = ["if", ""];
|
||||||
function MyComponent_li_Template_2(rf, ctx0, ctx) {
|
function MyComponent_li_Template_2(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵE(0, "li");
|
$r3$.ɵE(0, "li");
|
||||||
$r3$.ɵT(1);
|
$r3$.ɵT(1);
|
||||||
$r3$.ɵe();
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $foo$ = $r3$.ɵr(1, 1);
|
const $myComp$ = $r3$.ɵx();
|
||||||
$r3$.ɵt(1, $r3$.ɵi2("", ctx.salutation, " ", $foo$, ""));
|
const $foo$ = $r3$.ɵr(1);
|
||||||
|
$r3$.ɵt(1, $r3$.ɵi2("", $myComp$.salutation, " ", $foo$, ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
|
@ -1174,7 +1175,7 @@ describe('compiler compliance', () => {
|
||||||
$r3$.ɵT(2);
|
$r3$.ɵT(2);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $user$ = $r3$.ɵld(1);
|
const $user$ = $r3$.ɵr(1);
|
||||||
$r3$.ɵt(2, $r3$.ɵi1("Hello ", $user$.value, "!"));
|
$r3$.ɵt(2, $r3$.ɵi1("Hello ", $user$.value, "!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1224,20 +1225,22 @@ describe('compiler compliance', () => {
|
||||||
const $c2$ = ["if", ""];
|
const $c2$ = ["if", ""];
|
||||||
const $c3$ = ["baz", ""];
|
const $c3$ = ["baz", ""];
|
||||||
const $c4$ = ["bar", ""];
|
const $c4$ = ["bar", ""];
|
||||||
function MyComponent_div_span_Template_2(rf, ctx1, ctx0, ctx) {
|
function MyComponent_div_span_Template_2(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵE(0, "span");
|
$r3$.ɵE(0, "span");
|
||||||
$r3$.ɵT(1);
|
$r3$.ɵT(1);
|
||||||
$r3$.ɵe();
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $foo$ = $r3$.ɵr(2, 1);
|
$r3$.ɵx();
|
||||||
const $bar$ = $r3$.ɵr(1, 4);
|
const $bar$ = $r3$.ɵr(4);
|
||||||
const $baz$ = $r3$.ɵr(2, 5);
|
$r3$.ɵx();
|
||||||
|
const $foo$ = $r3$.ɵr(1);
|
||||||
|
const $baz$ = $r3$.ɵr(5);
|
||||||
$r3$.ɵt(1, $r3$.ɵi3("", $foo$, "-", $bar$, "-", $baz$, ""));
|
$r3$.ɵt(1, $r3$.ɵi3("", $foo$, "-", $bar$, "-", $baz$, ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function MyComponent_div_Template_3(rf, ctx0, ctx) {
|
function MyComponent_div_Template_3(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵE(0, "div");
|
$r3$.ɵE(0, "div");
|
||||||
$r3$.ɵT(1);
|
$r3$.ɵT(1);
|
||||||
|
@ -1246,8 +1249,9 @@ describe('compiler compliance', () => {
|
||||||
$r3$.ɵe();
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $foo$ = $r3$.ɵr(1, 1);
|
const $bar$ = $r3$.ɵr(4);
|
||||||
const $bar$ = $r3$.ɵld(4);
|
$r3$.ɵx();
|
||||||
|
const $foo$ = $r3$.ɵr(1);
|
||||||
$r3$.ɵt(1, $r3$.ɵi2(" ", $foo$, "-", $bar$, " "));
|
$r3$.ɵt(1, $r3$.ɵi2(" ", $foo$, "-", $bar$, " "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1264,7 +1268,7 @@ describe('compiler compliance', () => {
|
||||||
$r3$.ɵEe(4, "div", null, $c3$);
|
$r3$.ɵEe(4, "div", null, $c3$);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $foo$ = $r3$.ɵld(1);
|
const $foo$ = $r3$.ɵr(1);
|
||||||
$r3$.ɵt(2, $r3$.ɵi1(" ", $foo$, " "));
|
$r3$.ɵt(2, $r3$.ɵi1(" ", $foo$, " "));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1278,6 +1282,77 @@ describe('compiler compliance', () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support local refs mixed with context assignments', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`
|
||||||
|
<div *ngFor="let item of items">
|
||||||
|
<div #foo></div>
|
||||||
|
<span *ngIf="showing">
|
||||||
|
{{ foo }} - {{ item }}
|
||||||
|
</span>
|
||||||
|
</div>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent], imports: [CommonModule]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
const $c0$ = ["ngFor","","ngForOf",""];
|
||||||
|
const $c1$ = ["foo", ""];
|
||||||
|
const $c2$ = ["ngIf",""];
|
||||||
|
|
||||||
|
function MyComponent_div_span_Template_3(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵE(0, "span");
|
||||||
|
$i0$.ɵT(1);
|
||||||
|
$i0$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
const $item$ = $i0$.ɵx().$implicit;
|
||||||
|
const $foo$ = $i0$.ɵr(2);
|
||||||
|
$i0$.ɵt(1, $i0$.ɵi2(" ", $foo$, " - ", $item$, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function MyComponent_div_Template_0(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵE(0, "div");
|
||||||
|
$i0$.ɵEe(1, "div", null, $c1$);
|
||||||
|
$i0$.ɵC(3, MyComponent_div_span_Template_3, null, $c2$);
|
||||||
|
$i0$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
const $app$ = $i0$.ɵx();
|
||||||
|
$i0$.ɵp(3, "ngIf", $i0$.ɵb($app$.showing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
template:function MyComponent_Template(rf, ctx){
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵC(0, MyComponent_div_Template_0, null, $c0$);
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
$i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
describe('lifecycle hooks', () => {
|
describe('lifecycle hooks', () => {
|
||||||
const files = {
|
const files = {
|
||||||
app: {
|
app: {
|
||||||
|
@ -1452,7 +1527,7 @@ describe('compiler compliance', () => {
|
||||||
|
|
||||||
const MyComponentDefinition = `
|
const MyComponentDefinition = `
|
||||||
const $_c0$ = ["for","","forOf",""];
|
const $_c0$ = ["for","","forOf",""];
|
||||||
function MyComponent__svg_g_Template_1(rf, ctx0, ctx) {
|
function MyComponent__svg_g_Template_1(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵNS();
|
$r3$.ɵNS();
|
||||||
$r3$.ɵE(0,"g");
|
$r3$.ɵE(0,"g");
|
||||||
|
@ -1525,14 +1600,14 @@ describe('compiler compliance', () => {
|
||||||
|
|
||||||
const MyComponentDefinition = `
|
const MyComponentDefinition = `
|
||||||
const $_c0$ = ["for","","forOf",""];
|
const $_c0$ = ["for","","forOf",""];
|
||||||
function MyComponent_li_Template_1(rf, ctx0, ctx) {
|
function MyComponent_li_Template_1(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵE(0, "li");
|
$r3$.ɵE(0, "li");
|
||||||
$r3$.ɵT(1);
|
$r3$.ɵT(1);
|
||||||
$r3$.ɵe();
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $item$ = ctx0.$implicit;
|
const $item$ = ctx.$implicit;
|
||||||
$r3$.ɵt(1, $r3$.ɵi1("", $item$.name, ""));
|
$r3$.ɵt(1, $r3$.ɵi1("", $item$.name, ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1602,20 +1677,20 @@ describe('compiler compliance', () => {
|
||||||
|
|
||||||
const MyComponentDefinition = `
|
const MyComponentDefinition = `
|
||||||
const $c1$ = ["for", "", "forOf", ""];
|
const $c1$ = ["for", "", "forOf", ""];
|
||||||
function MyComponent_li_li_Template_4(rf, ctx1, ctx0, ctx) {
|
function MyComponent_li_li_Template_4(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵE(0, "li");
|
$r3$.ɵE(0, "li");
|
||||||
$r3$.ɵT(1);
|
$r3$.ɵT(1);
|
||||||
$r3$.ɵe();
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $item$ = ctx0.$implicit;
|
const $info$ = ctx.$implicit;
|
||||||
const $info$ = ctx1.$implicit;
|
const $item$ = $r3$.ɵx().$implicit;
|
||||||
$r3$.ɵt(1, $r3$.ɵi2(" ", $item$.name, ": ", $info$.description, " "));
|
$r3$.ɵt(1, $r3$.ɵi2(" ", $item$.name, ": ", $info$.description, " "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function MyComponent_li_Template_1(rf, ctx0, ctx) {
|
function MyComponent_li_Template_1(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵE(0, "li");
|
$r3$.ɵE(0, "li");
|
||||||
$r3$.ɵE(1, "div");
|
$r3$.ɵE(1, "div");
|
||||||
|
@ -1627,7 +1702,7 @@ describe('compiler compliance', () => {
|
||||||
$r3$.ɵe();
|
$r3$.ɵe();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $item$ = ctx0.$implicit;
|
const $item$ = ctx.$implicit;
|
||||||
$r3$.ɵt(2, $r3$.ɵi1("", IDENT.name, ""));
|
$r3$.ɵt(2, $r3$.ɵi1("", IDENT.name, ""));
|
||||||
$r3$.ɵp(4, "forOf", $r3$.ɵb(IDENT.infos));
|
$r3$.ɵp(4, "forOf", $r3$.ɵb(IDENT.infos));
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,56 +52,262 @@ describe('compiler compliance: template', () => {
|
||||||
// The template should look like this (where IDENT is a wild card for an identifier):
|
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||||
const template = `
|
const template = `
|
||||||
const $c0$ = ["ngFor","","ngForOf",""];
|
const $c0$ = ["ngFor","","ngForOf",""];
|
||||||
function MyComponent_ul_li_div_Template_1(rf, $ctx2$, $ctx1$, $ctx0$, $ctx$) {
|
function MyComponent_ul_li_div_Template_1(rf, ctx) {
|
||||||
|
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
|
const $inner$ = ctx.$implicit;
|
||||||
|
const $middle$ = $i0$.ɵx().$implicit;
|
||||||
|
const $outer$ = $i0$.ɵx().$implicit;
|
||||||
|
const $myComp$ = $i0$.ɵx();
|
||||||
$i0$.ɵE(0, "div");
|
$i0$.ɵE(0, "div");
|
||||||
$i0$.ɵL("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
|
$i0$.ɵL("click", function MyComponent_ul_li_div_Template_1_div_click_listener($event){
|
||||||
const $outer$ = $ctx0$.$implicit;
|
return $myComp$.onClick($outer$, $middle$, $inner$);
|
||||||
const $middle$ = $ctx1$.$implicit;
|
|
||||||
const $inner$ = $ctx2$.$implicit;
|
|
||||||
return ctx.onClick($outer$, $middle$, $inner$);
|
|
||||||
});
|
});
|
||||||
$i0$.ɵT(1);
|
$i0$.ɵT(1);
|
||||||
$i0$.ɵe();
|
$i0$.ɵe();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $outer$ = $ctx0$.$implicit;
|
const $inner1$ = ctx.$implicit;
|
||||||
const $middle$ = $ctx1$.$implicit;
|
const $middle1$ = $i0$.ɵx().$implicit;
|
||||||
const $inner$ = $ctx2$.$implicit;
|
const $outer1$ = $i0$.ɵx().$implicit;
|
||||||
$i0$.ɵp(0, "title", $i0$.ɵb(ctx.format($outer$, $middle$, $inner$, $ctx$.component)));
|
const $myComp1$ = $i0$.ɵx();
|
||||||
$i0$.ɵt(1, $i0$.ɵi1(" ", ctx.format($outer$, $middle$, $inner$, $ctx$.component), " "));
|
$i0$.ɵp(0, "title", $i0$.ɵb($myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component)));
|
||||||
|
$i0$.ɵt(1, $i0$.ɵi1(" ", $myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component), " "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function MyComponent_ul_li_Template_1(rf, $ctx1$, $ctx0$, $ctx$) {
|
function MyComponent_ul_li_Template_1(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$i0$.ɵE(0, "li");
|
$i0$.ɵE(0, "li");
|
||||||
$i0$.ɵC(1, MyComponent_ul_li_div_Template_1, null, _c0);
|
$i0$.ɵC(1, MyComponent_ul_li_div_Template_1, null, _c0);
|
||||||
$i0$.ɵe();
|
$i0$.ɵe();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$i0$.ɵp(1, "ngForOf", $i0$.ɵb($ctx$.items));
|
const $myComp2$ = $i0$.ɵx(2);
|
||||||
|
$i0$.ɵp(1, "ngForOf", $i0$.ɵb($myComp2$.items));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function MyComponent_ul_Template_0(rf, $ctx0$, $ctx$) {
|
function MyComponent_ul_Template_0(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$i0$.ɵE(0, "ul");
|
$i0$.ɵE(0, "ul");
|
||||||
$i0$.ɵC(1, MyComponent_ul_li_Template_1, null, _c0);
|
$i0$.ɵC(1, MyComponent_ul_li_Template_1, null, _c0);
|
||||||
$i0$.ɵe();
|
$i0$.ɵe();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
const $outer$ = $ctx0$.$implicit;
|
const $outer2$ = ctx.$implicit;
|
||||||
$i0$.ɵp(1, "ngForOf", $i0$.ɵb($outer$.items));
|
$i0$.ɵp(1, "ngForOf", $i0$.ɵb($outer2$.items));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ...
|
// ...
|
||||||
template:function MyComponent_Template(rf, $ctx$){
|
template:function MyComponent_Template(rf, ctx){
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$i0$.ɵC(0, MyComponent_ul_Template_0, null, _c0);
|
$i0$.ɵC(0, MyComponent_ul_Template_0, null, _c0);
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$i0$.ɵp(0, "ngForOf", $i0$.ɵb($ctx$.items));
|
$i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support ngFor context variables', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`
|
||||||
|
<span *ngFor="let item of items; index as i">
|
||||||
|
{{ i }} - {{ item }}
|
||||||
|
</span>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent], imports: [CommonModule]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
const $c0$ = ["ngFor","","ngForOf",""];
|
||||||
|
|
||||||
|
function MyComponent_span_Template_0(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵE(0, "span");
|
||||||
|
$i0$.ɵT(1);
|
||||||
|
$i0$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
const $item$ = ctx.$implicit;
|
||||||
|
const $i$ = ctx.index;
|
||||||
|
$i0$.ɵt(1, $i0$.ɵi2(" ", $i$, " - ", $item$, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
template:function MyComponent_Template(rf, ctx){
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵC(0, MyComponent_span_Template_0, null, _c0);
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
$i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support ngFor context variables in parent views', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`
|
||||||
|
<div *ngFor="let item of items; index as i">
|
||||||
|
<span *ngIf="showing">
|
||||||
|
{{ i }} - {{ item }}
|
||||||
|
</span>
|
||||||
|
</div>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent], imports: [CommonModule]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const template = `
|
||||||
|
const $c0$ = ["ngFor","","ngForOf",""];
|
||||||
|
const $c1$ = ["ngIf",""];
|
||||||
|
|
||||||
|
function MyComponent_div_span_Template_1(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵE(0, "span");
|
||||||
|
$i0$.ɵT(1);
|
||||||
|
$i0$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
const $div$ = $i0$.ɵx();
|
||||||
|
const $i$ = $div$.index;
|
||||||
|
const $item$ = $div$.$implicit;
|
||||||
|
$i0$.ɵt(1, $i0$.ɵi2(" ", $i$, " - ", $item$, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function MyComponent_div_Template_0(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵE(0, "div");
|
||||||
|
$i0$.ɵC(1, MyComponent_div_span_Template_1, null, $c1$);
|
||||||
|
$i0$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
const $app$ = $i0$.ɵx();
|
||||||
|
$i0$.ɵp(1, "ngIf", $i0$.ɵb($app$.showing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
template:function MyComponent_Template(rf, ctx){
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵC(0, MyComponent_div_Template_0, null, $c0$);
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
$i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const result = compile(files, angularFiles);
|
||||||
|
|
||||||
|
expectEmit(result.source, template, 'Incorrect template');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly skip contexts as needed', () => {
|
||||||
|
const files = {
|
||||||
|
app: {
|
||||||
|
'spec.ts': `
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-component',
|
||||||
|
template: \`
|
||||||
|
<div *ngFor="let outer of items">
|
||||||
|
<div *ngFor="let middle of outer.items">
|
||||||
|
<div *ngFor="let inner of middle.items">
|
||||||
|
{{ middle.value }} - {{ name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>\`
|
||||||
|
})
|
||||||
|
export class MyComponent {}
|
||||||
|
|
||||||
|
@NgModule({declarations: [MyComponent], imports: [CommonModule]})
|
||||||
|
export class MyModule {}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The template should look like this (where IDENT is a wild card for an identifier):
|
||||||
|
const template = `
|
||||||
|
const $c0$ = ["ngFor","","ngForOf",""];
|
||||||
|
function MyComponent_div_div_div_Template_1(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵE(0, "div");
|
||||||
|
$i0$.ɵT(1);
|
||||||
|
$i0$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
const $middle$ = $i0$.ɵx().$implicit;
|
||||||
|
const $myComp$ = $i0$.ɵx(2);
|
||||||
|
$i0$.ɵt(1, $i0$.ɵi2(" ", $middle$.value, " - ", $myComp$.name, " "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function MyComponent_div_div_Template_1(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵE(0, "div");
|
||||||
|
$i0$.ɵC(1, MyComponent_div_div_div_Template_1, null, _c0);
|
||||||
|
$i0$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
const $middle$ = ctx.$implicit;
|
||||||
|
$i0$.ɵp(1, "ngForOf", $i0$.ɵb($middle$.items));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function MyComponent_div_Template_0(rf, ctx) {
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵE(0, "div");
|
||||||
|
$i0$.ɵC(1, MyComponent_div_div_Template_1, null, _c0);
|
||||||
|
$i0$.ɵe();
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
const $outer$ = ctx.$implicit;
|
||||||
|
$i0$.ɵp(1, "ngForOf", $i0$.ɵb($outer$.items));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ...
|
||||||
|
template:function MyComponent_Template(rf, ctx){
|
||||||
|
if (rf & 1) {
|
||||||
|
$i0$.ɵC(0, MyComponent_div_Template_0, null, _c0);
|
||||||
|
}
|
||||||
|
if (rf & 2) {
|
||||||
|
$i0$.ɵp(0, "ngForOf", $i0$.ɵb(ctx.items));
|
||||||
}
|
}
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
|
|
@ -338,6 +338,8 @@ export class WriteVarExpr extends Expression {
|
||||||
toDeclStmt(type?: Type|null, modifiers?: StmtModifier[]|null): DeclareVarStmt {
|
toDeclStmt(type?: Type|null, modifiers?: StmtModifier[]|null): DeclareVarStmt {
|
||||||
return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
|
return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toConstDecl(): DeclareVarStmt { return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@ export class Identifiers {
|
||||||
|
|
||||||
static containerCreate: o.ExternalReference = {name: 'ɵC', moduleName: CORE};
|
static containerCreate: o.ExternalReference = {name: 'ɵC', moduleName: CORE};
|
||||||
|
|
||||||
|
static nextContext: o.ExternalReference = {name: 'ɵx', moduleName: CORE};
|
||||||
|
|
||||||
static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};
|
static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};
|
||||||
|
|
||||||
static textBinding: o.ExternalReference = {name: 'ɵt', moduleName: CORE};
|
static textBinding: o.ExternalReference = {name: 'ɵt', moduleName: CORE};
|
||||||
|
|
|
@ -149,7 +149,7 @@ export function compileComponentFromMetadata(
|
||||||
const template = meta.template;
|
const template = meta.template;
|
||||||
const templateFunctionExpression =
|
const templateFunctionExpression =
|
||||||
new TemplateDefinitionBuilder(
|
new TemplateDefinitionBuilder(
|
||||||
constantPool, CONTEXT_NAME, BindingScope.ROOT_SCOPE, 0, templateTypeName, templateName,
|
constantPool, BindingScope.ROOT_SCOPE, 0, templateTypeName, templateName,
|
||||||
meta.viewQueries, directiveMatcher, directivesUsed, meta.pipes, pipesUsed,
|
meta.viewQueries, directiveMatcher, directivesUsed, meta.pipes, pipesUsed,
|
||||||
R3.namespaceHTML)
|
R3.namespaceHTML)
|
||||||
.buildTemplateFunction(
|
.buildTemplateFunction(
|
||||||
|
|
|
@ -57,12 +57,33 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
private _bindingContext = 0;
|
private _bindingContext = 0;
|
||||||
private _prefixCode: o.Statement[] = [];
|
private _prefixCode: o.Statement[] = [];
|
||||||
private _creationCode: o.Statement[] = [];
|
private _creationCode: o.Statement[] = [];
|
||||||
private _variableCode: o.Statement[] = [];
|
/**
|
||||||
private _bindingCode: (() => o.Statement)[] = [];
|
* List of callbacks to generate update mode instructions. We store them here as we process
|
||||||
private _nestedTemplates: (() => void)[] = [];
|
* the template so bindings are resolved only once all nodes have been visited. This ensures
|
||||||
|
* all local refs and context variables are available for matching.
|
||||||
|
*/
|
||||||
|
private _updateCodeFns: (() => o.Statement)[] = [];
|
||||||
|
/** Temporary variable declarations generated from visiting pipes, literals, etc. */
|
||||||
|
private _tempVariables: o.Statement[] = [];
|
||||||
|
/**
|
||||||
|
* List of callbacks to build nested templates. Nested templates must not be visited until
|
||||||
|
* after the parent template has finished visiting all of its nodes. This ensures that all
|
||||||
|
* local ref bindings in nested templates are able to find local ref values if the refs
|
||||||
|
* are defined after the template declaration.
|
||||||
|
*/
|
||||||
|
private _nestedTemplateFns: (() => void)[] = [];
|
||||||
|
/**
|
||||||
|
* This scope contains local variables declared in the update mode block of the template.
|
||||||
|
* (e.g. refs and context vars in bindings)
|
||||||
|
*/
|
||||||
|
private _updateScope: 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;
|
||||||
private _bindingScope: BindingScope;
|
|
||||||
|
|
||||||
// Whether we are inside a translatable element (`<p i18n>... somewhere here ... </p>)
|
// Whether we are inside a translatable element (`<p i18n>... somewhere here ... </p>)
|
||||||
private _inI18nSection: boolean = false;
|
private _inI18nSection: boolean = false;
|
||||||
|
@ -74,20 +95,19 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
private _pureFunctionSlots = 0;
|
private _pureFunctionSlots = 0;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private constantPool: ConstantPool, private contextParameter: string,
|
private constantPool: ConstantPool, parentBindingScope: BindingScope, private level = 0,
|
||||||
parentBindingScope: BindingScope, private level = 0, private contextName: string|null,
|
private contextName: string|null, private templateName: string|null,
|
||||||
private templateName: string|null, private viewQueries: R3QueryMetadata[],
|
private viewQueries: R3QueryMetadata[], private directiveMatcher: SelectorMatcher|null,
|
||||||
private directiveMatcher: SelectorMatcher|null, private directives: Set<o.Expression>,
|
private directives: Set<o.Expression>, private pipeTypeByName: Map<string, o.Expression>,
|
||||||
private pipeTypeByName: Map<string, o.Expression>, private pipes: Set<o.Expression>,
|
private pipes: Set<o.Expression>, private _namespace: o.ExternalReference) {
|
||||||
private _namespace: o.ExternalReference) {
|
|
||||||
// view queries can take up space in data and allocation happens earlier (in the "viewQuery"
|
// view queries can take up space in data and allocation happens earlier (in the "viewQuery"
|
||||||
// function)
|
// function)
|
||||||
this._dataIndex = viewQueries.length;
|
this._dataIndex = viewQueries.length;
|
||||||
this._bindingScope =
|
|
||||||
parentBindingScope.nestedScope(level, (lhsVar: o.ReadVarExpr, rhsExpr: o.Expression) => {
|
// TODO(kara): generate restore instruction in listener to replace creation scope
|
||||||
this._variableCode.push(
|
this._creationScope = parentBindingScope.nestedScope(level);
|
||||||
lhsVar.set(rhsExpr).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
|
this._updateScope = parentBindingScope.nestedScope(level);
|
||||||
});
|
|
||||||
this._valueConverter = new ValueConverter(
|
this._valueConverter = new ValueConverter(
|
||||||
constantPool, () => this.allocateDataSlot(),
|
constantPool, () => this.allocateDataSlot(),
|
||||||
(numSlots: number): number => this._pureFunctionSlots += numSlots,
|
(numSlots: number): number => this._pureFunctionSlots += numSlots,
|
||||||
|
@ -96,12 +116,33 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
if (pipeType) {
|
if (pipeType) {
|
||||||
this.pipes.add(pipeType);
|
this.pipes.add(pipeType);
|
||||||
}
|
}
|
||||||
this._bindingScope.set(localName, value);
|
this._updateScope.set(this.level, localName, value);
|
||||||
this._creationCode.push(
|
this._creationCode.push(
|
||||||
o.importExpr(R3.pipe).callFn([o.literal(slot), o.literal(name)]).toStmt());
|
o.importExpr(R3.pipe).callFn([o.literal(slot), o.literal(name)]).toStmt());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerContextVariables(variable: t.Variable, retrievalScope: BindingScope) {
|
||||||
|
const scopedName = retrievalScope.freshReferenceName();
|
||||||
|
const retrievalLevel = this.level;
|
||||||
|
const lhs = o.variable(variable.name + scopedName);
|
||||||
|
retrievalScope.set(
|
||||||
|
retrievalLevel, variable.name, lhs, DeclarationPriority.CONTEXT,
|
||||||
|
(scope: BindingScope, relativeLevel: number) => {
|
||||||
|
let rhs: o.Expression;
|
||||||
|
if (scope.bindingLevel === retrievalLevel) {
|
||||||
|
// e.g. ctx
|
||||||
|
rhs = o.variable(CONTEXT_NAME);
|
||||||
|
} else {
|
||||||
|
const sharedCtxVar = scope.getSharedContextName(retrievalLevel);
|
||||||
|
// e.g. ctx_r0 OR x(2);
|
||||||
|
rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel);
|
||||||
|
}
|
||||||
|
// e.g. const $item$ = x(2).$implicit;
|
||||||
|
return [lhs.set(rhs.prop(variable.value || IMPLICIT_REFERENCE)).toConstDecl()];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
buildTemplateFunction(
|
buildTemplateFunction(
|
||||||
nodes: t.Node[], variables: t.Variable[], hasNgContent: boolean = false,
|
nodes: t.Node[], variables: t.Variable[], hasNgContent: boolean = false,
|
||||||
ngContentSelectors: string[] = []): o.FunctionExpr {
|
ngContentSelectors: string[] = []): o.FunctionExpr {
|
||||||
|
@ -111,12 +152,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
|
|
||||||
// Create variable bindings
|
// Create variable bindings
|
||||||
for (const variable of variables) {
|
for (const variable of variables) {
|
||||||
const variableName = variable.name;
|
|
||||||
const expression =
|
|
||||||
o.variable(this.contextParameter).prop(variable.value || IMPLICIT_REFERENCE);
|
|
||||||
const scopedName = this._bindingScope.freshReferenceName();
|
|
||||||
// Add the reference to the local scope.
|
// Add the reference to the local scope.
|
||||||
this._bindingScope.set(variableName, o.variable(variableName + scopedName), expression);
|
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
|
||||||
|
@ -135,42 +173,50 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
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
|
||||||
|
// generate all creation mode instructions & queue all update mode instructions for
|
||||||
|
// generation in the second pass. It's necessary to separate the passes to ensure
|
||||||
|
// local refs are defined before resolving bindings.
|
||||||
t.visitAll(this, nodes);
|
t.visitAll(this, nodes);
|
||||||
|
|
||||||
const creationCode = this._creationCode.length > 0 ?
|
// Generate all the update mode instructions as the second pass (e.g. resolve bindings)
|
||||||
[renderFlagCheckIfStmt(core.RenderFlags.Create, this._creationCode)] :
|
const updateStatements = this._updateCodeFns.map((fn: () => o.Statement) => fn());
|
||||||
[];
|
|
||||||
|
|
||||||
const updateCode = this._bindingCode.length > 0 ?
|
|
||||||
[renderFlagCheckIfStmt(core.RenderFlags.Update, this._variableCode.concat(
|
|
||||||
this._variableCode.concat(this._bindingCode.map((fn: () => o.Statement) => fn()))))] :
|
|
||||||
[];
|
|
||||||
|
|
||||||
|
// 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));
|
this.creationInstruction(null, R3.reserveSlots, o.literal(this._pureFunctionSlots));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const creationCode = this._creationCode.length > 0 ?
|
||||||
|
[renderFlagCheckIfStmt(
|
||||||
|
core.RenderFlags.Create,
|
||||||
|
this._creationScope.variableDeclarations().concat(this._creationCode))] :
|
||||||
|
[];
|
||||||
|
|
||||||
|
// This must occur after binding resolution so we can generate context instructions that
|
||||||
|
// 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))] :
|
||||||
|
[];
|
||||||
|
|
||||||
// Generate maps of placeholder name to node indexes
|
// Generate maps of placeholder name to node indexes
|
||||||
// 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._bindingScope.freshReferenceName();
|
const scopedName = this._updateScope.freshReferenceName();
|
||||||
const phMap = o.variable(scopedName)
|
const phMap = o.variable(scopedName).set(mapToExpression(phToNodeIdx, true)).toConstDecl();
|
||||||
.set(mapToExpression(phToNodeIdx, true))
|
|
||||||
.toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]);
|
|
||||||
|
|
||||||
this._prefixCode.push(phMap);
|
this._prefixCode.push(phMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._nestedTemplates.forEach(buildTemplateFn => buildTemplateFn());
|
this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
|
||||||
|
|
||||||
return o.fn(
|
return o.fn(
|
||||||
// i.e. (rf: RenderFlags, ctx0: any, ctx: any)
|
// i.e. (rf: RenderFlags, ctx: any)
|
||||||
[
|
[new o.FnParam(RENDER_FLAGS, o.NUMBER_TYPE), new o.FnParam(CONTEXT_NAME, null)],
|
||||||
new o.FnParam(RENDER_FLAGS, o.NUMBER_TYPE), ...this.getNestedContexts(),
|
|
||||||
new o.FnParam(CONTEXT_NAME, null)
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
// 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,
|
||||||
|
@ -183,7 +229,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocalResolver
|
// LocalResolver
|
||||||
getLocal(name: string): o.Expression|null { return this._bindingScope.get(name); }
|
getLocal(name: string): o.Expression|null { return this._updateScope.get(name); }
|
||||||
|
|
||||||
visitContent(ngContent: t.Content) {
|
visitContent(ngContent: t.Content) {
|
||||||
const slot = this.allocateDataSlot();
|
const slot = this.allocateDataSlot();
|
||||||
|
@ -225,18 +271,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
this.creationInstruction(element.sourceSpan, nsInstruction);
|
this.creationInstruction(element.sourceSpan, nsInstruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNestedContexts(): o.FnParam[] {
|
|
||||||
const nestedContexts = [];
|
|
||||||
let nestingLevel = this.level - 1;
|
|
||||||
|
|
||||||
while (nestingLevel >= 0) {
|
|
||||||
nestedContexts.push(new o.FnParam(`ctx${nestingLevel}`, null));
|
|
||||||
nestingLevel--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nestedContexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
visitElement(element: t.Element) {
|
visitElement(element: t.Element) {
|
||||||
const elementIndex = this.allocateDataSlot();
|
const elementIndex = this.allocateDataSlot();
|
||||||
const wasInI18nSection = this._inI18nSection;
|
const wasInI18nSection = this._inI18nSection;
|
||||||
|
@ -426,17 +460,19 @@ 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._bindingScope.freshReferenceName();
|
const variableName = this._updateScope.freshReferenceName();
|
||||||
// When the ref's binding is processed, we'll either generate a load() or a reference()
|
const retrievalLevel = this.level;
|
||||||
// instruction depending on the nesting level of the binding relative to the reference def.
|
const lhs = o.variable(variableName);
|
||||||
const refLevel = this.level;
|
this._updateScope.set(
|
||||||
this._bindingScope.set(
|
retrievalLevel, reference.name, lhs, DeclarationPriority.DEFAULT,
|
||||||
reference.name, o.variable(variableName), undefined, (bindingLevel: number) => {
|
(scope: BindingScope, relativeLevel: number) => {
|
||||||
return bindingLevel === refLevel ?
|
// e.g. x(2);
|
||||||
o.importExpr(R3.load).callFn([o.literal(slot)]) :
|
const nextContextStmt =
|
||||||
o.importExpr(R3.reference).callFn([
|
relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : [];
|
||||||
o.literal(bindingLevel - refLevel), o.literal(slot)
|
|
||||||
]);
|
// e.g. const $foo$ = r(1);
|
||||||
|
const refExpr = lhs.set(o.importExpr(R3.reference).callFn([o.literal(slot)]));
|
||||||
|
return nextContextStmt.concat(refExpr.toConstDecl());
|
||||||
});
|
});
|
||||||
return [reference.name, reference.value];
|
return [reference.name, reference.value];
|
||||||
}));
|
}));
|
||||||
|
@ -515,17 +551,11 @@ 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`;
|
||||||
const localVars: o.Statement[] = [];
|
|
||||||
const bindingScope = this._bindingScope.nestedScope(
|
|
||||||
this.level + 1, (lhsVar: o.ReadVarExpr, rhsExpression: o.Expression) => {
|
|
||||||
localVars.push(
|
|
||||||
lhsVar.set(rhsExpression).toDeclStmt(o.INFERRED_TYPE, [o.StmtModifier.Final]));
|
|
||||||
});
|
|
||||||
const bindingExpr = convertActionBinding(
|
const bindingExpr = convertActionBinding(
|
||||||
bindingScope, implicit, outputAst.handler, 'b',
|
this._creationScope, implicit, outputAst.handler, 'b',
|
||||||
() => error('Unexpected interpolation'));
|
() => error('Unexpected interpolation'));
|
||||||
const handler = o.fn(
|
const handler = o.fn(
|
||||||
[new o.FnParam('$event', o.DYNAMIC_TYPE)], [...localVars, ...bindingExpr.render3Stmts],
|
[new o.FnParam('$event', o.DYNAMIC_TYPE)], [...bindingExpr.render3Stmts],
|
||||||
o.INFERRED_TYPE, null, functionName);
|
o.INFERRED_TYPE, null, functionName);
|
||||||
this.creationInstruction(
|
this.creationInstruction(
|
||||||
outputAst.sourceSpan, R3.listener, o.literal(outputAst.name), handler);
|
outputAst.sourceSpan, R3.listener, o.literal(outputAst.name), handler);
|
||||||
|
@ -668,8 +698,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
const templateName =
|
const templateName =
|
||||||
contextName ? `${contextName}_Template_${templateIndex}` : `Template_${templateIndex}`;
|
contextName ? `${contextName}_Template_${templateIndex}` : `Template_${templateIndex}`;
|
||||||
|
|
||||||
const templateContext = `ctx${this.level}`;
|
|
||||||
|
|
||||||
const parameters: o.Expression[] = [
|
const parameters: o.Expression[] = [
|
||||||
o.literal(templateIndex),
|
o.literal(templateIndex),
|
||||||
o.variable(templateName),
|
o.variable(templateName),
|
||||||
|
@ -713,15 +741,14 @@ 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, templateContext, this._bindingScope, this.level + 1, contextName,
|
this.constantPool, this._updateScope, this.level + 1, contextName, templateName, [],
|
||||||
templateName, [], this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes,
|
this.directiveMatcher, this.directives, this.pipeTypeByName, this.pipes, this._namespace);
|
||||||
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
|
||||||
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
|
// processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
|
||||||
// be able to support bindings in nested templates to local refs that occur after the
|
// be able to support bindings in nested templates to local refs that occur after the
|
||||||
// template definition. e.g. <div *ngIf="showing"> {{ foo }} </div> <div #foo></div>
|
// template definition. e.g. <div *ngIf="showing"> {{ foo }} </div> <div #foo></div>
|
||||||
this._nestedTemplates.push(() => {
|
this._nestedTemplateFns.push(() => {
|
||||||
const templateFunctionExpr =
|
const templateFunctionExpr =
|
||||||
templateVisitor.buildTemplateFunction(template.children, template.variables);
|
templateVisitor.buildTemplateFunction(template.children, template.variables);
|
||||||
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||||
|
@ -790,7 +817,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
// bindings. e.g. {{ foo }} <div #foo></div>
|
// bindings. e.g. {{ foo }} <div #foo></div>
|
||||||
private updateInstruction(
|
private updateInstruction(
|
||||||
span: ParseSourceSpan|null, reference: o.ExternalReference, paramsFn: () => o.Expression[]) {
|
span: ParseSourceSpan|null, reference: o.ExternalReference, paramsFn: () => o.Expression[]) {
|
||||||
this._bindingCode.push(() => { return this.instruction(span, reference, paramsFn()); });
|
this._updateCodeFns.push(() => { return this.instruction(span, reference, paramsFn()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertPropertyBinding(implicit: o.Expression, value: AST, skipBindFn?: boolean):
|
private convertPropertyBinding(implicit: o.Expression, value: AST, skipBindFn?: boolean):
|
||||||
|
@ -800,7 +827,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
|
|
||||||
const convertedPropertyBinding = convertPropertyBinding(
|
const convertedPropertyBinding = convertPropertyBinding(
|
||||||
this, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolationFn);
|
this, implicit, value, this.bindingContext(), BindingForm.TrySimple, interpolationFn);
|
||||||
this._variableCode.push(...convertedPropertyBinding.stmts);
|
this._tempVariables.push(...convertedPropertyBinding.stmts);
|
||||||
|
|
||||||
const valExpr = convertedPropertyBinding.currValExpr;
|
const valExpr = convertedPropertyBinding.currValExpr;
|
||||||
return value instanceof Interpolation || skipBindFn ? valExpr :
|
return value instanceof Interpolation || skipBindFn ? valExpr :
|
||||||
|
@ -888,6 +915,12 @@ function pureFunctionCallInfo(args: o.Expression[]) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// e.g. x(2);
|
||||||
|
function generateNextContextExpr(relativeLevelDiff: number): o.Expression {
|
||||||
|
return o.importExpr(R3.nextContext)
|
||||||
|
.callFn(relativeLevelDiff > 1 ? [o.literal(relativeLevelDiff)] : []);
|
||||||
|
}
|
||||||
|
|
||||||
function getLiteralFactory(
|
function getLiteralFactory(
|
||||||
constantPool: ConstantPool, literal: o.LiteralArrayExpr | o.LiteralMapExpr,
|
constantPool: ConstantPool, literal: o.LiteralArrayExpr | o.LiteralMapExpr,
|
||||||
allocateSlots: (numSlots: number) => number): o.Expression {
|
allocateSlots: (numSlots: number) => number): o.Expression {
|
||||||
|
@ -919,33 +952,44 @@ function getLiteralFactory(
|
||||||
*
|
*
|
||||||
* It is expected that the function creates the `const localName = expression`; statement.
|
* It is expected that the function creates the `const localName = expression`; statement.
|
||||||
*/
|
*/
|
||||||
export type DeclareLocalVarCallback = (lhsVar: o.ReadVarExpr, rhsExpression: o.Expression) => void;
|
export type DeclareLocalVarCallback = (scope: BindingScope, relativeLevel: number) => o.Statement[];
|
||||||
|
|
||||||
|
/** The prefix used to get a shared context in BindingScope's map. */
|
||||||
|
const SHARED_CONTEXT_KEY = '$$shared_ctx$$';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used when one refers to variable such as: 'let abc = x(2).$implicit`.
|
||||||
|
* - key to the map is the string literal `"abc"`.
|
||||||
|
* - value `retrievalLevel` is the level from which this value can be retrieved, which is 2 levels
|
||||||
|
* up in example.
|
||||||
|
* - value `lhs` is the left hand side which is an AST representing `abc`.
|
||||||
|
* - value `declareLocalCallback` is a callback that is invoked when declaring the local.
|
||||||
|
* - value `declare` is true if this value needs to be declared.
|
||||||
|
* - value `priority` dictates the sorting priority of this var declaration compared
|
||||||
|
* to other var declarations on the same retrieval level. For example, if there is a
|
||||||
|
* context variable and a local ref accessing the same parent view, the context var
|
||||||
|
* declaration should always come before the local ref declaration.
|
||||||
|
*/
|
||||||
|
type BindingData = {
|
||||||
|
retrievalLevel: number; lhs: o.ReadVarExpr; declareLocalCallback?: DeclareLocalVarCallback;
|
||||||
|
declare: boolean;
|
||||||
|
priority: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sorting priority of a local variable declaration. Higher numbers
|
||||||
|
* mean the declaration will appear first in the generated code.
|
||||||
|
*/
|
||||||
|
const enum DeclarationPriority { DEFAULT = 0, CONTEXT = 1, SHARED_CONTEXT = 2 }
|
||||||
|
|
||||||
export class BindingScope implements LocalResolver {
|
export class BindingScope implements LocalResolver {
|
||||||
/**
|
/** Keeps a map from local variables to their BindingData. */
|
||||||
* Keeps a map from local variables to their expressions.
|
private map = new Map<string, BindingData>();
|
||||||
*
|
|
||||||
* This is used when one refers to variable such as: 'let abc = a.b.c`.
|
|
||||||
* - key to the map is the string literal `"abc"`.
|
|
||||||
* - value `lhs` is the left hand side which is an AST representing `abc`.
|
|
||||||
* - value `rhs` is the right hand side which is an AST representing `a.b.c`.
|
|
||||||
* - value `declared` is true if the `declareLocalVarCallback` has been called for this scope
|
|
||||||
* already.
|
|
||||||
*/
|
|
||||||
private map = new Map < string, {
|
|
||||||
lhs: o.ReadVarExpr;
|
|
||||||
rhs: o.Expression|undefined;
|
|
||||||
declared: boolean;
|
|
||||||
rhsCallback?: (level: number) => o.Expression;
|
|
||||||
}
|
|
||||||
> ();
|
|
||||||
private referenceNameIndex = 0;
|
private referenceNameIndex = 0;
|
||||||
|
|
||||||
static ROOT_SCOPE = new BindingScope().set('$event', o.variable('$event'));
|
static ROOT_SCOPE = new BindingScope().set(-1, '$event', o.variable('$event'));
|
||||||
|
|
||||||
private constructor(
|
private constructor(public bindingLevel: number = 0, private parent: BindingScope|null = null) {}
|
||||||
private level: number = 0, private parent: BindingScope|null = null,
|
|
||||||
private declareLocalVarCallback: DeclareLocalVarCallback = noop) {}
|
|
||||||
|
|
||||||
get(name: string): o.Expression|null {
|
get(name: string): o.Expression|null {
|
||||||
let current: BindingScope|null = this;
|
let current: BindingScope|null = this;
|
||||||
|
@ -953,48 +997,118 @@ export class BindingScope implements LocalResolver {
|
||||||
let value = current.map.get(name);
|
let value = current.map.get(name);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
if (current !== this) {
|
if (current !== this) {
|
||||||
// make a local copy and reset the `declared` state.
|
// make a local copy and reset the `declare` state
|
||||||
value = {lhs: value.lhs, rhs: value.rhs, rhsCallback: value.rhsCallback, declared: false};
|
value = {
|
||||||
|
retrievalLevel: value.retrievalLevel,
|
||||||
|
lhs: value.lhs,
|
||||||
|
declareLocalCallback: value.declareLocalCallback,
|
||||||
|
declare: false,
|
||||||
|
priority: value.priority
|
||||||
|
};
|
||||||
|
|
||||||
// Cache the value locally.
|
// Cache the value locally.
|
||||||
this.map.set(name, value);
|
this.map.set(name, value);
|
||||||
|
// Possibly generate a shared context var
|
||||||
|
this.maybeGenerateSharedContextVar(value);
|
||||||
}
|
}
|
||||||
const rhs = value.rhs || value.rhsCallback && value.rhsCallback(this.level);
|
|
||||||
if (rhs && !value.declared) {
|
if (value.declareLocalCallback && !value.declare) {
|
||||||
// if it is first time we are referencing the variable in the scope
|
value.declare = true;
|
||||||
// then invoke the callback to insert variable declaration.
|
|
||||||
this.declareLocalVarCallback(value.lhs, rhs);
|
|
||||||
value.declared = true;
|
|
||||||
}
|
}
|
||||||
return value.lhs;
|
return value.lhs;
|
||||||
}
|
}
|
||||||
current = current.parent;
|
current = current.parent;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
// If we get to this point, we are looking for a property on the top level component
|
||||||
|
// - If level === 0, we are on the top and don't need to re-declare `ctx`.
|
||||||
|
// - If level > 0, we are in an embedded view. We need to retrieve the name of the
|
||||||
|
// local var we used to store the component context, e.g. const $comp$ = x();
|
||||||
|
return this.bindingLevel === 0 ? null : this.getComponentProperty(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a local variable for later reference.
|
* Create a local variable for later reference.
|
||||||
*
|
*
|
||||||
|
* @param retrievalLevel The level from which this value can be retrieved
|
||||||
* @param name Name of the variable.
|
* @param name Name of the variable.
|
||||||
* @param lhs AST representing the left hand side of the `let lhs = rhs;`.
|
* @param lhs AST representing the left hand side of the `let lhs = rhs;`.
|
||||||
* @param rhs AST representing the right hand side of the `let lhs = rhs;`. The `rhs` can be
|
* @param priority The sorting priority of this var
|
||||||
* `undefined` for variable that are ambient such as `$event` and which don't have `rhs`
|
* @param declareLocalCallback The callback to invoke when declaring this local var
|
||||||
* declaration.
|
|
||||||
*/
|
*/
|
||||||
set(name: string, lhs: o.ReadVarExpr, rhs?: o.Expression,
|
set(retrievalLevel: number, name: string, lhs: o.ReadVarExpr,
|
||||||
rhsCallback?: (level: number) => o.Expression): BindingScope {
|
priority: number = DeclarationPriority.DEFAULT,
|
||||||
|
declareLocalCallback?: DeclareLocalVarCallback): BindingScope {
|
||||||
!this.map.has(name) ||
|
!this.map.has(name) ||
|
||||||
error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
|
error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
|
||||||
this.map.set(name, {lhs: lhs, rhs: rhs, declared: false, rhsCallback: rhsCallback});
|
this.map.set(name, {
|
||||||
|
retrievalLevel: retrievalLevel,
|
||||||
|
lhs: lhs,
|
||||||
|
declare: false,
|
||||||
|
declareLocalCallback: declareLocalCallback,
|
||||||
|
priority: priority
|
||||||
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getLocal(name: string): (o.Expression|null) { return this.get(name); }
|
getLocal(name: string): (o.Expression|null) { return this.get(name); }
|
||||||
|
|
||||||
nestedScope(level: number, declareCallback: DeclareLocalVarCallback): BindingScope {
|
nestedScope(level: number): BindingScope {
|
||||||
return new BindingScope(level, this, declareCallback);
|
const newScope = new BindingScope(level, this);
|
||||||
|
if (level > 0) newScope.generateSharedContextVar(0);
|
||||||
|
return newScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSharedContextName(retrievalLevel: number): o.ReadVarExpr|null {
|
||||||
|
const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + retrievalLevel);
|
||||||
|
return sharedCtxObj && sharedCtxObj.declare ? sharedCtxObj.lhs : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
maybeGenerateSharedContextVar(value: BindingData) {
|
||||||
|
if (value.priority === DeclarationPriority.CONTEXT) {
|
||||||
|
const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + value.retrievalLevel);
|
||||||
|
if (sharedCtxObj) {
|
||||||
|
sharedCtxObj.declare = true;
|
||||||
|
} else {
|
||||||
|
this.generateSharedContextVar(value.retrievalLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generateSharedContextVar(retrievalLevel: number) {
|
||||||
|
const lhs = o.variable(CONTEXT_NAME + this.freshReferenceName());
|
||||||
|
this.map.set(SHARED_CONTEXT_KEY + retrievalLevel, {
|
||||||
|
retrievalLevel: retrievalLevel,
|
||||||
|
lhs: lhs,
|
||||||
|
declareLocalCallback: (scope: BindingScope, relativeLevel: number) => {
|
||||||
|
// const ctx_r0 = x(2);
|
||||||
|
return [lhs.set(generateNextContextExpr(relativeLevel)).toConstDecl()];
|
||||||
|
},
|
||||||
|
declare: false,
|
||||||
|
priority: DeclarationPriority.SHARED_CONTEXT
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentProperty(name: string): o.Expression {
|
||||||
|
const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0) !;
|
||||||
|
componentValue.declare = true;
|
||||||
|
return componentValue.lhs.prop(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
variableDeclarations(): o.Statement[] {
|
||||||
|
let currentContextLevel = 0;
|
||||||
|
return Array.from(this.map.values())
|
||||||
|
.filter(value => value.declare)
|
||||||
|
.sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority)
|
||||||
|
.reduce((stmts: o.Statement[], value: BindingData) => {
|
||||||
|
const levelDiff = this.bindingLevel - value.retrievalLevel;
|
||||||
|
const currStmts = value.declareLocalCallback !(this, levelDiff - currentContextLevel);
|
||||||
|
currentContextLevel = levelDiff;
|
||||||
|
return stmts.concat(currStmts);
|
||||||
|
}, []) as o.Statement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
freshReferenceName(): string {
|
freshReferenceName(): string {
|
||||||
let current: BindingScope = this;
|
let current: BindingScope = this;
|
||||||
// Find the top scope as it maintains the global reference count
|
// Find the top scope as it maintains the global reference count
|
||||||
|
|
|
@ -32,6 +32,7 @@ export {
|
||||||
NgModuleFactory as ɵNgModuleFactory,
|
NgModuleFactory as ɵNgModuleFactory,
|
||||||
NC as ɵNC,
|
NC as ɵNC,
|
||||||
C as ɵC,
|
C as ɵC,
|
||||||
|
x as ɵx,
|
||||||
E as ɵE,
|
E as ɵE,
|
||||||
NH as ɵNH,
|
NH as ɵNH,
|
||||||
NM as ɵNM,
|
NM as ɵNM,
|
||||||
|
|
|
@ -22,7 +22,7 @@ import {baseDirectiveCreate, createLNode, createLViewData, createTView, elementC
|
||||||
import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition';
|
import {ComponentDefInternal, ComponentType, RenderFlags} from './interfaces/definition';
|
||||||
import {LElementNode, TNode, TNodeType} from './interfaces/node';
|
import {LElementNode, TNode, TNodeType} from './interfaces/node';
|
||||||
import {RElement, domRendererFactory3} from './interfaces/renderer';
|
import {RElement, domRendererFactory3} from './interfaces/renderer';
|
||||||
import {FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
|
import {CONTEXT, FLAGS, INJECTOR, LViewData, LViewFlags, RootContext, TVIEW} from './interfaces/view';
|
||||||
import {ViewRef} from './view_ref';
|
import {ViewRef} from './view_ref';
|
||||||
|
|
||||||
export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver {
|
export class ComponentFactoryResolver extends viewEngine_ComponentFactoryResolver {
|
||||||
|
@ -120,6 +120,7 @@ export class ComponentFactory<T> extends viewEngine_ComponentFactory<T> {
|
||||||
rootContext.components.push(
|
rootContext.components.push(
|
||||||
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
|
component = baseDirectiveCreate(0, this.componentDef.factory(), this.componentDef) as T);
|
||||||
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
|
initChangeDetectorIfExisting(elementNode.nodeInjector, component, elementNode.data !);
|
||||||
|
(elementNode.data as LViewData)[CONTEXT] = component;
|
||||||
|
|
||||||
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
|
// TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
|
||||||
// executed here?
|
// executed here?
|
||||||
|
|
|
@ -738,8 +738,8 @@ class TemplateRef<T> implements viewEngine.TemplateRef<T> {
|
||||||
readonly elementRef: viewEngine.ElementRef;
|
readonly elementRef: viewEngine.ElementRef;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _declarationParentView: LViewData, elementRef: viewEngine.ElementRef, private _tView: TView, private _renderer: Renderer3,
|
private _declarationParentView: LViewData, elementRef: viewEngine.ElementRef,
|
||||||
private _queries: LQueries|null) {
|
private _tView: TView, private _renderer: Renderer3, private _queries: LQueries|null) {
|
||||||
this.elementRef = elementRef;
|
this.elementRef = elementRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,8 @@ export {
|
||||||
containerRefreshStart as cR,
|
containerRefreshStart as cR,
|
||||||
containerRefreshEnd as cr,
|
containerRefreshEnd as cr,
|
||||||
|
|
||||||
|
nextContext as x,
|
||||||
|
|
||||||
element as Ee,
|
element as Ee,
|
||||||
elementAttribute as a,
|
elementAttribute as a,
|
||||||
elementClassProp as cp,
|
elementClassProp as cp,
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {assertDefined, assertEqual, assertLessThan, assertNotDefined, assertNotE
|
||||||
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
||||||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||||
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||||
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, EmbeddedTemplate, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
import {ComponentDefInternal, ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||||
import {LInjector} from './interfaces/injector';
|
import {LInjector} from './interfaces/injector';
|
||||||
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LElementNode, LNode, LProjectionNode, LTextNode, LViewNode, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||||
|
@ -165,6 +165,14 @@ export function getCreationMode(): boolean {
|
||||||
*/
|
*/
|
||||||
let viewData: LViewData;
|
let viewData: LViewData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The last viewData retrieved by nextContext().
|
||||||
|
* Allows building nextContext() and reference() calls.
|
||||||
|
*
|
||||||
|
* e.g. const inner = x().$implicit; const outer = x().$implicit;
|
||||||
|
*/
|
||||||
|
let contextViewData: LViewData = null !;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of directive instances in the current view.
|
* An array of directive instances in the current view.
|
||||||
*
|
*
|
||||||
|
@ -223,7 +231,7 @@ export function enterView(newView: LViewData, host: LElementNode | LViewNode | n
|
||||||
isParent = true;
|
isParent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewData = newView;
|
viewData = contextViewData = newView;
|
||||||
currentQueries = newView && newView[QUERIES];
|
currentQueries = newView && newView[QUERIES];
|
||||||
|
|
||||||
return oldView;
|
return oldView;
|
||||||
|
@ -547,7 +555,7 @@ export function renderEmbeddedTemplate<T>(
|
||||||
|
|
||||||
oldView = enterView(viewNode.data !, viewNode);
|
oldView = enterView(viewNode.data !, viewNode);
|
||||||
namespaceHTML();
|
namespaceHTML();
|
||||||
callTemplateWithContexts(rf, context, tView.template !, viewNode.data ![DECLARATION_VIEW] !);
|
tView.template !(rf, context);
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
refreshDescendantViews();
|
refreshDescendantViews();
|
||||||
} else {
|
} else {
|
||||||
|
@ -566,113 +574,18 @@ export function renderEmbeddedTemplate<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function calls the template function of a dynamically created view with
|
* Retrieves a context at the level specified and saves it as the global, contextViewData.
|
||||||
* all of its declaration parent contexts (up the view tree) until it reaches the
|
* Will get the next level up if level is not specified.
|
||||||
* component boundary.
|
|
||||||
*
|
*
|
||||||
* Example:
|
* This is used to save contexts of parent views so they can be bound in embedded views, or
|
||||||
|
* in conjunction with reference() to bind a ref from a parent view.
|
||||||
*
|
*
|
||||||
* AppComponent template:
|
* @param level The relative level of the view from which to grab context compared to contextVewData
|
||||||
* <ul *ngFor="let list of lists">
|
* @returns context
|
||||||
* <li *ngFor="let item of list"> {{ item }} </li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* function ulTemplate(rf, ulCtx, appCtx) {...}
|
|
||||||
* function liTemplate(rf, liCtx, ulCtx, appCtx) {...}
|
|
||||||
*
|
|
||||||
* class AppComponent {...}
|
|
||||||
* AppComponent.ngComponentDef = defineComponent({
|
|
||||||
* template: function AppComponentTemplate(rf, ctx) {...}
|
|
||||||
* });
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The ul view's template must be called with its own context and its declaration
|
|
||||||
* parent, AppComponent. The li view's template must be called with its own context, its
|
|
||||||
* parent (the ul), and the ul's parent (AppComponent).
|
|
||||||
*
|
|
||||||
* Note that a declaration parent is NOT always the same as the insertion parent. Templates
|
|
||||||
* can be declared in different views than they are used.
|
|
||||||
*
|
|
||||||
* @param rf The RenderFlags for this template invocation
|
|
||||||
* @param currentContext The context for this template
|
|
||||||
* @param template The template function to call
|
|
||||||
* @param parentView The declaration view of the dynamic view
|
|
||||||
*/
|
*/
|
||||||
function callTemplateWithContexts<T>(
|
export function nextContext(level: number = 1): any {
|
||||||
rf: RenderFlags, currentContext: T, template: EmbeddedTemplate<T>,
|
contextViewData = walkUpViews(level, contextViewData !);
|
||||||
parentView: LViewData): void {
|
return contextViewData[CONTEXT];
|
||||||
const parentContext = parentView[CONTEXT];
|
|
||||||
const parentView2 = parentView[DECLARATION_VIEW];
|
|
||||||
|
|
||||||
// Calling a function with extra arguments has a VM cost, so only call with necessary args
|
|
||||||
if (parentView2 === null) {
|
|
||||||
return template(rf, currentContext, parentContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentContext2 = parentView2[CONTEXT];
|
|
||||||
const parentView3 = parentView2[DECLARATION_VIEW];
|
|
||||||
if (parentView3 === null) {
|
|
||||||
return template(rf, currentContext, parentContext, parentContext2);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentContext3 = parentView3[CONTEXT];
|
|
||||||
const parentView4 = parentView3[DECLARATION_VIEW];
|
|
||||||
if (parentView4 === null) {
|
|
||||||
return template(rf, currentContext, parentContext, parentContext2, parentContext3);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentContext4 = parentView4[CONTEXT];
|
|
||||||
const parentView5 = parentView4[DECLARATION_VIEW];
|
|
||||||
if (parentView5 === null) {
|
|
||||||
return template(
|
|
||||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentContext5 = parentView5[CONTEXT];
|
|
||||||
const parentView6 = parentView5[DECLARATION_VIEW];
|
|
||||||
if (parentView6 === null) {
|
|
||||||
return template(
|
|
||||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
|
||||||
parentContext5);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentContext6 = parentView6[CONTEXT];
|
|
||||||
const parentView7 = parentView6[DECLARATION_VIEW];
|
|
||||||
if (parentView7 === null) {
|
|
||||||
return template(
|
|
||||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
|
||||||
parentContext5, parentContext6);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentContext7 = parentView7[CONTEXT];
|
|
||||||
const parentView8 = parentView7[DECLARATION_VIEW];
|
|
||||||
if (parentView8 === null) {
|
|
||||||
return template(
|
|
||||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
|
||||||
parentContext5, parentContext6, parentContext7);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentContext8 = parentView8[CONTEXT];
|
|
||||||
const parentView9 = parentView8[DECLARATION_VIEW];
|
|
||||||
if (parentView9 === null) {
|
|
||||||
return template(
|
|
||||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
|
||||||
parentContext5, parentContext6, parentContext7, parentContext8);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We support up to 8 nesting levels in embedded views before we give up and call apply()
|
|
||||||
const templateArgs = [
|
|
||||||
rf, currentContext, parentContext, parentContext2, parentContext3, parentContext4,
|
|
||||||
parentContext5, parentContext6, parentContext7, parentContext8, parentView9[CONTEXT]
|
|
||||||
];
|
|
||||||
|
|
||||||
let currentDeclarationView: LViewData|null = parentView9[DECLARATION_VIEW];
|
|
||||||
while (currentDeclarationView) {
|
|
||||||
templateArgs.push(currentDeclarationView[CONTEXT]);
|
|
||||||
currentDeclarationView = currentDeclarationView[DECLARATION_VIEW] !;
|
|
||||||
}
|
|
||||||
|
|
||||||
template.apply(null, templateArgs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderComponentOrTemplate<T>(
|
export function renderComponentOrTemplate<T>(
|
||||||
|
@ -1007,7 +920,7 @@ function getOrCreateTView(
|
||||||
* @param pipes Registry of pipes for this view
|
* @param pipes Registry of pipes for this view
|
||||||
*/
|
*/
|
||||||
export function createTView(
|
export function createTView(
|
||||||
viewIndex: number, template: ComponentTemplate<any>| EmbeddedTemplate<any>| null,
|
viewIndex: number, template: ComponentTemplate<any>| null,
|
||||||
directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null,
|
directives: DirectiveDefListOrFactory | null, pipes: PipeDefListOrFactory | null,
|
||||||
viewQuery: ComponentQuery<any>| null): TView {
|
viewQuery: ComponentQuery<any>| null): TView {
|
||||||
ngDevMode && ngDevMode.tView++;
|
ngDevMode && ngDevMode.tView++;
|
||||||
|
@ -1828,7 +1741,7 @@ export function createLContainer(
|
||||||
* @param localRefs A set of local reference bindings on the element.
|
* @param localRefs A set of local reference bindings on the element.
|
||||||
*/
|
*/
|
||||||
export function container(
|
export function container(
|
||||||
index: number, template?: EmbeddedTemplate<any>, tagName?: string | null, attrs?: TAttributes,
|
index: number, template?: ComponentTemplate<any>, tagName?: string | null, attrs?: TAttributes,
|
||||||
localRefs?: string[] | null): void {
|
localRefs?: string[] | null): void {
|
||||||
ngDevMode &&
|
ngDevMode &&
|
||||||
assertEqual(
|
assertEqual(
|
||||||
|
@ -2653,9 +2566,19 @@ export function store<T>(index: number, value: T): void {
|
||||||
viewData[adjustedIndex] = value;
|
viewData[adjustedIndex] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retrieves a value from an LViewData at the given nesting level. */
|
/**
|
||||||
export function reference<T>(nestingLevel: number, index: number) {
|
* Retrieves a local reference from the current contextViewData.
|
||||||
let currentView = viewData;
|
*
|
||||||
|
* If the reference to retrieve is in a parent view, this instruction is used in conjunction
|
||||||
|
* with a nextContext() call, which walks up the tree and updates the contextViewData instance.
|
||||||
|
*
|
||||||
|
* @param index The index of the local ref in contextViewData.
|
||||||
|
*/
|
||||||
|
export function reference<T>(index: number) {
|
||||||
|
return loadInternal<T>(index, contextViewData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function walkUpViews(nestingLevel: number, currentView: LViewData): LViewData {
|
||||||
while (nestingLevel > 0) {
|
while (nestingLevel > 0) {
|
||||||
ngDevMode && assertDefined(
|
ngDevMode && assertDefined(
|
||||||
currentView[DECLARATION_VIEW],
|
currentView[DECLARATION_VIEW],
|
||||||
|
@ -2663,8 +2586,7 @@ export function reference<T>(nestingLevel: number, index: number) {
|
||||||
currentView = currentView[DECLARATION_VIEW] !;
|
currentView = currentView[DECLARATION_VIEW] !;
|
||||||
nestingLevel--;
|
nestingLevel--;
|
||||||
}
|
}
|
||||||
|
return currentView;
|
||||||
return loadInternal<T>(index, currentView);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Retrieves a value from the `directives` array. */
|
/** Retrieves a value from the `directives` array. */
|
||||||
|
|
|
@ -19,13 +19,6 @@ export type ComponentTemplate<T> = {
|
||||||
(rf: RenderFlags, ctx: T): void; ngPrivateData?: never;
|
(rf: RenderFlags, ctx: T): void; ngPrivateData?: never;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Definition of what a template rendering function should look like for an embedded view.
|
|
||||||
*/
|
|
||||||
export type EmbeddedTemplate<T> = {
|
|
||||||
(rf: RenderFlags, ctx: T, ...parentCtx: any[]): void;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Definition of what a query function should look like.
|
* Definition of what a query function should look like.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -36,6 +36,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
||||||
'ɵa': r3.a,
|
'ɵa': r3.a,
|
||||||
'ɵb': r3.b,
|
'ɵb': r3.b,
|
||||||
'ɵC': r3.C,
|
'ɵC': r3.C,
|
||||||
|
'ɵx': r3.x,
|
||||||
'ɵcR': r3.cR,
|
'ɵcR': r3.cR,
|
||||||
'ɵcr': r3.cr,
|
'ɵcr': r3.cr,
|
||||||
'ɵd': r3.d,
|
'ɵd': r3.d,
|
||||||
|
|
|
@ -12,7 +12,6 @@ import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_co
|
||||||
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
|
||||||
|
|
||||||
import {checkNoChanges, detectChanges, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
import {checkNoChanges, detectChanges, markViewDirty, storeCleanupFn, viewAttached} from './instructions';
|
||||||
import {EmbeddedTemplate} from './interfaces/definition';
|
|
||||||
import {LViewNode} from './interfaces/node';
|
import {LViewNode} from './interfaces/node';
|
||||||
import {FLAGS, LViewData, LViewFlags} from './interfaces/view';
|
import {FLAGS, LViewData, LViewFlags} from './interfaces/view';
|
||||||
import {destroyLView} from './node_manipulation';
|
import {destroyLView} from './node_manipulation';
|
||||||
|
@ -244,30 +243,3 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
||||||
|
|
||||||
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
|
attachToAppRef(appRef: ApplicationRef) { this._appRef = appRef; }
|
||||||
}
|
}
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
|
|
||||||
|
|
||||||
export class EmbeddedViewRef<T> extends ViewRef<T> {
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
_lViewNode: LViewNode;
|
|
||||||
private _viewContainerRef: viewEngine_ViewContainerRef|null = null;
|
|
||||||
|
|
||||||
constructor(viewNode: LViewNode, template: EmbeddedTemplate<T>, context: T) {
|
|
||||||
super(viewNode.data, context);
|
|
||||||
this._lViewNode = viewNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
destroy(): void {
|
|
||||||
if (this._viewContainerRef && viewAttached(this._view)) {
|
|
||||||
this._viewContainerRef.detach(this._viewContainerRef.indexOf(this));
|
|
||||||
this._viewContainerRef = null;
|
|
||||||
}
|
|
||||||
super.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
attachToViewContainerRef(vcRef: viewEngine_ViewContainerRef) { this._viewContainerRef = vcRef; }
|
|
||||||
}
|
|
||||||
>>>>>>> fixup! fix(ivy): flatten template fns for nested views
|
|
||||||
|
|
|
@ -17,9 +17,6 @@
|
||||||
{
|
{
|
||||||
"name": "ChangeDetectionStrategy"
|
"name": "ChangeDetectionStrategy"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "DECLARATION_VIEW"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "DIRECTIVES"
|
"name": "DIRECTIVES"
|
||||||
},
|
},
|
||||||
|
@ -101,9 +98,6 @@
|
||||||
{
|
{
|
||||||
"name": "callHooks"
|
"name": "callHooks"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "callTemplateWithContexts"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "canInsertNativeNode"
|
"name": "canInsertNativeNode"
|
||||||
},
|
},
|
||||||
|
|
|
@ -326,9 +326,6 @@
|
||||||
{
|
{
|
||||||
"name": "callHooks"
|
"name": "callHooks"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "callTemplateWithContexts"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "canInsertNativeNode"
|
"name": "canInsertNativeNode"
|
||||||
},
|
},
|
||||||
|
@ -347,6 +344,9 @@
|
||||||
{
|
{
|
||||||
"name": "container"
|
"name": "container"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "contextViewData"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "createDirectivesAndLocals"
|
"name": "createDirectivesAndLocals"
|
||||||
},
|
},
|
||||||
|
@ -692,6 +692,9 @@
|
||||||
{
|
{
|
||||||
"name": "namespaceHTML"
|
"name": "namespaceHTML"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "nextContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pointers"
|
"name": "pointers"
|
||||||
},
|
},
|
||||||
|
@ -725,9 +728,6 @@
|
||||||
{
|
{
|
||||||
"name": "readElementValue"
|
"name": "readElementValue"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "reference"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "refreshChildComponents"
|
"name": "refreshChildComponents"
|
||||||
},
|
},
|
||||||
|
@ -869,6 +869,9 @@
|
||||||
{
|
{
|
||||||
"name": "walkLNodeTree"
|
"name": "walkLNodeTree"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "walkUpViews"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "wrapListenerWithDirtyAndDefault"
|
"name": "wrapListenerWithDirtyAndDefault"
|
||||||
},
|
},
|
||||||
|
|
|
@ -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, text, textBinding} from '../../src/render3/instructions';
|
import {bind, container, elementEnd, elementProperty, elementStart, interpolation1, interpolation2, interpolation3, interpolationV, listener, load, nextContext, 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';
|
||||||
|
@ -20,6 +20,18 @@ describe('@angular/common integration', () => {
|
||||||
|
|
||||||
describe('NgForOf', () => {
|
describe('NgForOf', () => {
|
||||||
it('should update a loop', () => {
|
it('should update a loop', () => {
|
||||||
|
function liTemplate(rf: RenderFlags, ctx: NgForOfContext<string>) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'li');
|
||||||
|
{ text(1); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const item = ctx.$implicit;
|
||||||
|
textBinding(1, bind(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MyApp {
|
class MyApp {
|
||||||
items: string[] = ['first', 'second'];
|
items: string[] = ['first', 'second'];
|
||||||
|
|
||||||
|
@ -30,25 +42,14 @@ describe('@angular/common integration', () => {
|
||||||
// <ul>
|
// <ul>
|
||||||
// <li *ngFor="let item of items">{{item}}</li>
|
// <li *ngFor="let item of items">{{item}}</li>
|
||||||
// </ul>
|
// </ul>
|
||||||
template: (rf: RenderFlags, myApp: MyApp) => {
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'ul');
|
elementStart(0, 'ul');
|
||||||
{ container(1, liTemplate, undefined, ['ngForOf', '']); }
|
{ container(1, liTemplate, undefined, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
elementProperty(1, 'ngForOf', bind(myApp.items));
|
elementProperty(1, 'ngForOf', bind(ctx.items));
|
||||||
}
|
|
||||||
|
|
||||||
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>, parent: MyApp) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'li');
|
|
||||||
{ text(1); }
|
|
||||||
elementEnd();
|
|
||||||
}
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
|
||||||
textBinding(1, bind(row.$implicit));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
directives: () => [NgForOf]
|
directives: () => [NgForOf]
|
||||||
|
@ -79,6 +80,17 @@ describe('@angular/common integration', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support ngForOf context variables', () => {
|
it('should support ngForOf context variables', () => {
|
||||||
|
function liTemplate(rf: RenderFlags, ctx: NgForOfContext<string>) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'li');
|
||||||
|
{ text(1); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const item = ctx.$implicit;
|
||||||
|
textBinding(1, interpolation3('', ctx.index, ' of ', ctx.count, ': ', item, ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MyApp {
|
class MyApp {
|
||||||
items: string[] = ['first', 'second'];
|
items: string[] = ['first', 'second'];
|
||||||
|
@ -88,29 +100,19 @@ describe('@angular/common integration', () => {
|
||||||
factory: () => new MyApp(),
|
factory: () => new MyApp(),
|
||||||
selectors: [['my-app']],
|
selectors: [['my-app']],
|
||||||
// <ul>
|
// <ul>
|
||||||
// <li *ngFor="let item of items">{{index}} of {{count}}: {{item}}</li>
|
// <li *ngFor="let item of items; index as index; count as count">{{index}} of
|
||||||
|
// {{count}}: {{item}}</li>
|
||||||
// </ul>
|
// </ul>
|
||||||
template: (rf: RenderFlags, myApp: MyApp) => {
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'ul');
|
elementStart(0, 'ul');
|
||||||
{ container(1, liTemplate, undefined, ['ngForOf', '']); }
|
{ container(1, liTemplate, undefined, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
elementProperty(1, 'ngForOf', bind(myApp.items));
|
elementProperty(1, 'ngForOf', bind(ctx.items));
|
||||||
}
|
}
|
||||||
|
|
||||||
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>, parent: MyApp) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'li');
|
|
||||||
{ text(1); }
|
|
||||||
elementEnd();
|
|
||||||
}
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
|
||||||
textBinding(
|
|
||||||
1, interpolation3('', row.index, ' of ', row.count, ': ', row.$implicit, ''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
directives: () => [NgForOf]
|
directives: () => [NgForOf]
|
||||||
});
|
});
|
||||||
|
@ -128,6 +130,18 @@ describe('@angular/common integration', () => {
|
||||||
|
|
||||||
it('should retain parent view listeners when the NgFor destroy views', () => {
|
it('should retain parent view listeners when the NgFor destroy views', () => {
|
||||||
|
|
||||||
|
function liTemplate(rf: RenderFlags, ctx: NgForOfContext<string>) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'li');
|
||||||
|
{ text(1); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const item = ctx.$implicit;
|
||||||
|
textBinding(1, interpolation1('', item, ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class MyApp {
|
class MyApp {
|
||||||
private _data: number[] = [1, 2, 3];
|
private _data: number[] = [1, 2, 3];
|
||||||
items: number[] = [];
|
items: number[] = [];
|
||||||
|
@ -148,11 +162,11 @@ describe('@angular/common integration', () => {
|
||||||
// <ul>
|
// <ul>
|
||||||
// <li *ngFor="let item of items">{{index}}</li>
|
// <li *ngFor="let item of items">{{index}}</li>
|
||||||
// </ul>
|
// </ul>
|
||||||
template: (rf: RenderFlags, myApp: MyApp) => {
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'button');
|
elementStart(0, 'button');
|
||||||
{
|
{
|
||||||
listener('click', function() { return myApp.toggle(); });
|
listener('click', function() { return ctx.toggle(); });
|
||||||
text(1, 'Toggle List');
|
text(1, 'Toggle List');
|
||||||
}
|
}
|
||||||
elementEnd();
|
elementEnd();
|
||||||
|
@ -161,19 +175,9 @@ describe('@angular/common integration', () => {
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
elementProperty(3, 'ngForOf', bind(myApp.items));
|
elementProperty(3, 'ngForOf', bind(ctx.items));
|
||||||
}
|
}
|
||||||
|
|
||||||
function liTemplate(rf1: RenderFlags, row: NgForOfContext<string>, parent: MyApp) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'li');
|
|
||||||
{ text(1); }
|
|
||||||
elementEnd();
|
|
||||||
}
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
|
||||||
textBinding(1, interpolation1('', row.$implicit, ''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
directives: () => [NgForOf]
|
directives: () => [NgForOf]
|
||||||
});
|
});
|
||||||
|
@ -205,7 +209,8 @@ describe('@angular/common integration', () => {
|
||||||
/**
|
/**
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li *ngFor="let row of items">
|
* <li *ngFor="let row of items">
|
||||||
* <span *ngFor="let cell of row.data">{{cell}} - {{ row.value }}</span>
|
* <span *ngFor="let cell of row.data">{{cell}} - {{ row.value }} - {{ items.length }}
|
||||||
|
* </span>
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
|
@ -216,14 +221,14 @@ describe('@angular/common integration', () => {
|
||||||
type: MyApp,
|
type: MyApp,
|
||||||
factory: () => new MyApp(),
|
factory: () => new MyApp(),
|
||||||
selectors: [['my-app']],
|
selectors: [['my-app']],
|
||||||
template: (rf: RenderFlags, myApp: MyApp) => {
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'ul');
|
elementStart(0, 'ul');
|
||||||
{ container(1, liTemplate, null, ['ngForOf', '']); }
|
{ container(1, liTemplate, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
elementProperty(1, 'ngForOf', bind(myApp.items));
|
elementProperty(1, 'ngForOf', bind(ctx.items));
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -231,27 +236,29 @@ describe('@angular/common integration', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function liTemplate(rf1: RenderFlags, row: any, myApp: MyApp) {
|
function liTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'li');
|
elementStart(0, 'li');
|
||||||
{ container(1, spanTemplate, null, ['ngForOf', '']); }
|
{ container(1, spanTemplate, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const r1 = row.$implicit as any;
|
const row = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(r1.data));
|
elementProperty(1, 'ngForOf', bind(row.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function spanTemplate(rf1: RenderFlags, cell: any, row: any, myApp: MyApp) {
|
function spanTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ text(1); }
|
{ text(1); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
textBinding(
|
const cell = ctx.$implicit;
|
||||||
1, interpolation2('', cell.$implicit, ' - ', (row.$implicit as any).value, ''));
|
const row = nextContext().$implicit as any;
|
||||||
|
const app = nextContext() as any;
|
||||||
|
textBinding(1, interpolation3('', cell, ' - ', row.value, ' - ', app.items.length, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,26 +268,198 @@ describe('@angular/common integration', () => {
|
||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html)
|
expect(fixture.html)
|
||||||
.toEqual(
|
.toEqual(
|
||||||
'<ul><li><span>1 - first</span><span>2 - first</span></li><li><span>3 - second</span><span>4 - second</span></li></ul>');
|
'<ul><li><span>1 - first - 2</span><span>2 - first - 2</span></li><li><span>3 - second - 2</span><span>4 - second - 2</span></li></ul>');
|
||||||
|
|
||||||
// Remove the last item
|
// Remove the last item
|
||||||
fixture.component.items.length = 1;
|
fixture.component.items.length = 1;
|
||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html)
|
expect(fixture.html)
|
||||||
.toEqual('<ul><li><span>1 - first</span><span>2 - first</span></li></ul>');
|
.toEqual('<ul><li><span>1 - first - 1</span><span>2 - first - 1</span></li></ul>');
|
||||||
|
|
||||||
// Change an item
|
// Change an item
|
||||||
fixture.component.items[0].data[0] = 'one';
|
fixture.component.items[0].data[0] = 'one';
|
||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html)
|
expect(fixture.html)
|
||||||
.toEqual('<ul><li><span>one - first</span><span>2 - first</span></li></ul>');
|
.toEqual('<ul><li><span>one - first - 1</span><span>2 - first - 1</span></li></ul>');
|
||||||
|
|
||||||
// Add an item
|
// Add an item
|
||||||
fixture.component.items[1] = {data: ['three', '4'], value: 'third'};
|
fixture.component.items[1] = {data: ['three', '4'], value: 'third'};
|
||||||
fixture.update();
|
fixture.update();
|
||||||
expect(fixture.html)
|
expect(fixture.html)
|
||||||
.toEqual(
|
.toEqual(
|
||||||
'<ul><li><span>one - first</span><span>2 - first</span></li><li><span>three - third</span><span>4 - third</span></li></ul>');
|
'<ul><li><span>one - first - 2</span><span>2 - first - 2</span></li><li><span>three - third - 2</span><span>4 - third - 2</span></li></ul>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support multiple levels of embedded templates with listeners', () => {
|
||||||
|
/**
|
||||||
|
* <div *ngFor="let row of items">
|
||||||
|
* <p *ngFor="let cell of row.data">
|
||||||
|
* <span (click)="onClick(row.value, name)"></span>
|
||||||
|
* {{ row.value }} - {{ name }}
|
||||||
|
* </p>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
class MyApp {
|
||||||
|
items: any[] = [{data: ['1'], value: 'first'}];
|
||||||
|
name = 'app';
|
||||||
|
events: string[] = [];
|
||||||
|
|
||||||
|
onClick(value: string, name: string) { this.events.push(value, name); }
|
||||||
|
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: MyApp,
|
||||||
|
factory: () => new MyApp(),
|
||||||
|
selectors: [['my-app']],
|
||||||
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
container(0, divTemplate, null, ['ngForOf', '']);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementProperty(0, 'ngForOf', bind(ctx.items));
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
directives: () => [NgForOf]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function divTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
{ container(1, pTemplate, null, ['ngForOf', '']); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const row = ctx.$implicit as any;
|
||||||
|
elementProperty(1, 'ngForOf', bind(row.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
const row = nextContext().$implicit as any;
|
||||||
|
const app = nextContext();
|
||||||
|
elementStart(0, 'p');
|
||||||
|
{
|
||||||
|
elementStart(1, 'span');
|
||||||
|
{
|
||||||
|
listener('click', () => { app.onClick(row.value, app.name); });
|
||||||
|
}
|
||||||
|
elementEnd();
|
||||||
|
text(2);
|
||||||
|
}
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const row = nextContext().$implicit as any;
|
||||||
|
const app = nextContext() as any;
|
||||||
|
textBinding(2, interpolation2('', row.value, ' - ', app.name, ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(MyApp);
|
||||||
|
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual('<div><p><span></span>first - app</p></div>');
|
||||||
|
|
||||||
|
const span = fixture.hostElement.querySelector('span') as any;
|
||||||
|
span.click();
|
||||||
|
expect(fixture.component.events).toEqual(['first', 'app']);
|
||||||
|
|
||||||
|
fixture.component.name = 'new name';
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual('<div><p><span></span>first - new name</p></div>');
|
||||||
|
|
||||||
|
span.click();
|
||||||
|
expect(fixture.component.events).toEqual(['first', 'app', 'first', 'new name']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support skipping contexts', () => {
|
||||||
|
/**
|
||||||
|
* <div *ngFor="let row of items">
|
||||||
|
* <div *ngFor="let cell of row">
|
||||||
|
* <span *ngFor="let span of cell.data">
|
||||||
|
* {{ cell.value }} - {{ name }}
|
||||||
|
* </span>
|
||||||
|
* </div>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
class MyApp {
|
||||||
|
name = 'app';
|
||||||
|
items: any[] = [
|
||||||
|
[
|
||||||
|
// row
|
||||||
|
{value: 'one', data: ['1', '2']} // cell
|
||||||
|
],
|
||||||
|
[{value: 'two', data: ['3', '4']}]
|
||||||
|
];
|
||||||
|
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: MyApp,
|
||||||
|
factory: () => new MyApp(),
|
||||||
|
selectors: [['my-app']],
|
||||||
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
container(0, divTemplate, null, ['ngForOf', '']);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementProperty(0, 'ngForOf', bind(ctx.items));
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
directives: () => [NgForOf]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function divTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
{ container(1, innerDivTemplate, null, ['ngForOf', '']); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const row = ctx.$implicit as any;
|
||||||
|
elementProperty(1, 'ngForOf', bind(row));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function innerDivTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
{ container(1, spanTemplate, null, ['ngForOf', '']); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const cell = ctx.$implicit as any;
|
||||||
|
elementProperty(1, 'ngForOf', bind(cell.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function spanTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'span');
|
||||||
|
{ text(1); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const cell = nextContext().$implicit as any;
|
||||||
|
const app = nextContext(2) as any;
|
||||||
|
textBinding(1, interpolation2('', cell.value, ' - ', app.name, ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(MyApp);
|
||||||
|
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
`<div><div><span>one - app</span><span>one - app</span></div></div><div><div><span>two - app</span><span>two - app</span></div></div>`);
|
||||||
|
|
||||||
|
fixture.component.name = 'other';
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html)
|
||||||
|
.toEqual(
|
||||||
|
`<div><div><span>one - other</span><span>one - other</span></div></div><div><div><span>two - other</span><span>two - other</span></div></div>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support context for 9+ levels of embedded templates', () => {
|
it('should support context for 9+ levels of embedded templates', () => {
|
||||||
|
@ -385,12 +564,12 @@ describe('@angular/common integration', () => {
|
||||||
type: MyApp,
|
type: MyApp,
|
||||||
factory: () => new MyApp(),
|
factory: () => new MyApp(),
|
||||||
selectors: [['my-app']],
|
selectors: [['my-app']],
|
||||||
template: (rf: RenderFlags, myApp: MyApp) => {
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
container(0, itemTemplate0, null, ['ngForOf', '']);
|
container(0, itemTemplate0, null, ['ngForOf', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
elementProperty(0, 'ngForOf', bind(myApp.items));
|
elementProperty(0, 'ngForOf', bind(ctx.items));
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -398,127 +577,124 @@ describe('@angular/common integration', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate0(rf1: RenderFlags, item0: any, myApp: MyApp) {
|
function itemTemplate0(rf: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ container(1, itemTemplate1, null, ['ngForOf', '']); }
|
{ container(1, itemTemplate1, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const item = item0.$implicit as any;
|
const item0 = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(item.data));
|
elementProperty(1, 'ngForOf', bind(item0.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate1(rf1: RenderFlags, item1: any, item0: any, myApp: MyApp) {
|
function itemTemplate1(rf: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ container(1, itemTemplate2, null, ['ngForOf', '']); }
|
{ container(1, itemTemplate2, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const item = item1.$implicit as any;
|
const item1 = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(item.data));
|
elementProperty(1, 'ngForOf', bind(item1.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate2(rf1: RenderFlags, item2: any, item1: any, item0: any, myApp: MyApp) {
|
function itemTemplate2(rf: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ container(1, itemTemplate3, null, ['ngForOf', '']); }
|
{ container(1, itemTemplate3, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const item = item2.$implicit as any;
|
const item2 = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(item.data));
|
elementProperty(1, 'ngForOf', bind(item2.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate3(
|
function itemTemplate3(rf: RenderFlags, ctx: any) {
|
||||||
rf1: RenderFlags, item3: any, item2: any, item1: any, item0: any, myApp: MyApp) {
|
if (rf & RenderFlags.Create) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ container(1, itemTemplate4, null, ['ngForOf', '']); }
|
{ container(1, itemTemplate4, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const item = item3.$implicit as any;
|
const item3 = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(item.data));
|
elementProperty(1, 'ngForOf', bind(item3.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate4(
|
function itemTemplate4(rf: RenderFlags, ctx: any) {
|
||||||
rf1: RenderFlags, item4: any, item3: any, item2: any, item1: any, item0: any,
|
if (rf & RenderFlags.Create) {
|
||||||
myApp: MyApp) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ container(1, itemTemplate5, null, ['ngForOf', '']); }
|
{ container(1, itemTemplate5, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const item = item4.$implicit as any;
|
const item4 = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(item.data));
|
elementProperty(1, 'ngForOf', bind(item4.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate5(
|
function itemTemplate5(rf: RenderFlags, ctx: any) {
|
||||||
rf1: RenderFlags, item5: any, item4: any, item3: any, item2: any, item1: any, item0: any,
|
if (rf & RenderFlags.Create) {
|
||||||
myApp: MyApp) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ container(1, itemTemplate6, null, ['ngForOf', '']); }
|
{ container(1, itemTemplate6, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const item = item5.$implicit as any;
|
const item5 = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(item.data));
|
elementProperty(1, 'ngForOf', bind(item5.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate6(
|
function itemTemplate6(rf: RenderFlags, ctx: any) {
|
||||||
rf1: RenderFlags, item6: any, item5: any, item4: any, item3: any, item2: any, item1: any,
|
if (rf & RenderFlags.Create) {
|
||||||
item0: any, myApp: MyApp) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ container(1, itemTemplate7, null, ['ngForOf', '']); }
|
{ container(1, itemTemplate7, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const item = item6.$implicit as any;
|
const item6 = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(item.data));
|
elementProperty(1, 'ngForOf', bind(item6.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate7(
|
function itemTemplate7(rf: RenderFlags, ctx: any) {
|
||||||
rf1: RenderFlags, item7: any, item6: any, item5: any, item4: any, item3: any, item2: any,
|
if (rf & RenderFlags.Create) {
|
||||||
item1: any, item0: any, myApp: MyApp) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ container(1, itemTemplate8, null, ['ngForOf', '']); }
|
{ container(1, itemTemplate8, null, ['ngForOf', '']); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const item = item7.$implicit as any;
|
const item7 = ctx.$implicit as any;
|
||||||
elementProperty(1, 'ngForOf', bind(item.data));
|
elementProperty(1, 'ngForOf', bind(item7.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function itemTemplate8(
|
function itemTemplate8(rf: RenderFlags, ctx: any) {
|
||||||
rf1: RenderFlags, item8: any, item7: any, item6: any, item5: any, item4: any, item3: any,
|
if (rf & RenderFlags.Create) {
|
||||||
item2: any, item1: any, item0: any, myApp: MyApp) {
|
|
||||||
if (rf1 & RenderFlags.Create) {
|
|
||||||
elementStart(0, 'span');
|
elementStart(0, 'span');
|
||||||
{ text(1); }
|
{ text(1); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
textBinding(
|
const value = ctx.$implicit;
|
||||||
1, interpolationV([
|
const item7 = nextContext().$implicit;
|
||||||
'', item8.$implicit, '.', item7.$implicit.value, '.', item6.$implicit.value,
|
const item6 = nextContext().$implicit;
|
||||||
'.', item5.$implicit.value, '.', item4.$implicit.value, '.', item3.$implicit.value,
|
const item5 = nextContext().$implicit;
|
||||||
'.', item2.$implicit.value, '.', item1.$implicit.value, '.', item0.$implicit.value,
|
const item4 = nextContext().$implicit;
|
||||||
'.', myApp.value, ''
|
const item3 = nextContext().$implicit;
|
||||||
|
const item2 = nextContext().$implicit;
|
||||||
|
const item1 = nextContext().$implicit;
|
||||||
|
const item0 = nextContext().$implicit;
|
||||||
|
const myApp = nextContext();
|
||||||
|
textBinding(1, interpolationV([
|
||||||
|
'', value, '.', item7.value, '.', item6.value, '.', item5.value,
|
||||||
|
'.', item4.value, '.', item3.value, '.', item2.value, '.', item1.value,
|
||||||
|
'.', item0.value, '.', myApp.value, ''
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -554,14 +730,14 @@ describe('@angular/common integration', () => {
|
||||||
* <div *ngIf="showing">{{ valueOne }}</div>
|
* <div *ngIf="showing">{{ valueOne }}</div>
|
||||||
* <div *ngIf="showing">{{ valueTwo }}</div>
|
* <div *ngIf="showing">{{ valueTwo }}</div>
|
||||||
*/
|
*/
|
||||||
template: (rf: RenderFlags, myApp: MyApp) => {
|
template: (rf: RenderFlags, ctx: MyApp) => {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
container(0, templateOne, undefined, ['ngIf', '']);
|
container(0, templateOne, undefined, ['ngIf', '']);
|
||||||
container(1, templateTwo, undefined, ['ngIf', '']);
|
container(1, templateTwo, undefined, ['ngIf', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
elementProperty(0, 'ngIf', bind(myApp.showing));
|
elementProperty(0, 'ngIf', bind(ctx.showing));
|
||||||
elementProperty(1, 'ngIf', bind(myApp.showing));
|
elementProperty(1, 'ngIf', bind(ctx.showing));
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -569,24 +745,26 @@ describe('@angular/common integration', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function templateOne(rf: RenderFlags, ctx: any, myApp: MyApp) {
|
function templateOne(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div');
|
elementStart(0, 'div');
|
||||||
{ text(1); }
|
{ text(1); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
const myApp = nextContext();
|
||||||
textBinding(1, bind(myApp.valueOne));
|
textBinding(1, bind(myApp.valueOne));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function templateTwo(rf: RenderFlags, ctx: any, myApp: MyApp) {
|
function templateTwo(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div');
|
elementStart(0, 'div');
|
||||||
{ text(1); }
|
{ text(1); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
const myApp = nextContext();
|
||||||
textBinding(1, bind(myApp.valueTwo));
|
textBinding(1, bind(myApp.valueTwo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -600,6 +778,83 @@ describe('@angular/common integration', () => {
|
||||||
expect(fixture.html).toEqual('<div>$$one$$</div><div>$$two$$</div>');
|
expect(fixture.html).toEqual('<div>$$one$$</div><div>$$two$$</div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should handle nested ngIfs with no intermediate context vars', () => {
|
||||||
|
/**
|
||||||
|
* <div *ngIf="showing">
|
||||||
|
* <div *ngIf="outerShowing">
|
||||||
|
* <div *ngIf="innerShowing'>
|
||||||
|
* {{ name }}
|
||||||
|
* </div>
|
||||||
|
* </div>
|
||||||
|
* </div>
|
||||||
|
*/
|
||||||
|
class AppComponent {
|
||||||
|
showing = true;
|
||||||
|
outerShowing = true;
|
||||||
|
innerShowing = true;
|
||||||
|
name = 'App name';
|
||||||
|
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: AppComponent,
|
||||||
|
factory: () => new AppComponent(),
|
||||||
|
selectors: [['my-app']],
|
||||||
|
template: (rf: RenderFlags, ctx: AppComponent) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
container(0, divTemplate, undefined, ['ngIf', '']);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementProperty(0, 'ngIf', bind(ctx.showing));
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
directives: () => [NgIf]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function divTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
{ container(1, outerDivTemplate, undefined, ['ngIf', '']); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const app = nextContext();
|
||||||
|
elementProperty(1, 'ngIf', bind(app.outerShowing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function outerDivTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
{ container(1, innerDivTemplate, undefined, ['ngIf', '']); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const app = nextContext(2);
|
||||||
|
elementProperty(1, 'ngIf', bind(app.innerShowing));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function innerDivTemplate(rf: RenderFlags, ctx: any) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
elementStart(0, 'div');
|
||||||
|
{ text(1); }
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
const app = nextContext(3);
|
||||||
|
textBinding(1, bind(app.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(AppComponent);
|
||||||
|
expect(fixture.html).toEqual(`<div><div><div>App name</div></div></div>`);
|
||||||
|
|
||||||
|
fixture.component.name = 'Other name';
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual(`<div><div><div>Other name</div></div></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('NgTemplateOutlet', () => {
|
describe('NgTemplateOutlet', () => {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
import {DoCheck, Input, TemplateRef, ViewContainerRef, ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
|
import {DoCheck, Input, TemplateRef, ViewContainerRef, ViewEncapsulation, createInjector, defineInjectable, defineInjector} from '../../src/core';
|
||||||
import {getRenderedText} from '../../src/render3/component';
|
import {getRenderedText} from '../../src/render3/component';
|
||||||
import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty} from '../../src/render3/index';
|
import {AttributeMarker, ComponentFactory, LifecycleHooksFeature, defineComponent, directiveInject, markDirty} from '../../src/render3/index';
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, text, textBinding, tick} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, nextContext, text, textBinding, tick} from '../../src/render3/instructions';
|
||||||
import {ComponentDefInternal, DirectiveDefInternal, RenderFlags} from '../../src/render3/interfaces/definition';
|
import {ComponentDefInternal, DirectiveDefInternal, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {createRendererType2} from '../../src/view/index';
|
import {createRendererType2} from '../../src/view/index';
|
||||||
|
|
||||||
|
@ -394,22 +394,24 @@ describe('recursive components', () => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function IfTemplate(rf1: RenderFlags, left: any, parent: NgIfTree) {
|
function IfTemplate(rf: RenderFlags, left: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'ng-if-tree');
|
elementStart(0, 'ng-if-tree');
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
const parent = nextContext();
|
||||||
elementProperty(0, 'data', bind(parent.data.left));
|
elementProperty(0, 'data', bind(parent.data.left));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function IfTemplate2(rf1: RenderFlags, right: any, parent: NgIfTree) {
|
function IfTemplate2(rf: RenderFlags, right: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'ng-if-tree');
|
elementStart(0, 'ng-if-tree');
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
const parent = nextContext();
|
||||||
elementProperty(0, 'data', bind(parent.data.right));
|
elementProperty(0, 'data', bind(parent.data.right));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -825,7 +825,7 @@ describe('content projection', () => {
|
||||||
|
|
||||||
}, [NgIf]);
|
}, [NgIf]);
|
||||||
|
|
||||||
function IfTemplate(rf1: RenderFlags, ctx: any, child: any) {
|
function IfTemplate(rf1: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf1 & RenderFlags.Create) {
|
||||||
projectionDef();
|
projectionDef();
|
||||||
projection(0);
|
projection(0);
|
||||||
|
@ -890,7 +890,7 @@ describe('content projection', () => {
|
||||||
|
|
||||||
}, [NgIf]);
|
}, [NgIf]);
|
||||||
|
|
||||||
function IfTemplate(rf: RenderFlags, ctx: any, child: any) {
|
function IfTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
projectionDef();
|
projectionDef();
|
||||||
projection(0);
|
projection(0);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {RenderFlags} from '@angular/core/src/render3/interfaces/definition';
|
||||||
import {defineComponent} from '../../src/render3/definition';
|
import {defineComponent} from '../../src/render3/definition';
|
||||||
import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di';
|
import {bloomAdd, bloomFindPossibleInjector, getOrCreateNodeInjector, injectAttribute} from '../../src/render3/di';
|
||||||
import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
|
import {NgOnChangesFeature, PublicFeature, defineDirective, directiveInject, injectChangeDetectorRef, injectElementRef, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, load, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, createLNode, createLViewData, createTView, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, enterView, interpolation2, leaveView, projection, projectionDef, reference, text, textBinding} from '../../src/render3/instructions';
|
||||||
import {LInjector} from '../../src/render3/interfaces/injector';
|
import {LInjector} from '../../src/render3/interfaces/injector';
|
||||||
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
|
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
|
||||||
import {LViewFlags} from '../../src/render3/interfaces/view';
|
import {LViewFlags} from '../../src/render3/interfaces/view';
|
||||||
|
@ -40,9 +40,8 @@ describe('di', () => {
|
||||||
{ text(2); }
|
{ text(2); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
let tmp: any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
tmp = load(1);
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, bind(tmp.value));
|
textBinding(2, bind(tmp.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,9 +104,8 @@ describe('di', () => {
|
||||||
}
|
}
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
let tmp: any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
tmp = load(2);
|
const tmp = reference(2) as any;
|
||||||
textBinding(3, bind(tmp.value));
|
textBinding(3, bind(tmp.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -815,11 +813,9 @@ describe('di', () => {
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
let tmp1: any;
|
|
||||||
let tmp2: any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
tmp1 = load(1);
|
const tmp1 = reference(1) as any;
|
||||||
tmp2 = load(2);
|
const tmp2 = reference(2) as any;
|
||||||
textBinding(3, interpolation2('', tmp2.value, '-', tmp1.value, ''));
|
textBinding(3, interpolation2('', tmp2.value, '-', tmp1.value, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -870,11 +866,9 @@ describe('di', () => {
|
||||||
}, undefined, ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']);
|
}, undefined, ['dir', '', 'dirSame', ''], ['dir', 'dir', 'dirSame', 'dirSame']);
|
||||||
text(3);
|
text(3);
|
||||||
}
|
}
|
||||||
let tmp1: any;
|
|
||||||
let tmp2: any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
tmp1 = load(1);
|
const tmp1 = reference(1) as any;
|
||||||
tmp2 = load(2);
|
const tmp2 = reference(2) as any;
|
||||||
textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, ''));
|
textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -925,11 +919,9 @@ describe('di', () => {
|
||||||
{ text(3); }
|
{ text(3); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
let tmp1: any;
|
|
||||||
let tmp2: any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
tmp1 = load(1);
|
const tmp1 = reference(1) as any;
|
||||||
tmp2 = load(2);
|
const tmp2 = reference(2) as any;
|
||||||
textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, ''));
|
textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1016,9 +1008,8 @@ describe('di', () => {
|
||||||
element(0, 'my-comp', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
|
element(0, 'my-comp', ['dir', '', 'dirSame', ''], ['dir', 'dir']);
|
||||||
text(2);
|
text(2);
|
||||||
}
|
}
|
||||||
let tmp: any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
tmp = load(1);
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, bind(tmp.value));
|
textBinding(2, bind(tmp.value));
|
||||||
}
|
}
|
||||||
}, directives);
|
}, directives);
|
||||||
|
@ -1048,9 +1039,8 @@ describe('di', () => {
|
||||||
{ text(2); }
|
{ text(2); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
let tmp: any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
tmp = load(1);
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, bind(tmp.value));
|
textBinding(2, bind(tmp.value));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1087,8 +1077,8 @@ describe('di', () => {
|
||||||
elementEnd();
|
elementEnd();
|
||||||
text(3);
|
text(3);
|
||||||
}
|
}
|
||||||
const tmp = load(2) as any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
const tmp = reference(2) as any;
|
||||||
textBinding(3, bind(tmp.value));
|
textBinding(3, bind(tmp.value));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1134,9 +1124,8 @@ describe('di', () => {
|
||||||
{ text(2); }
|
{ text(2); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
let tmp: any;
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf1 & RenderFlags.Update) {
|
||||||
tmp = load(1);
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, bind(tmp.value));
|
textBinding(2, bind(tmp.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1183,9 +1172,8 @@ describe('di', () => {
|
||||||
{ text(2); }
|
{ text(2); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
let tmp: any;
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf1 & RenderFlags.Update) {
|
||||||
tmp = load(1);
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, bind(tmp.value));
|
textBinding(2, bind(tmp.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1382,11 +1370,9 @@ describe('di', () => {
|
||||||
{ text(3); }
|
{ text(3); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
let tmp1: any;
|
|
||||||
let tmp2: any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
tmp1 = load(1);
|
const tmp1 = reference(1) as any;
|
||||||
tmp2 = load(2);
|
const tmp2 = reference(2) as any;
|
||||||
textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, ''));
|
textBinding(3, interpolation2('', tmp1.value, '-', tmp2.value, ''));
|
||||||
}
|
}
|
||||||
embeddedViewEnd();
|
embeddedViewEnd();
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AttributeMarker, defineComponent, defineDirective} from '../../src/render3/index';
|
import {AttributeMarker, defineComponent, defineDirective} from '../../src/render3/index';
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementEnd, elementProperty, elementStart, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation2, load, reference, text, textBinding} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementAttribute, elementClassProp, elementEnd, elementProperty, elementStart, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation2, nextContext, reference, text, textBinding} from '../../src/render3/instructions';
|
||||||
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
|
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
|
||||||
import {NgIf} from './common_with_def';
|
import {NgIf} from './common_with_def';
|
||||||
|
@ -23,7 +23,7 @@ describe('exports', () => {
|
||||||
text(2);
|
text(2);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(1) as any;
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, tmp.value);
|
textBinding(2, tmp.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ describe('exports', () => {
|
||||||
text(2);
|
text(2);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(1) as any;
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, tmp.name);
|
textBinding(2, tmp.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ describe('exports', () => {
|
||||||
element(2, 'div', ['myDir', '']);
|
element(2, 'div', ['myDir', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(1) as any;
|
const tmp = reference(1) as any;
|
||||||
elementProperty(2, 'myDir', bind(tmp));
|
elementProperty(2, 'myDir', bind(tmp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ describe('exports', () => {
|
||||||
text(2);
|
text(2);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(1) as any;
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, tmp.name);
|
textBinding(2, tmp.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ describe('exports', () => {
|
||||||
element(1, 'input', ['value', 'one'], ['myInput', '']);
|
element(1, 'input', ['value', 'one'], ['myInput', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(2) as any;
|
const tmp = reference(2) as any;
|
||||||
textBinding(0, bind(tmp.value));
|
textBinding(0, bind(tmp.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ describe('exports', () => {
|
||||||
element(1, 'input', ['value', 'one'], ['myInput', '']);
|
element(1, 'input', ['value', 'one'], ['myInput', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(2) as any;
|
const tmp = reference(2) as any;
|
||||||
elementProperty(0, 'title', bind(tmp.value));
|
elementProperty(0, 'title', bind(tmp.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,7 +186,7 @@ describe('exports', () => {
|
||||||
element(1, 'input', ['value', 'one'], ['myInput', '']);
|
element(1, 'input', ['value', 'one'], ['myInput', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(2) as any;
|
const tmp = reference(2) as any;
|
||||||
elementAttribute(0, 'aria-label', bind(tmp.value));
|
elementAttribute(0, 'aria-label', bind(tmp.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ describe('exports', () => {
|
||||||
element(1, 'input', ['type', 'checkbox', 'checked', 'true'], ['myInput', '']);
|
element(1, 'input', ['type', 'checkbox', 'checked', 'true'], ['myInput', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(2) as any;
|
const tmp = reference(2) as any;
|
||||||
elementClassProp(0, 0, tmp.checked);
|
elementClassProp(0, 0, tmp.checked);
|
||||||
elementStylingApply(0);
|
elementStylingApply(0);
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ describe('exports', () => {
|
||||||
element(1, 'comp', null, ['myComp', '']);
|
element(1, 'comp', null, ['myComp', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(2) as any;
|
const tmp = reference(2) as any;
|
||||||
elementProperty(0, 'myDir', bind(tmp));
|
elementProperty(0, 'myDir', bind(tmp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -271,8 +271,8 @@ describe('exports', () => {
|
||||||
element(4, 'input', ['value', 'one'], ['myInput', '']);
|
element(4, 'input', ['value', 'one'], ['myInput', '']);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp1 = load(3) as any;
|
const tmp1 = reference(3) as any;
|
||||||
const tmp2 = load(5) as any;
|
const tmp2 = reference(5) as any;
|
||||||
textBinding(0, bind(tmp2.value));
|
textBinding(0, bind(tmp2.value));
|
||||||
textBinding(1, bind(tmp1.name));
|
textBinding(1, bind(tmp1.name));
|
||||||
}
|
}
|
||||||
|
@ -314,7 +314,7 @@ describe('exports', () => {
|
||||||
element(1, 'input', ['value', 'one'], ['myInput', '']);
|
element(1, 'input', ['value', 'one'], ['myInput', '']);
|
||||||
}
|
}
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf1 & RenderFlags.Update) {
|
||||||
const tmp = load(2) as any;
|
const tmp = reference(2) as any;
|
||||||
textBinding(0, bind(tmp.value));
|
textBinding(0, bind(tmp.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,7 @@ describe('exports', () => {
|
||||||
}
|
}
|
||||||
}, [NgIf]);
|
}, [NgIf]);
|
||||||
|
|
||||||
function outerTemplate(rf: RenderFlags, outer: any, app: any) {
|
function outerTemplate(rf: RenderFlags, outer: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div');
|
elementStart(0, 'div');
|
||||||
{
|
{
|
||||||
|
@ -367,23 +367,26 @@ describe('exports', () => {
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
const outerInput = reference(1, 1) as any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
const app = nextContext();
|
||||||
|
const outerInput = reference(1) as any;
|
||||||
textBinding(1, bind(outerInput.value));
|
textBinding(1, bind(outerInput.value));
|
||||||
elementProperty(4, 'ngIf', bind(app.inner));
|
elementProperty(4, 'ngIf', bind(app.inner));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function innerTemplate(rf: RenderFlags, inner: any, outer: any, app: any) {
|
function innerTemplate(rf: RenderFlags, inner: any) {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div');
|
elementStart(0, 'div');
|
||||||
{ text(1); }
|
{ text(1); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
const outerInput = reference(2, 1) as any;
|
|
||||||
const innerInput = reference(1, 3) as any;
|
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
nextContext();
|
||||||
|
const innerInput = reference(3) as any;
|
||||||
|
nextContext();
|
||||||
|
const outerInput = reference(1) as any;
|
||||||
textBinding(1, interpolation2('', outerInput.value, ' - ', innerInput.value, ''));
|
textBinding(1, interpolation2('', outerInput.value, ' - ', innerInput.value, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import {EventEmitter} from '@angular/core';
|
import {EventEmitter} from '@angular/core';
|
||||||
|
|
||||||
import {defineComponent, defineDirective, tick} from '../../src/render3/index';
|
import {defineComponent, defineDirective, tick} from '../../src/render3/index';
|
||||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, load, loadDirective, text, textBinding} from '../../src/render3/instructions';
|
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, listener, loadDirective, reference, text, textBinding} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
|
||||||
import {ComponentFixture, renderToHtml} from './render_util';
|
import {ComponentFixture, renderToHtml} from './render_util';
|
||||||
|
@ -554,7 +554,7 @@ describe('elementProperty', () => {
|
||||||
text(2);
|
text(2);
|
||||||
}
|
}
|
||||||
if (rf & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
const tmp = load(1) as any;
|
const tmp = reference(1) as any;
|
||||||
textBinding(2, bind(tmp.role));
|
textBinding(2, bind(tmp.role));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import {Component, ComponentFactoryResolver, Directive, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
|
import {Component, ComponentFactoryResolver, Directive, EmbeddedViewRef, NgModuleRef, Pipe, PipeTransform, RendererFactory2, TemplateRef, ViewContainerRef, createInjector, defineInjector, ɵAPP_ROOT as APP_ROOT, ɵNgModuleDef as NgModuleDef} from '../../src/core';
|
||||||
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
|
import {getOrCreateNodeInjectorForNode, getOrCreateTemplateRef} from '../../src/render3/di';
|
||||||
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
|
import {AttributeMarker, NgOnChangesFeature, defineComponent, defineDirective, definePipe, injectComponentFactoryResolver, injectTemplateRef, injectViewContainerRef} from '../../src/render3/index';
|
||||||
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, load, loadDirective, projection, projectionDef, reserveSlots, text, textBinding} from '../../src/render3/instructions';
|
import {bind, container, containerRefreshEnd, containerRefreshStart, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation3, load, loadDirective, nextContext, projection, projectionDef, reserveSlots, text, textBinding} from '../../src/render3/instructions';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {NgModuleFactory} from '../../src/render3/ng_module_ref';
|
import {NgModuleFactory} from '../../src/render3/ng_module_ref';
|
||||||
import {pipe, pipeBind1} from '../../src/render3/pipe';
|
import {pipe, pipeBind1} from '../../src/render3/pipe';
|
||||||
|
@ -521,7 +521,7 @@ describe('ViewContainerRef', () => {
|
||||||
|
|
||||||
}, [Child]);
|
}, [Child]);
|
||||||
|
|
||||||
function fooTemplate(rf1: RenderFlags, ctx: any, parent: any) {
|
function fooTemplate(rf1: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf1 & RenderFlags.Create) {
|
||||||
elementStart(0, 'div');
|
elementStart(0, 'div');
|
||||||
{ text(1); }
|
{ text(1); }
|
||||||
|
@ -529,6 +529,7 @@ describe('ViewContainerRef', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf1 & RenderFlags.Update) {
|
||||||
|
const parent = nextContext();
|
||||||
textBinding(1, bind(parent.name));
|
textBinding(1, bind(parent.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,32 +613,34 @@ describe('ViewContainerRef', () => {
|
||||||
|
|
||||||
}, [LoopComp]);
|
}, [LoopComp]);
|
||||||
|
|
||||||
function rowTemplate(rf1: RenderFlags, row: any, parent: any) {
|
function rowTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
container(0, cellTemplate);
|
container(0, cellTemplate);
|
||||||
elementStart(1, 'loop-comp');
|
elementStart(1, 'loop-comp');
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
|
const row = ctx.$implicit as any;
|
||||||
// Hack until we have local refs for templates
|
// Hack until we have local refs for templates
|
||||||
const cellTemplateRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
|
const cellTemplateRef = getOrCreateTemplateRef(getOrCreateNodeInjectorForNode(load(0)));
|
||||||
elementProperty(1, 'tpl', bind(cellTemplateRef));
|
elementProperty(1, 'tpl', bind(cellTemplateRef));
|
||||||
elementProperty(1, 'rows', bind(row.$implicit.data));
|
elementProperty(1, 'rows', bind(row.data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cellTemplate(rf1: RenderFlags, cell: any, row: any, parent: any) {
|
function cellTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf1 & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
elementStart(0, 'div');
|
elementStart(0, 'div');
|
||||||
{ text(1); }
|
{ text(1); }
|
||||||
elementEnd();
|
elementEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rf1 & RenderFlags.Update) {
|
if (rf & RenderFlags.Update) {
|
||||||
textBinding(
|
const cell = ctx.$implicit as any;
|
||||||
1, interpolation3(
|
const row = nextContext().$implicit as any;
|
||||||
'', cell.$implicit, ' - ', row.$implicit.value, ' - ', parent.name, ''));
|
const parent = nextContext();
|
||||||
|
textBinding(1, interpolation3('', cell, ' - ', row.value, ' - ', parent.name, ''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue