parent
a181e8e7d8
commit
10217bb3bc
|
@ -317,7 +317,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "id", $r3$.ɵɵbind(ctx.id));
|
||||
$r3$.ɵɵproperty("id", ctx.id);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -354,6 +354,14 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
};
|
||||
|
||||
///////////////
|
||||
// TODO(FW-1273): The code generated below is adding extra parens, and we need to stop
|
||||
// generating those.
|
||||
//
|
||||
// For example:
|
||||
// `$r3$.ɵɵproperty("ternary", (ctx.cond ? $r3$.ɵɵpureFunction1(8, $c0$, ctx.a): $c1$));`
|
||||
///////////////
|
||||
|
||||
const $e0_attrs$ = [];
|
||||
const factory =
|
||||
'factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }';
|
||||
|
@ -365,10 +373,10 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "ternary", $r3$.ɵɵbind((ctx.cond ? $r3$.ɵɵpureFunction1(8, $c0$, ctx.a): $c1$)));
|
||||
$r3$.ɵɵelementProperty(0, "pipe", $r3$.ɵɵbind($r3$.ɵɵpipeBind3(1, 4, ctx.value, 1, 2)));
|
||||
$r3$.ɵɵelementProperty(0, "and", $r3$.ɵɵbind((ctx.cond && $r3$.ɵɵpureFunction1(10, $c0$, ctx.b))));
|
||||
$r3$.ɵɵelementProperty(0, "or", $r3$.ɵɵbind((ctx.cond || $r3$.ɵɵpureFunction1(12, $c0$, ctx.c))));
|
||||
$r3$.ɵɵproperty("ternary", (ctx.cond ? $r3$.ɵɵpureFunction1(8, $c0$, ctx.a): $c1$));
|
||||
$r3$.ɵɵproperty("pipe", $r3$.ɵɵpipeBind3(1, 4, ctx.value, 1, 2));
|
||||
$r3$.ɵɵproperty("and", (ctx.cond && $r3$.ɵɵpureFunction1(10, $c0$, ctx.b)));
|
||||
$r3$.ɵɵproperty("or", (ctx.cond || $r3$.ɵɵpureFunction1(12, $c0$, ctx.c)));
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -880,7 +888,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "names", $r3$.ɵɵbind($r3$.ɵɵpureFunction1(1, $e0_ff$, ctx.customName)));
|
||||
$r3$.ɵɵproperty("names", $r3$.ɵɵpureFunction1(1, $e0_ff$, ctx.customName));
|
||||
}
|
||||
},
|
||||
directives: [MyComp],
|
||||
|
@ -963,9 +971,8 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(
|
||||
0, "names",
|
||||
$r3$.ɵɵbind($r3$.ɵɵpureFunctionV(1, $e0_ff$, [ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8])));
|
||||
$r3$.ɵɵproperty("names",
|
||||
$r3$.ɵɵpureFunctionV(1, $e0_ff$, [ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8]));
|
||||
}
|
||||
},
|
||||
directives: [MyComp],
|
||||
|
@ -1028,7 +1035,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "config", $r3$.ɵɵbind($r3$.ɵɵpureFunction1(1, $e0_ff$, ctx.name)));
|
||||
$r3$.ɵɵproperty("config", $r3$.ɵɵpureFunction1(1, $e0_ff$, ctx.name));
|
||||
}
|
||||
},
|
||||
directives: [ObjectComp],
|
||||
|
@ -1097,9 +1104,9 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(
|
||||
0, "config",
|
||||
$r3$.ɵɵbind($r3$.ɵɵpureFunction2(5, $e0_ff_2$, ctx.name, $r3$.ɵɵpureFunction1(3, $e0_ff_1$, $r3$.ɵɵpureFunction1(1, $e0_ff$, ctx.duration)))));
|
||||
$r3$.ɵɵproperty(
|
||||
"config",
|
||||
$r3$.ɵɵpureFunction2(5, $e0_ff_2$, ctx.name, $r3$.ɵɵpureFunction1(3, $e0_ff_1$, $r3$.ɵɵpureFunction1(1, $e0_ff$, ctx.duration))));
|
||||
}
|
||||
},
|
||||
directives: [NestedComp],
|
||||
|
@ -1261,9 +1268,9 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
$r3$.ɵɵselect(1);
|
||||
$r3$.ɵɵelementProperty(1, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -2350,7 +2357,7 @@ describe('compiler compliance', () => {
|
|||
if (rf & 2) {
|
||||
const $app$ = $i0$.ɵɵnextContext();
|
||||
$r3$.ɵɵselect(3);
|
||||
$i0$.ɵɵelementProperty(3, "ngIf", $i0$.ɵɵbind($app$.showing));
|
||||
$i0$.ɵɵproperty("ngIf", $app$.showing);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2361,7 +2368,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "ngForOf", $i0$.ɵɵbind(ctx.items));
|
||||
$i0$.ɵɵproperty("ngForOf", ctx.items);
|
||||
}
|
||||
}`;
|
||||
|
||||
|
@ -2442,9 +2449,9 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "name", $r3$.ɵɵbind(ctx.name1));
|
||||
$r3$.ɵɵproperty("name", ctx.name1);
|
||||
$r3$.ɵɵselect(1);
|
||||
$r3$.ɵɵelementProperty(1, "name", $r3$.ɵɵbind(ctx.name2));
|
||||
$r3$.ɵɵproperty("name", ctx.name2);
|
||||
}
|
||||
},
|
||||
directives: [LifecycleComp],
|
||||
|
@ -2576,7 +2583,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(1);
|
||||
$r3$.ɵɵelementProperty(1,"forOf",$r3$.ɵɵbind(ctx.items));
|
||||
$r3$.ɵɵproperty("forOf", ctx.items);
|
||||
}
|
||||
},
|
||||
directives: function() { return [ForOfDirective]; },
|
||||
|
@ -2658,7 +2665,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(1);
|
||||
$r3$.ɵɵelementProperty(1, "forOf", $r3$.ɵɵbind(ctx.items));
|
||||
$r3$.ɵɵproperty("forOf", ctx.items);
|
||||
}
|
||||
},
|
||||
directives: function() { return [ForOfDirective]; },
|
||||
|
@ -2743,7 +2750,7 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵɵselect(2);
|
||||
$r3$.ɵɵtextBinding(2, $r3$.ɵɵinterpolation1("", IDENT.name, ""));
|
||||
$r3$.ɵɵselect(4);
|
||||
$r3$.ɵɵelementProperty(4, "forOf", $r3$.ɵɵbind(IDENT.infos));
|
||||
$r3$.ɵɵproperty("forOf", IDENT.infos);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2762,7 +2769,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(1);
|
||||
$r3$.ɵɵelementProperty(1, "forOf", $r3$.ɵɵbind(ctx.items));
|
||||
$r3$.ɵɵproperty("forOf", ctx.items);
|
||||
}
|
||||
},
|
||||
directives: function () { return [ForOfDirective]; },
|
||||
|
|
|
@ -82,7 +82,7 @@ describe('compiler compliance: bindings', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "title", $i0$.ɵɵbind($ctx$.title));
|
||||
$i0$.ɵɵproperty("title", $ctx$.title);
|
||||
}
|
||||
}`;
|
||||
const result = compile(files, angularFiles);
|
||||
|
@ -174,7 +174,7 @@ describe('compiler compliance: bindings', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "for", $i0$.ɵɵbind(ctx.forValue));
|
||||
$i0$.ɵɵproperty("for", ctx.forValue);
|
||||
}
|
||||
}`;
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ describe('compiler compliance: directives', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "someDirective", $r3$.ɵɵbind(true));
|
||||
$r3$.ɵɵproperty("someDirective", true);
|
||||
}
|
||||
},
|
||||
…
|
||||
|
@ -255,7 +255,7 @@ describe('compiler compliance: directives', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "ngIf", $r3$.ɵɵbind(ctx.showing));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.showing);
|
||||
}
|
||||
},
|
||||
…
|
||||
|
@ -303,7 +303,7 @@ describe('compiler compliance: directives', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "someDirective", $r3$.ɵɵbind(true));
|
||||
$r3$.ɵɵproperty("someDirective", true);
|
||||
}
|
||||
},
|
||||
…
|
||||
|
|
|
@ -580,7 +580,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "ngForOf", $r3$.ɵɵbind(ctx.items));
|
||||
$r3$.ɵɵproperty("ngForOf", ctx.items);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -767,7 +767,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "ngForOf", $r3$.ɵɵbind(ctx.items));
|
||||
$r3$.ɵɵproperty("ngForOf", ctx.items);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -1429,7 +1429,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(2);
|
||||
$r3$.ɵɵelementProperty(2, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -1491,9 +1491,9 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(1);
|
||||
$r3$.ɵɵelementProperty(1, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
$r3$.ɵɵselect(2);
|
||||
$r3$.ɵɵelementProperty(2, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -1558,7 +1558,7 @@ describe('i18n support in the view compiler', () => {
|
|||
if (rf & 2) {
|
||||
const $ctx_r0$ = $r3$.ɵɵnextContext();
|
||||
$r3$.ɵɵselect(4);
|
||||
$r3$.ɵɵelementProperty(4, "ngIf", $r3$.ɵɵbind($ctx_r0$.exists));
|
||||
$r3$.ɵɵproperty("ngIf", $ctx_r0$.exists);
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind($ctx_r0$.valueA));
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind($r3$.ɵɵpipeBind1(3, 3, $ctx_r0$.valueB)));
|
||||
$r3$.ɵɵi18nApply(0);
|
||||
|
@ -1628,9 +1628,9 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(2);
|
||||
$r3$.ɵɵelementProperty(2, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
$r3$.ɵɵselect(3);
|
||||
$r3$.ɵɵelementProperty(3, "ngIf", $r3$.ɵɵbind(!ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", !ctx.visible);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -1686,7 +1686,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -2597,9 +2597,9 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵɵi18nExp($r3$.ɵɵbind(ctx.gender));
|
||||
$r3$.ɵɵi18nApply(1);
|
||||
$r3$.ɵɵselect(2);
|
||||
$r3$.ɵɵelementProperty(2, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
$r3$.ɵɵselect(3);
|
||||
$r3$.ɵɵelementProperty(3, "ngIf", $r3$.ɵɵbind(ctx.available));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.available);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -2935,7 +2935,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(3);
|
||||
$r3$.ɵɵelementProperty(3, "ngIf", $r3$.ɵɵbind(ctx.visible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.visible);
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind(ctx.gender));
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind(ctx.gender));
|
||||
$r3$.ɵɵi18nApply(1);
|
||||
|
@ -3075,7 +3075,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(2);
|
||||
$r3$.ɵɵelementProperty(2, "ngIf", $r3$.ɵɵbind(ctx.ageVisible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.ageVisible);
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind(ctx.gender));
|
||||
$r3$.ɵɵi18nApply(1);
|
||||
}
|
||||
|
@ -3174,7 +3174,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(2);
|
||||
$r3$.ɵɵelementProperty(2, "ngIf", $r3$.ɵɵbind(ctx.ageVisible));
|
||||
$r3$.ɵɵproperty("ngIf", ctx.ageVisible);
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind(ctx.gender));
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind(ctx.weight));
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind(ctx.height));
|
||||
|
|
|
@ -164,7 +164,7 @@ describe('compiler compliance: listen()', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "ngIf", $i0$.ɵɵbind(ctx.showing));
|
||||
$i0$.ɵɵproperty("ngIf", ctx.showing);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -147,8 +147,8 @@ describe('r3_view_compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "@attr", …);
|
||||
$i0$.ɵɵelementProperty(0, "@binding", …);
|
||||
$i0$.ɵɵproperty("@attr", …);
|
||||
$i0$.ɵɵproperty("@binding", …);
|
||||
}
|
||||
}`;
|
||||
const result = compile(files, angularFiles);
|
||||
|
@ -179,7 +179,7 @@ describe('r3_view_compiler', () => {
|
|||
$i0$.ɵɵelementStart(0, "div");
|
||||
…
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "@mySelector", …);
|
||||
$i0$.ɵɵproperty("@mySelector", …);
|
||||
}
|
||||
}`;
|
||||
const result = compile(files, angularFiles);
|
||||
|
|
|
@ -227,11 +227,11 @@ describe('compiler compliance: styling', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "@foo", $r3$.ɵɵbind(ctx.exp));
|
||||
$r3$.ɵɵproperty("@foo", ctx.exp);
|
||||
$r3$.ɵɵselect(1);
|
||||
$r3$.ɵɵelementProperty(1, "@bar", $r3$.ɵɵbind(undefined));
|
||||
$r3$.ɵɵproperty("@bar", undefined);
|
||||
$r3$.ɵɵselect(2);
|
||||
$r3$.ɵɵelementProperty(2, "@baz", $r3$.ɵɵbind(undefined));
|
||||
$r3$.ɵɵproperty("@baz", undefined);
|
||||
}
|
||||
},
|
||||
encapsulation: 2
|
||||
|
@ -289,7 +289,7 @@ describe('compiler compliance: styling', () => {
|
|||
$r3$.ɵɵelementEnd();
|
||||
} if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "@myAnimation", $r3$.ɵɵbind(ctx.exp));
|
||||
$r3$.ɵɵproperty("@myAnimation", ctx.exp);
|
||||
}
|
||||
},
|
||||
encapsulation: 2,
|
||||
|
|
|
@ -76,7 +76,7 @@ describe('compiler compliance: template', () => {
|
|||
const $outer1$ = $i0$.ɵɵnextContext().$implicit;
|
||||
const $myComp1$ = $i0$.ɵɵnextContext();
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "title", $i0$.ɵɵbind($myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component)));
|
||||
$i0$.ɵɵproperty("title", $myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component));
|
||||
$r3$.ɵɵselect(1);
|
||||
$i0$.ɵɵtextBinding(1, $i0$.ɵɵinterpolation1(" ", $myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component), " "));
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ describe('compiler compliance: template', () => {
|
|||
if (rf & 2) {
|
||||
const $myComp2$ = $i0$.ɵɵnextContext(2);
|
||||
$r3$.ɵɵselect(1);
|
||||
$i0$.ɵɵelementProperty(1, "ngForOf", $i0$.ɵɵbind($myComp2$.items));
|
||||
$i0$.ɵɵproperty("ngForOf", $myComp2$.items);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ describe('compiler compliance: template', () => {
|
|||
if (rf & 2) {
|
||||
const $outer2$ = ctx.$implicit;
|
||||
$r3$.ɵɵselect(1);
|
||||
$i0$.ɵɵelementProperty(1, "ngForOf", $i0$.ɵɵbind($outer2$.items));
|
||||
$i0$.ɵɵproperty("ngForOf", $outer2$.items);
|
||||
}
|
||||
}
|
||||
// ...
|
||||
|
@ -114,7 +114,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "ngForOf", $i0$.ɵɵbind(ctx.items));
|
||||
$i0$.ɵɵproperty("ngForOf", ctx.items);
|
||||
}
|
||||
}`;
|
||||
|
||||
|
@ -171,7 +171,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(0);
|
||||
$r3$.ɵɵelementProperty(0, "ngForOf", $r3$.ɵɵbind(ctx._data));
|
||||
$r3$.ɵɵproperty("ngForOf", ctx._data);
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -225,7 +225,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "ngForOf", $i0$.ɵɵbind(ctx.items));
|
||||
$i0$.ɵɵproperty("ngForOf", ctx.items);
|
||||
}
|
||||
}`;
|
||||
|
||||
|
@ -285,7 +285,7 @@ describe('compiler compliance: template', () => {
|
|||
if (rf & 2) {
|
||||
const $app$ = $i0$.ɵɵnextContext();
|
||||
$r3$.ɵɵselect(1);
|
||||
$i0$.ɵɵelementProperty(1, "ngIf", $i0$.ɵɵbind($app$.showing));
|
||||
$i0$.ɵɵproperty("ngIf", $app$.showing);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,7 +296,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "ngForOf", $i0$.ɵɵbind(ctx.items));
|
||||
$i0$.ɵɵproperty("ngForOf", ctx.items);
|
||||
}
|
||||
}`;
|
||||
|
||||
|
@ -356,7 +356,7 @@ describe('compiler compliance: template', () => {
|
|||
if (rf & 2) {
|
||||
const $middle$ = ctx.$implicit;
|
||||
$r3$.ɵɵselect(1);
|
||||
$i0$.ɵɵelementProperty(1, "ngForOf", $i0$.ɵɵbind($middle$.items));
|
||||
$i0$.ɵɵproperty("ngForOf", $middle$.items);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,7 +369,7 @@ describe('compiler compliance: template', () => {
|
|||
if (rf & 2) {
|
||||
const $outer$ = ctx.$implicit;
|
||||
$r3$.ɵɵselect(1);
|
||||
$i0$.ɵɵelementProperty(1, "ngForOf", $i0$.ɵɵbind($outer$.items));
|
||||
$i0$.ɵɵproperty("ngForOf", $outer$.items);
|
||||
}
|
||||
}
|
||||
// ...
|
||||
|
@ -379,7 +379,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "ngForOf", $i0$.ɵɵbind(ctx.items));
|
||||
$i0$.ɵɵproperty("ngForOf", ctx.items);
|
||||
}
|
||||
}`;
|
||||
|
||||
|
@ -426,7 +426,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "boundAttr", $i0$.ɵɵbind(ctx.b));
|
||||
$i0$.ɵɵproperty("boundAttr", ctx.b);
|
||||
}
|
||||
}`;
|
||||
|
||||
|
@ -669,7 +669,7 @@ describe('compiler compliance: template', () => {
|
|||
$i0$.ɵɵpipe(1, "pipe");
|
||||
} if (rf & 2) {
|
||||
$i0$.ɵɵselect(0);
|
||||
$i0$.ɵɵelementProperty(0, "ngIf", $i0$.ɵɵbind($i0$.ɵɵpipeBind1(1, 1, ctx.val)));
|
||||
$i0$.ɵɵproperty("ngIf", $i0$.ɵɵpipeBind1(1, 1, ctx.val));
|
||||
}
|
||||
}`;
|
||||
|
||||
|
|
|
@ -104,13 +104,14 @@ describe('template source-mapping', () => {
|
|||
});
|
||||
expect(mappings).toContain({
|
||||
source: '[attr]="name"',
|
||||
generated: 'i0.ɵɵelementProperty(0, "attr", i0.ɵɵbind(ctx.name))',
|
||||
generated: 'i0.ɵɵproperty("attr", ctx.name)',
|
||||
sourceUrl: '../test.ts'
|
||||
});
|
||||
});
|
||||
|
||||
it('should map a complex input binding expression', () => {
|
||||
const mappings = compileAndMap('<div [attr]="greeting + name"></div>');
|
||||
|
||||
expect(mappings).toContain({
|
||||
source: '<div [attr]="greeting + name"></div>',
|
||||
generated: 'i0.ɵɵelement(0, "div", _c0)',
|
||||
|
@ -118,7 +119,7 @@ describe('template source-mapping', () => {
|
|||
});
|
||||
expect(mappings).toContain({
|
||||
source: '[attr]="greeting + name"',
|
||||
generated: 'i0.ɵɵelementProperty(0, "attr", i0.ɵɵbind((ctx.greeting + ctx.name)))',
|
||||
generated: 'i0.ɵɵproperty("attr", (ctx.greeting + ctx.name))',
|
||||
sourceUrl: '../test.ts'
|
||||
});
|
||||
});
|
||||
|
@ -132,7 +133,7 @@ describe('template source-mapping', () => {
|
|||
});
|
||||
expect(mappings).toContain({
|
||||
source: 'bind-attr="name"',
|
||||
generated: 'i0.ɵɵelementProperty(0, "attr", i0.ɵɵbind(ctx.name))',
|
||||
generated: 'i0.ɵɵproperty("attr", ctx.name)',
|
||||
sourceUrl: '../test.ts'
|
||||
});
|
||||
});
|
||||
|
|
|
@ -123,6 +123,8 @@ export class Identifiers {
|
|||
static pipeBind4: o.ExternalReference = {name: 'ɵɵpipeBind4', moduleName: CORE};
|
||||
static pipeBindV: o.ExternalReference = {name: 'ɵɵpipeBindV', moduleName: CORE};
|
||||
|
||||
static property: o.ExternalReference = {name: 'ɵɵproperty', moduleName: CORE};
|
||||
|
||||
static i18n: o.ExternalReference = {name: 'ɵɵi18n', moduleName: CORE};
|
||||
static i18nAttributes: o.ExternalReference = {name: 'ɵɵi18nAttributes', moduleName: CORE};
|
||||
static i18nExp: o.ExternalReference = {name: 'ɵɵi18nExp', moduleName: CORE};
|
||||
|
|
|
@ -39,6 +39,7 @@ import {I18N_ICU_MAPPING_PREFIX, TRANSLATION_PREFIX, assembleBoundTextPlaceholde
|
|||
import {Instruction, StylingBuilder} from './styling_builder';
|
||||
import {CONTEXT_NAME, IMPLICIT_REFERENCE, NON_BINDABLE_ATTR, REFERENCE_PREFIX, RENDER_FLAGS, asLiteral, getAttrsForDirectiveMatching, invalid, trimTrailingNulls, unsupported} from './util';
|
||||
|
||||
|
||||
// Default selector used by `<ng-content>` if none specified
|
||||
const DEFAULT_NG_CONTENT_SELECTOR = '*';
|
||||
|
||||
|
@ -52,20 +53,6 @@ const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs';
|
|||
const GLOBAL_TARGET_RESOLVERS = new Map<string, o.ExternalReference>(
|
||||
[['window', R3.resolveWindow], ['document', R3.resolveDocument], ['body', R3.resolveBody]]);
|
||||
|
||||
function mapBindingToInstruction(type: BindingType): o.ExternalReference|undefined {
|
||||
switch (type) {
|
||||
case BindingType.Property:
|
||||
case BindingType.Animation:
|
||||
return R3.elementProperty;
|
||||
case BindingType.Class:
|
||||
return R3.elementClassProp;
|
||||
case BindingType.Attribute:
|
||||
return R3.elementAttribute;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// if (rf & flags) { .. }
|
||||
export function renderFlagCheckIfStmt(
|
||||
flags: core.RenderFlags, statements: o.Statement[]): o.IfStmt {
|
||||
|
@ -707,13 +694,12 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
// the reason why `undefined` is used is because the renderer understands this as a
|
||||
// special value to symbolize that there is no RHS to this binding
|
||||
// TODO (matsko): revisit this once FW-959 is approached
|
||||
const emptyValueBindInstruction = o.importExpr(R3.bind).callFn([o.literal(undefined)]);
|
||||
const emptyValueBindInstruction = o.literal(undefined);
|
||||
|
||||
// Generate element input bindings
|
||||
allOtherInputs.forEach((input: t.BoundAttribute) => {
|
||||
|
||||
const instruction = mapBindingToInstruction(input.type);
|
||||
if (input.type === BindingType.Animation) {
|
||||
const inputType = input.type;
|
||||
if (inputType === BindingType.Animation) {
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
// animation bindings can be presented in the following formats:
|
||||
// 1. [@binding]="fooExp"
|
||||
|
@ -727,13 +713,15 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
const hasValue = value instanceof LiteralPrimitive ? !!value.value : true;
|
||||
this.allocateBindingSlots(value);
|
||||
const bindingName = prepareSyntheticPropertyName(input.name);
|
||||
this.updateInstruction(elementIndex, input.sourceSpan, R3.elementProperty, () => {
|
||||
|
||||
this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
|
||||
return [
|
||||
o.literal(elementIndex), o.literal(bindingName),
|
||||
(hasValue ? this.convertPropertyBinding(implicit, value) : emptyValueBindInstruction)
|
||||
o.literal(bindingName),
|
||||
(hasValue ? this.convertPropertyBinding(implicit, value, /* skipBindFn */ true) :
|
||||
emptyValueBindInstruction),
|
||||
];
|
||||
});
|
||||
} else if (instruction) {
|
||||
} else {
|
||||
// we must skip attributes with associated i18n context, since these attributes are handled
|
||||
// separately and corresponding `i18nExp` and `i18nApply` instructions will be generated
|
||||
if (input.i18n) return;
|
||||
|
@ -742,7 +730,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
if (value !== undefined) {
|
||||
const params: any[] = [];
|
||||
const [attrNamespace, attrName] = splitNsName(input.name);
|
||||
const isAttributeBinding = input.type === BindingType.Attribute;
|
||||
const isAttributeBinding = inputType === BindingType.Attribute;
|
||||
const sanitizationRef = resolveSanitizationFn(input.securityContext, isAttributeBinding);
|
||||
if (sanitizationRef) params.push(sanitizationRef);
|
||||
if (attrNamespace) {
|
||||
|
@ -757,15 +745,34 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
}
|
||||
}
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstruction(elementIndex, input.sourceSpan, instruction, () => {
|
||||
return [
|
||||
o.literal(elementIndex), o.literal(attrName),
|
||||
this.convertPropertyBinding(implicit, value), ...params
|
||||
];
|
||||
});
|
||||
|
||||
if (inputType === BindingType.Property && !(value instanceof Interpolation)) {
|
||||
// Bound, un-interpolated properties
|
||||
this.updateInstruction(elementIndex, input.sourceSpan, R3.property, () => {
|
||||
return [
|
||||
o.literal(attrName), this.convertPropertyBinding(implicit, value, true), ...params
|
||||
];
|
||||
});
|
||||
} else {
|
||||
let instruction: any;
|
||||
|
||||
if (inputType === BindingType.Property) {
|
||||
// Interpolated properties
|
||||
instruction = R3.elementProperty;
|
||||
} else if (inputType === BindingType.Class) {
|
||||
instruction = R3.elementClassProp;
|
||||
} else {
|
||||
instruction = R3.elementAttribute;
|
||||
}
|
||||
|
||||
this.updateInstruction(elementIndex, input.sourceSpan, instruction, () => {
|
||||
return [
|
||||
o.literal(elementIndex), o.literal(attrName),
|
||||
this.convertPropertyBinding(implicit, value), ...params
|
||||
];
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._unsupported(`binding type ${input.type}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -856,7 +863,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
return trimTrailingNulls(parameters);
|
||||
});
|
||||
|
||||
// handle property bindings e.g. ɵɵelementProperty(1, 'ngForOf', ɵɵbind(ctx.items));
|
||||
// handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al;
|
||||
const context = o.variable(CONTEXT_NAME);
|
||||
this.templatePropertyBindings(template, templateIndex, context, template.templateAttrs);
|
||||
|
||||
|
@ -970,12 +977,9 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
if (input instanceof t.BoundAttribute) {
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstruction(templateIndex, template.sourceSpan, R3.elementProperty, () => {
|
||||
return [
|
||||
o.literal(templateIndex), o.literal(input.name),
|
||||
this.convertPropertyBinding(context, value)
|
||||
];
|
||||
});
|
||||
this.updateInstruction(
|
||||
templateIndex, template.sourceSpan, R3.property,
|
||||
() => [o.literal(input.name), this.convertPropertyBinding(context, value, true)]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ export {
|
|||
ɵɵloadContentQuery,
|
||||
ɵɵelementEnd,
|
||||
ɵɵelementProperty,
|
||||
ɵɵproperty,
|
||||
ɵɵcomponentHostSyntheticProperty,
|
||||
ɵɵcomponentHostSyntheticListener,
|
||||
ɵɵprojectionDef,
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {assertNotEqual} from '../../util/assert';
|
||||
import {bindingUpdated} from '../bindings';
|
||||
import {SanitizerFn} from '../interfaces/sanitization';
|
||||
import {BINDING_INDEX} from '../interfaces/view';
|
||||
|
@ -38,6 +39,7 @@ export function ɵɵproperty<T>(
|
|||
propName: string, value: T, sanitizer?: SanitizerFn | null,
|
||||
nativeOnly?: boolean): TsickleIssue1009 {
|
||||
const index = getSelectedIndex();
|
||||
ngDevMode && assertNotEqual(index, -1, 'selected index cannot be -1');
|
||||
const bindReconciledValue = ɵɵbind(value);
|
||||
elementPropertyInternal(index, propName, bindReconciledValue, sanitizer, nativeOnly);
|
||||
return ɵɵproperty;
|
||||
|
|
|
@ -36,7 +36,14 @@ export function ɵɵselect(index: number): void {
|
|||
ngDevMode &&
|
||||
assertLessThan(
|
||||
index, getLView().length - HEADER_OFFSET, 'Should be within range for the view data');
|
||||
setSelectedIndex(index);
|
||||
const lView = getLView();
|
||||
|
||||
// Flush the initial hooks for elements in the view that have been added up to this point.
|
||||
executePreOrderHooks(lView, lView[TVIEW], getCheckNoChangesMode(), index);
|
||||
|
||||
// We must set the selected index *after* running the hooks, because hooks may have side-effects
|
||||
// that cause other template functions to run, thus updating the selected index, which is global
|
||||
// state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
|
||||
// will be altered by the time we leave the `ɵɵselect` instruction.
|
||||
setSelectedIndex(index);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import {StylingContext} from '../interfaces/styling';
|
|||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from '../node_assert';
|
||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, incrementActiveDirectiveId, isCreationMode, leaveView, resetComponentState, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode, setSelectedIndex, ɵɵnamespaceHTML} from '../state';
|
||||
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getSelectedIndex, incrementActiveDirectiveId, isCreationMode, leaveView, resetComponentState, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode, setSelectedIndex, ɵɵnamespaceHTML} from '../state';
|
||||
import {initializeStaticContext as initializeStaticStylingContext} from '../styling/class_and_style_bindings';
|
||||
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../styling/util';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
|
@ -101,51 +101,55 @@ export function refreshDescendantViews(lView: LView) {
|
|||
|
||||
/** Sets the host bindings for the current view. */
|
||||
export function setHostBindings(tView: TView, viewData: LView): void {
|
||||
if (tView.expandoInstructions) {
|
||||
let bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex;
|
||||
setBindingRoot(bindingRootIndex);
|
||||
let currentDirectiveIndex = -1;
|
||||
let currentElementIndex = -1;
|
||||
for (let i = 0; i < tView.expandoInstructions.length; i++) {
|
||||
const instruction = tView.expandoInstructions[i];
|
||||
if (typeof instruction === 'number') {
|
||||
if (instruction <= 0) {
|
||||
// Negative numbers mean that we are starting new EXPANDO block and need to update
|
||||
// the current element and directive index.
|
||||
currentElementIndex = -instruction;
|
||||
setActiveHostElement(currentElementIndex);
|
||||
const selectedIndex = getSelectedIndex();
|
||||
try {
|
||||
if (tView.expandoInstructions) {
|
||||
let bindingRootIndex = viewData[BINDING_INDEX] = tView.expandoStartIndex;
|
||||
setBindingRoot(bindingRootIndex);
|
||||
let currentDirectiveIndex = -1;
|
||||
let currentElementIndex = -1;
|
||||
for (let i = 0; i < tView.expandoInstructions.length; i++) {
|
||||
const instruction = tView.expandoInstructions[i];
|
||||
if (typeof instruction === 'number') {
|
||||
if (instruction <= 0) {
|
||||
// Negative numbers mean that we are starting new EXPANDO block and need to update
|
||||
// the current element and directive index.
|
||||
currentElementIndex = -instruction;
|
||||
setActiveHostElement(currentElementIndex);
|
||||
|
||||
// Injector block and providers are taken into account.
|
||||
const providerCount = (tView.expandoInstructions[++i] as number);
|
||||
bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount;
|
||||
// Injector block and providers are taken into account.
|
||||
const providerCount = (tView.expandoInstructions[++i] as number);
|
||||
bindingRootIndex += INJECTOR_BLOOM_PARENT_SIZE + providerCount;
|
||||
|
||||
currentDirectiveIndex = bindingRootIndex;
|
||||
currentDirectiveIndex = bindingRootIndex;
|
||||
} else {
|
||||
// This is either the injector size (so the binding root can skip over directives
|
||||
// and get to the first set of host bindings on this node) or the host var count
|
||||
// (to get to the next set of host bindings on this node).
|
||||
bindingRootIndex += instruction;
|
||||
}
|
||||
setBindingRoot(bindingRootIndex);
|
||||
} else {
|
||||
// This is either the injector size (so the binding root can skip over directives
|
||||
// and get to the first set of host bindings on this node) or the host var count
|
||||
// (to get to the next set of host bindings on this node).
|
||||
bindingRootIndex += instruction;
|
||||
}
|
||||
setBindingRoot(bindingRootIndex);
|
||||
} else {
|
||||
// If it's not a number, it's a host binding function that needs to be executed.
|
||||
if (instruction !== null) {
|
||||
viewData[BINDING_INDEX] = bindingRootIndex;
|
||||
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
|
||||
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
|
||||
// If it's not a number, it's a host binding function that needs to be executed.
|
||||
if (instruction !== null) {
|
||||
viewData[BINDING_INDEX] = bindingRootIndex;
|
||||
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
|
||||
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
|
||||
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
incrementActiveDirectiveId();
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
incrementActiveDirectiveId();
|
||||
}
|
||||
currentDirectiveIndex++;
|
||||
}
|
||||
currentDirectiveIndex++;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
setActiveHostElement(selectedIndex);
|
||||
}
|
||||
setActiveHostElement(null);
|
||||
}
|
||||
|
||||
/** Refreshes content queries for all directives in the given view. */
|
||||
|
@ -431,12 +435,8 @@ export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, con
|
|||
|
||||
oldView = enterView(viewToRender, viewToRender[T_HOST]);
|
||||
resetPreOrderHookFlags(viewToRender);
|
||||
ɵɵnamespaceHTML();
|
||||
executeTemplate(tView.template !, getRenderFlags(viewToRender), context);
|
||||
|
||||
// Reset the selected index so we can assert that `select` was called later
|
||||
setSelectedIndex(-1);
|
||||
|
||||
tView.template !(getRenderFlags(viewToRender), context);
|
||||
// This must be set to false immediately after the first creation run because in an
|
||||
// ngFor loop, all the views will be created together before update mode runs and turns
|
||||
// off firstTemplatePass. If we don't set it here, instances will perform directive
|
||||
|
@ -465,14 +465,7 @@ function renderComponentOrTemplate<T>(
|
|||
|
||||
if (creationModeIsActive) {
|
||||
// creation mode pass
|
||||
if (templateFn) {
|
||||
ɵɵnamespaceHTML();
|
||||
|
||||
// Reset the selected index so we can assert that `select` was called later
|
||||
setSelectedIndex(-1);
|
||||
|
||||
templateFn(RenderFlags.Create, context);
|
||||
}
|
||||
templateFn && executeTemplate(templateFn, RenderFlags.Create, context);
|
||||
|
||||
refreshDescendantViews(hostView);
|
||||
hostView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
|
@ -480,7 +473,7 @@ function renderComponentOrTemplate<T>(
|
|||
|
||||
// update mode pass
|
||||
resetPreOrderHookFlags(hostView);
|
||||
templateFn && templateFn(RenderFlags.Update, context);
|
||||
templateFn && executeTemplate(templateFn, RenderFlags.Update, context);
|
||||
refreshDescendantViews(hostView);
|
||||
} finally {
|
||||
if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
|
||||
|
@ -490,6 +483,17 @@ function renderComponentOrTemplate<T>(
|
|||
}
|
||||
}
|
||||
|
||||
function executeTemplate<T>(templateFn: ComponentTemplate<T>, rf: RenderFlags, context: T) {
|
||||
ɵɵnamespaceHTML();
|
||||
const prevSelectedIndex = getSelectedIndex();
|
||||
try {
|
||||
setActiveHostElement(null);
|
||||
templateFn(rf, context);
|
||||
} finally {
|
||||
setSelectedIndex(prevSelectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns the default configuration of rendering flags depending on when the
|
||||
* template is in creation mode or update mode. Update block and create block are
|
||||
|
@ -1073,26 +1077,29 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod
|
|||
const expando = tView.expandoInstructions !;
|
||||
const firstTemplatePass = tView.firstTemplatePass;
|
||||
const elementIndex = tNode.index - HEADER_OFFSET;
|
||||
setActiveHostElement(elementIndex);
|
||||
const selectedIndex = getSelectedIndex();
|
||||
try {
|
||||
setActiveHostElement(elementIndex);
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
const def = tView.data[i] as DirectiveDef<any>;
|
||||
const directive = viewData[i];
|
||||
if (def.hostBindings) {
|
||||
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
|
||||
for (let i = start; i < end; i++) {
|
||||
const def = tView.data[i] as DirectiveDef<any>;
|
||||
const directive = viewData[i];
|
||||
if (def.hostBindings) {
|
||||
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
|
||||
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
incrementActiveDirectiveId();
|
||||
} else if (firstTemplatePass) {
|
||||
expando.push(null);
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
incrementActiveDirectiveId();
|
||||
} else if (firstTemplatePass) {
|
||||
expando.push(null);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
setActiveHostElement(selectedIndex);
|
||||
}
|
||||
|
||||
setActiveHostElement(null);
|
||||
}
|
||||
|
||||
export function invokeHostBindingsInCreationMode(
|
||||
|
@ -1685,14 +1692,8 @@ export function checkView<T>(hostView: LView, component: T) {
|
|||
|
||||
try {
|
||||
resetPreOrderHookFlags(hostView);
|
||||
ɵɵnamespaceHTML();
|
||||
creationMode && executeViewQueryFn(RenderFlags.Create, hostTView, component);
|
||||
|
||||
// Reset the selected index so we can assert that `select` was called later
|
||||
setSelectedIndex(-1);
|
||||
|
||||
templateFn(getRenderFlags(hostView), component);
|
||||
|
||||
executeTemplate(templateFn, getRenderFlags(hostView), component);
|
||||
refreshDescendantViews(hostView);
|
||||
// Only check view queries again in creation mode if there are static view queries
|
||||
if (!creationMode || hostTView.staticViewQueries) {
|
||||
|
|
|
@ -85,6 +85,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
|||
'ɵɵpipeBind4': r3.ɵɵpipeBind4,
|
||||
'ɵɵpipeBindV': r3.ɵɵpipeBindV,
|
||||
'ɵɵprojectionDef': r3.ɵɵprojectionDef,
|
||||
'ɵɵproperty': r3.ɵɵproperty,
|
||||
'ɵɵpipe': r3.ɵɵpipe,
|
||||
'ɵɵqueryRefresh': r3.ɵɵqueryRefresh,
|
||||
'ɵɵviewQuery': r3.ɵɵviewQuery,
|
||||
|
|
|
@ -470,9 +470,6 @@ let _selectedIndex = -1;
|
|||
* current `LView` to act on.
|
||||
*/
|
||||
export function getSelectedIndex() {
|
||||
ngDevMode &&
|
||||
assertGreaterThan(
|
||||
_selectedIndex, -1, 'select() should be called prior to retrieving the selected index');
|
||||
return _selectedIndex;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {Component, Directive, ElementRef, HostBinding} from '@angular/core';
|
||||
import {Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, HostBinding, Input, NgModule, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {ivyEnabled, onlyInIvy} from '@angular/private/testing';
|
||||
|
@ -97,6 +97,99 @@ describe('acceptance integration tests', () => {
|
|||
expect(element.classList.contains('baz')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not cause problems if detectChanges is called when a property updates', () => {
|
||||
/**
|
||||
* Angular Material CDK Tree contains a code path whereby:
|
||||
*
|
||||
* 1. During the execution of a template function in which **more than one** property is
|
||||
* updated in a row.
|
||||
* 2. A property that **is not the last property** is updated in the **original template**:
|
||||
* - That sets up a new observable and subscribes to it
|
||||
* - The new observable it sets up can emit synchronously.
|
||||
* - When it emits, it calls `detectChanges` on a `ViewRef` that it has a handle to
|
||||
* - That executes a **different template**, that has host bindings
|
||||
* - this executes `setHostBindings`
|
||||
* - Inside of `setHostBindings` we are currently updating the selected index **global
|
||||
* state** via `setActiveHostElement`.
|
||||
* 3. We attempt to update the next property in the **original template**.
|
||||
* - But the selected index has been altered, and we get errors.
|
||||
*/
|
||||
|
||||
@Component({
|
||||
selector: 'child',
|
||||
template: `...`,
|
||||
})
|
||||
class ChildCmp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'parent',
|
||||
template: `
|
||||
<div>
|
||||
<div #template></div>
|
||||
<p>{{prop}}</p>
|
||||
<p>{{prop2}}</p>
|
||||
</div>
|
||||
`,
|
||||
host: {
|
||||
'[style.color]': 'color',
|
||||
},
|
||||
})
|
||||
class ParentCmp {
|
||||
private _prop = '';
|
||||
|
||||
@ViewChild('template', {read: ViewContainerRef})
|
||||
vcr: ViewContainerRef = null !;
|
||||
|
||||
private child: ComponentRef<ChildCmp> = null !;
|
||||
|
||||
@Input()
|
||||
set prop(value: string) {
|
||||
// Material CdkTree has at least one scenario where setting a property causes a data source
|
||||
// to update, which causes a synchronous call to detectChanges().
|
||||
this._prop = value;
|
||||
if (this.child) {
|
||||
this.child.changeDetectorRef.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
get prop() { return this._prop; }
|
||||
|
||||
@Input()
|
||||
prop2 = 0;
|
||||
|
||||
ngAfterViewInit() {
|
||||
const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp);
|
||||
this.child = this.vcr.createComponent(factory);
|
||||
}
|
||||
|
||||
constructor(private componentFactoryResolver: ComponentFactoryResolver) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `<parent [prop]="prop" [prop2]="prop2"></parent>`,
|
||||
})
|
||||
class App {
|
||||
prop = 'a';
|
||||
prop2 = 1;
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
entryComponents: [ChildCmp],
|
||||
declarations: [ChildCmp],
|
||||
})
|
||||
class ChildCmpModule {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [App, ParentCmp], imports: [ChildCmpModule]});
|
||||
const fixture = TestBed.createComponent(App);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentInstance.prop = 'b';
|
||||
fixture.componentInstance.prop2 = 2;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should render inline style and class attribute values on the element before a directive is instantiated',
|
||||
() => {
|
||||
@Component({
|
||||
|
|
|
@ -278,6 +278,9 @@
|
|||
{
|
||||
"name": "executePreOrderHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeTemplate"
|
||||
},
|
||||
{
|
||||
"name": "executeViewQueryFn"
|
||||
},
|
||||
|
@ -410,6 +413,9 @@
|
|||
{
|
||||
"name": "getRootView"
|
||||
},
|
||||
{
|
||||
"name": "getSelectedIndex"
|
||||
},
|
||||
{
|
||||
"name": "getStylingContextFromLView"
|
||||
},
|
||||
|
|
|
@ -218,6 +218,9 @@
|
|||
{
|
||||
"name": "executePreOrderHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeTemplate"
|
||||
},
|
||||
{
|
||||
"name": "executeViewQueryFn"
|
||||
},
|
||||
|
@ -320,6 +323,9 @@
|
|||
{
|
||||
"name": "getRootView"
|
||||
},
|
||||
{
|
||||
"name": "getSelectedIndex"
|
||||
},
|
||||
{
|
||||
"name": "hasParentInjector"
|
||||
},
|
||||
|
|
|
@ -593,6 +593,9 @@
|
|||
{
|
||||
"name": "executePreOrderHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeTemplate"
|
||||
},
|
||||
{
|
||||
"name": "executeViewQueryFn"
|
||||
},
|
||||
|
@ -821,6 +824,9 @@
|
|||
{
|
||||
"name": "getRootView"
|
||||
},
|
||||
{
|
||||
"name": "getSelectedIndex"
|
||||
},
|
||||
{
|
||||
"name": "getSinglePropIndexValue"
|
||||
},
|
||||
|
@ -1346,9 +1352,6 @@
|
|||
{
|
||||
"name": "ɵɵelementEnd"
|
||||
},
|
||||
{
|
||||
"name": "ɵɵelementProperty"
|
||||
},
|
||||
{
|
||||
"name": "ɵɵelementStart"
|
||||
},
|
||||
|
@ -1376,6 +1379,9 @@
|
|||
{
|
||||
"name": "ɵɵnextContext"
|
||||
},
|
||||
{
|
||||
"name": "ɵɵproperty"
|
||||
},
|
||||
{
|
||||
"name": "ɵɵreference"
|
||||
},
|
||||
|
|
|
@ -937,6 +937,8 @@ export declare function ɵɵprojection(nodeIndex: number, selectorIndex?: number
|
|||
|
||||
export declare function ɵɵprojectionDef(selectors?: CssSelectorList[]): void;
|
||||
|
||||
export declare function ɵɵproperty<T>(propName: string, value: T, sanitizer?: SanitizerFn | null, nativeOnly?: boolean): TsickleIssue1009;
|
||||
|
||||
export declare function ɵɵProvidersFeature<T>(providers: Provider[], viewProviders?: Provider[]): (definition: DirectiveDef<T>) => void;
|
||||
|
||||
export declare function ɵɵpureFunction0<T>(slotOffset: number, pureFn: () => T, thisArg?: any): T;
|
||||
|
|
Loading…
Reference in New Issue