fix(ivy): support property values changed in ngOnChanges (forward rref case) (#29054)
PR Close #29054
This commit is contained in:
parent
6215799055
commit
25166d4f41
|
@ -12,7 +12,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1440,
|
||||
"main": 13659,
|
||||
"main": 13921,
|
||||
"polyfills": 38390
|
||||
}
|
||||
}
|
||||
|
|
|
@ -798,6 +798,7 @@ describe('compiler compliance', () => {
|
|||
if (rf & 2) {
|
||||
const $myComp$ = $r3$.ɵnextContext();
|
||||
const $foo$ = $r3$.ɵreference(1);
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation2("", $myComp$.salutation, " ", $foo$, ""));
|
||||
}
|
||||
}
|
||||
|
@ -1255,6 +1256,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
}
|
||||
}
|
||||
|
@ -1947,6 +1949,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵtextBinding(0, $r3$.ɵinterpolation1("", $r3$.ɵpipeBind2(1, 3, $r3$.ɵpipeBind2(2, 6, ctx.name, ctx.size), ctx.size), ""));
|
||||
$r3$.ɵflushHooksUpTo(4);
|
||||
$r3$.ɵtextBinding(4, $r3$.ɵinterpolation2("", $r3$.ɵpipeBindV(5, 9, $r3$.ɵpureFunction1(18, $c0$, ctx.name)), " ", (ctx.name ? 1 : $r3$.ɵpipeBind1(6, 16, 2)), ""));
|
||||
}
|
||||
},
|
||||
|
@ -2061,6 +2064,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $user$ = $r3$.ɵreference(1);
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵtextBinding(2, $r3$.ɵinterpolation1("Hello ", $user$.value, "!"));
|
||||
}
|
||||
},
|
||||
|
@ -2123,6 +2127,7 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵnextContext();
|
||||
const $foo$ = $r3$.ɵreference(1);
|
||||
const $baz$ = $r3$.ɵreference(5);
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation3("", $foo$, "-", $bar$, "-", $baz$, ""));
|
||||
}
|
||||
}
|
||||
|
@ -2138,6 +2143,7 @@ describe('compiler compliance', () => {
|
|||
const $bar$ = $r3$.ɵreference(4);
|
||||
$r3$.ɵnextContext();
|
||||
const $foo$ = $r3$.ɵreference(1);
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation2(" ", $foo$, "-", $bar$, " "));
|
||||
}
|
||||
}
|
||||
|
@ -2157,6 +2163,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $foo$ = $r3$.ɵreference(1);
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵtextBinding(2, $r3$.ɵinterpolation1(" ", $foo$, " "));
|
||||
}
|
||||
},
|
||||
|
@ -2209,6 +2216,7 @@ describe('compiler compliance', () => {
|
|||
if (rf & 2) {
|
||||
const $item$ = $i0$.ɵnextContext().$implicit;
|
||||
const $foo$ = $i0$.ɵreference(2);
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵtextBinding(1, $i0$.ɵinterpolation2(" ", $foo$, " - ", $item$, " "));
|
||||
}
|
||||
}
|
||||
|
@ -2222,6 +2230,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $app$ = $i0$.ɵnextContext();
|
||||
$r3$.ɵflushHooksUpTo(3);
|
||||
$i0$.ɵelementProperty(3, "ngIf", $i0$.ɵbind($app$.showing));
|
||||
}
|
||||
}
|
||||
|
@ -2313,6 +2322,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "name", $r3$.ɵbind(ctx.name1));
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵelementProperty(1, "name", $r3$.ɵbind(ctx.name2));
|
||||
}
|
||||
},
|
||||
|
@ -2443,7 +2453,10 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵtemplate(1, MyComponent__svg_g_1_Template, 2, 0, "g", $t1_attrs$);
|
||||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) { $r3$.ɵelementProperty(1,"forOf",$r3$.ɵbind(ctx.items)); }
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵelementProperty(1,"forOf",$r3$.ɵbind(ctx.items));
|
||||
}
|
||||
},
|
||||
directives: function() { return [ForOfDirective]; },
|
||||
encapsulation: 2
|
||||
|
@ -2505,6 +2518,7 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $item$ = ctx.$implicit;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation1("", $item$.name, ""));
|
||||
}
|
||||
}
|
||||
|
@ -2522,6 +2536,7 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵelementProperty(1, "forOf", $r3$.ɵbind(ctx.items));
|
||||
}
|
||||
},
|
||||
|
@ -2586,6 +2601,7 @@ describe('compiler compliance', () => {
|
|||
if (rf & 2) {
|
||||
const $info$ = ctx.$implicit;
|
||||
const $item$ = $r3$.ɵnextContext().$implicit;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵtextBinding(1, $r3$.ɵinterpolation2(" ", $item$.name, ": ", $info$.description, " "));
|
||||
}
|
||||
}
|
||||
|
@ -2603,7 +2619,9 @@ describe('compiler compliance', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $item$ = ctx.$implicit;
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵtextBinding(2, $r3$.ɵinterpolation1("", IDENT.name, ""));
|
||||
$r3$.ɵflushHooksUpTo(4);
|
||||
$r3$.ɵelementProperty(4, "forOf", $r3$.ɵbind(IDENT.infos));
|
||||
}
|
||||
}
|
||||
|
@ -2622,6 +2640,7 @@ describe('compiler compliance', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵelementProperty(1, "forOf", $r3$.ɵbind(ctx.items));
|
||||
}
|
||||
},
|
||||
|
|
|
@ -44,6 +44,7 @@ describe('compiler compliance: bindings', () => {
|
|||
$i0$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵtextBinding(1, $i0$.ɵinterpolation1("Hello ", $ctx$.name, ""));
|
||||
}
|
||||
}`;
|
||||
|
@ -473,6 +474,7 @@ describe('compiler compliance: bindings', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $_r0$ = $i0$.ɵreference(1);
|
||||
$r3$.ɵflushHooksUpTo(4);
|
||||
$i0$.ɵtextBinding(4, $i0$.ɵinterpolation1(" ", $_r0$.id, " "));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -369,6 +369,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(1, 0, ctx.valueA)));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueB));
|
||||
$r3$.ɵi18nApply(2);
|
||||
$r3$.ɵflushHooksUpTo(3);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueA));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueB));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind((ctx.valueA + ctx.valueB)));
|
||||
|
@ -436,6 +437,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $outer_r1$ = ctx.$implicit;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(2, 0, $outer_r1$)));
|
||||
$r3$.ɵi18nApply(3);
|
||||
}
|
||||
|
@ -525,6 +527,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(1, 0, ctx.valueA)));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueB));
|
||||
$r3$.ɵi18nApply(2);
|
||||
$r3$.ɵflushHooksUpTo(3);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueA));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueB));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind((ctx.valueA + ctx.valueB)));
|
||||
|
@ -565,6 +568,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $outer_r1$ = ctx.$implicit;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(2, 0, $outer_r1$)));
|
||||
$r3$.ɵi18nApply(3);
|
||||
}
|
||||
|
@ -730,6 +734,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueA));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -756,6 +761,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueA));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -786,6 +792,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(2, 2, ctx.valueA)));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(((ctx.valueA == null) ? null : ((ctx.valueA.a == null) ? null : ctx.valueA.a.b))));
|
||||
$r3$.ɵi18nApply(1);
|
||||
|
@ -829,10 +836,13 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.one));
|
||||
$r3$.ɵi18nApply(1);
|
||||
$r3$.ɵflushHooksUpTo(3);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(4, 3, ctx.two)));
|
||||
$r3$.ɵi18nApply(3);
|
||||
$r3$.ɵflushHooksUpTo(6);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(((ctx.three + ctx.four) + ctx.five)));
|
||||
$r3$.ɵi18nApply(6);
|
||||
}
|
||||
|
@ -897,8 +907,10 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.one));
|
||||
$r3$.ɵi18nApply(1);
|
||||
$r3$.ɵflushHooksUpTo(4);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(5, 3, ctx.two)));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.nestedInBlockTwo));
|
||||
$r3$.ɵi18nApply(4);
|
||||
|
@ -965,11 +977,13 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueB));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueC));
|
||||
$r3$.ɵi18nApply(3);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueA));
|
||||
$r3$.ɵi18nApply(1);
|
||||
$r3$.ɵflushHooksUpTo(7);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.valueE));
|
||||
$r3$.ɵi18nApply(8);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(6, 5, ctx.valueD)));
|
||||
|
@ -1018,6 +1032,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $ctx_r0$ = $r3$.ɵnextContext();
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($ctx_r0$.valueA));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(4, 2, $ctx_r0$.valueB)));
|
||||
$r3$.ɵi18nApply(2);
|
||||
|
@ -1034,6 +1049,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵelementProperty(2, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
}
|
||||
}
|
||||
|
@ -1083,7 +1099,9 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵtemplate(2, MyComponent_img_2_Template, 2, 1, "img", $_c1$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵelementProperty(1, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵelementProperty(2, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
}
|
||||
}
|
||||
|
@ -1147,6 +1165,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $ctx_r0$ = $r3$.ɵnextContext();
|
||||
$r3$.ɵflushHooksUpTo(4);
|
||||
$r3$.ɵelementProperty(4, "ngIf", $r3$.ɵbind($ctx_r0$.exists));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($ctx_r0$.valueA));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(3, 3, $ctx_r0$.valueB)));
|
||||
|
@ -1196,7 +1215,9 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵelementProperty(2, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
$r3$.ɵflushHooksUpTo(3);
|
||||
$r3$.ɵelementProperty(3, "ngIf", $r3$.ɵbind(!ctx.visible));
|
||||
}
|
||||
}
|
||||
|
@ -1228,6 +1249,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $ctx_r0$ = $r3$.ɵnextContext();
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($ctx_r0$.valueA));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -1290,6 +1312,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.age));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -1378,6 +1401,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementContainerEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(2, 1, ctx.valueA)));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -1462,6 +1486,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($r3$.ɵpipeBind1(4, 1, ctx.valueB)));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -1507,6 +1532,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementContainerEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.age));
|
||||
$r3$.ɵi18nApply(2);
|
||||
}
|
||||
|
@ -1628,6 +1654,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵtemplate(2, MyComponent_ng_template_2_Template, 1, 1, "ng-template");
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -1771,6 +1798,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -1850,6 +1878,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $ctx_r0$ = $r3$.ɵnextContext();
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($ctx_r0$.age));
|
||||
$r3$.ɵi18nApply(1);
|
||||
}
|
||||
|
@ -1871,6 +1900,7 @@ describe('i18n support in the view compiler', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $ctx_r1$ = $r3$.ɵnextContext();
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($ctx_r1$.count));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind($ctx_r1$.count));
|
||||
$r3$.ɵi18nApply(2);
|
||||
|
@ -1888,9 +1918,12 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 2, "div", $_c0$);
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nApply(1);
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵelementProperty(2, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
$r3$.ɵflushHooksUpTo(3);
|
||||
$r3$.ɵelementProperty(3, "ngIf", $r3$.ɵbind(ctx.available));
|
||||
}
|
||||
}
|
||||
|
@ -1917,6 +1950,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.age));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.other));
|
||||
$r3$.ɵi18nApply(1);
|
||||
|
@ -2004,6 +2038,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(((ctx.ageA + ctx.ageB) + ctx.ageC)));
|
||||
$r3$.ɵi18nApply(1);
|
||||
|
@ -2045,6 +2080,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.age));
|
||||
$r3$.ɵi18nApply(1);
|
||||
|
@ -2116,6 +2152,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(3);
|
||||
$r3$.ɵelementProperty(3, "ngIf", $r3$.ɵbind(ctx.visible));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
|
@ -2157,6 +2194,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.age));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nApply(1);
|
||||
|
@ -2220,6 +2258,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵelementProperty(2, "ngIf", $r3$.ɵbind(ctx.ageVisible));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nApply(1);
|
||||
|
@ -2286,6 +2325,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵelementProperty(2, "ngIf", $r3$.ɵbind(ctx.ageVisible));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.weight));
|
||||
|
@ -2328,6 +2368,7 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.gender));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.weight));
|
||||
$r3$.ɵi18nExp($r3$.ɵbind(ctx.height));
|
||||
|
|
|
@ -227,7 +227,9 @@ describe('compiler compliance: styling', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵelementProperty(0, "@foo", $r3$.ɵbind(ctx.exp));
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$r3$.ɵelementProperty(1, "@bar", $r3$.ɵbind(undefined));
|
||||
$r3$.ɵflushHooksUpTo(2);
|
||||
$r3$.ɵelementProperty(2, "@baz", $r3$.ɵbind(undefined));
|
||||
}
|
||||
},
|
||||
|
@ -930,6 +932,7 @@ describe('compiler compliance: styling', () => {
|
|||
$r3$.ɵelementStyleProp(0, 1, $r3$.ɵpipeBind2(3, 7, $ctx$.bazExp, 4000));
|
||||
$r3$.ɵelementClassProp(0, 0, $r3$.ɵpipeBind2(4, 10, $ctx$.fooExp, 2000));
|
||||
$r3$.ɵelementStylingApply(0);
|
||||
$r3$.ɵflushHooksUpTo(5);
|
||||
$r3$.ɵtextBinding(5, $r3$.ɵinterpolation1(" ", $ctx$.item, ""));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ describe('compiler compliance: template', () => {
|
|||
const $outer1$ = $i0$.ɵnextContext().$implicit;
|
||||
const $myComp1$ = $i0$.ɵnextContext();
|
||||
$i0$.ɵelementProperty(0, "title", $i0$.ɵbind($myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component)));
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵtextBinding(1, $i0$.ɵinterpolation1(" ", $myComp1$.format($outer1$, $middle1$, $inner1$, $myComp1$.component), " "));
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +88,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $myComp2$ = $i0$.ɵnextContext(2);
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵelementProperty(1, "ngForOf", $i0$.ɵbind($myComp2$.items));
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +101,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $outer2$ = ctx.$implicit;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵelementProperty(1, "ngForOf", $i0$.ɵbind($outer2$.items));
|
||||
}
|
||||
}
|
||||
|
@ -207,6 +210,7 @@ describe('compiler compliance: template', () => {
|
|||
if (rf & 2) {
|
||||
const $item$ = ctx.$implicit;
|
||||
const $i$ = ctx.index;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵtextBinding(1, $i0$.ɵinterpolation2(" ", $i$, " - ", $item$, " "));
|
||||
}
|
||||
}
|
||||
|
@ -262,6 +266,7 @@ describe('compiler compliance: template', () => {
|
|||
const $div$ = $i0$.ɵnextContext();
|
||||
const $i$ = $div$.index;
|
||||
const $item$ = $div$.$implicit;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵtextBinding(1, $i0$.ɵinterpolation2(" ", $i$, " - ", $item$, " "));
|
||||
}
|
||||
}
|
||||
|
@ -274,6 +279,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $app$ = $i0$.ɵnextContext();
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵelementProperty(1, "ngIf", $i0$.ɵbind($app$.showing));
|
||||
}
|
||||
}
|
||||
|
@ -330,6 +336,7 @@ describe('compiler compliance: template', () => {
|
|||
if (rf & 2) {
|
||||
const $middle$ = $i0$.ɵnextContext().$implicit;
|
||||
const $myComp$ = $i0$.ɵnextContext(2);
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵtextBinding(1, $i0$.ɵinterpolation2(" ", $middle$.value, " - ", $myComp$.name, " "));
|
||||
}
|
||||
}
|
||||
|
@ -342,6 +349,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $middle$ = ctx.$implicit;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵelementProperty(1, "ngForOf", $i0$.ɵbind($middle$.items));
|
||||
}
|
||||
}
|
||||
|
@ -354,6 +362,7 @@ describe('compiler compliance: template', () => {
|
|||
}
|
||||
if (rf & 2) {
|
||||
const $outer$ = ctx.$implicit;
|
||||
$r3$.ɵflushHooksUpTo(1);
|
||||
$i0$.ɵelementProperty(1, "ngForOf", $i0$.ɵbind($outer$.items));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ export class Identifiers {
|
|||
|
||||
static elementProperty: o.ExternalReference = {name: 'ɵelementProperty', moduleName: CORE};
|
||||
|
||||
static flushHooksUpTo: o.ExternalReference = {name: 'ɵflushHooksUpTo', moduleName: CORE};
|
||||
|
||||
static componentHostSyntheticProperty:
|
||||
o.ExternalReference = {name: 'ɵcomponentHostSyntheticProperty', moduleName: CORE};
|
||||
|
||||
|
|
|
@ -120,6 +120,11 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
* all local refs and context variables are available for matching.
|
||||
*/
|
||||
private _updateCodeFns: (() => o.Statement)[] = [];
|
||||
/**
|
||||
* Memorizes the last node index for which a flushHooksUpTo instruction has been generated.
|
||||
* Initialized to 0 to avoid generating a useless flushHooksUpTo(0).
|
||||
*/
|
||||
private _lastNodeIndexWithFlush: number = 0;
|
||||
/** Temporary variable declarations generated from visiting pipes, literals, etc. */
|
||||
private _tempVariables: o.Statement[] = [];
|
||||
/**
|
||||
|
@ -451,10 +456,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
if (bindings.size) {
|
||||
bindings.forEach(binding => {
|
||||
this.updateInstruction(
|
||||
span, R3.i18nExp,
|
||||
index, span, R3.i18nExp,
|
||||
() => [this.convertPropertyBinding(o.variable(CONTEXT_NAME), binding)]);
|
||||
});
|
||||
this.updateInstruction(span, R3.i18nApply, [o.literal(index)]);
|
||||
this.updateInstruction(index, span, R3.i18nApply, [o.literal(index)]);
|
||||
}
|
||||
if (!selfClosing) {
|
||||
this.creationInstruction(span, R3.i18nEnd);
|
||||
|
@ -639,7 +644,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
converted.expressions.forEach(expression => {
|
||||
hasBindings = true;
|
||||
const binding = this.convertExpressionBinding(implicit, expression);
|
||||
this.updateInstruction(element.sourceSpan, R3.i18nExp, [binding]);
|
||||
this.updateInstruction(elementIndex, element.sourceSpan, R3.i18nExp, [binding]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +654,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
const args = this.constantPool.getConstLiteral(o.literalArr(i18nAttrArgs), true);
|
||||
this.creationInstruction(element.sourceSpan, R3.i18nAttributes, [index, args]);
|
||||
if (hasBindings) {
|
||||
this.updateInstruction(element.sourceSpan, R3.i18nApply, [index]);
|
||||
this.updateInstruction(elementIndex, element.sourceSpan, R3.i18nApply, [index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -711,7 +716,7 @@ 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(input.sourceSpan, R3.elementProperty, () => {
|
||||
this.updateInstruction(elementIndex, input.sourceSpan, R3.elementProperty, () => {
|
||||
return [
|
||||
o.literal(elementIndex), o.literal(bindingName),
|
||||
(hasValue ? this.convertPropertyBinding(implicit, value) : emptyValueBindInstruction)
|
||||
|
@ -737,7 +742,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
}
|
||||
}
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstruction(input.sourceSpan, instruction, () => {
|
||||
this.updateInstruction(elementIndex, input.sourceSpan, instruction, () => {
|
||||
return [
|
||||
o.literal(elementIndex), o.literal(attrName),
|
||||
this.convertPropertyBinding(implicit, value), ...params
|
||||
|
@ -839,7 +844,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
template.inputs.forEach(input => {
|
||||
const value = input.value.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstruction(template.sourceSpan, R3.elementProperty, () => {
|
||||
this.updateInstruction(templateIndex, template.sourceSpan, R3.elementProperty, () => {
|
||||
return [
|
||||
o.literal(templateIndex), o.literal(input.name),
|
||||
this.convertPropertyBinding(context, value)
|
||||
|
@ -880,7 +885,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
const value = text.value.visit(this._valueConverter);
|
||||
this.allocateBindingSlots(value);
|
||||
this.updateInstruction(
|
||||
text.sourceSpan, R3.textBinding,
|
||||
nodeIndex, text.sourceSpan, R3.textBinding,
|
||||
() => [o.literal(nodeIndex), this.convertPropertyBinding(o.variable(CONTEXT_NAME), value)]);
|
||||
}
|
||||
|
||||
|
@ -966,7 +971,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
if (createMode) {
|
||||
this.creationInstruction(instruction.sourceSpan, instruction.reference, paramsFn);
|
||||
} else {
|
||||
this.updateInstruction(instruction.sourceSpan, instruction.reference, paramsFn);
|
||||
this.updateInstruction(-1, instruction.sourceSpan, instruction.reference, paramsFn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -978,8 +983,12 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
|||
}
|
||||
|
||||
private updateInstruction(
|
||||
span: ParseSourceSpan|null, reference: o.ExternalReference,
|
||||
nodeIndex: number, span: ParseSourceSpan|null, reference: o.ExternalReference,
|
||||
paramsOrFn?: o.Expression[]|(() => o.Expression[])) {
|
||||
if (this._lastNodeIndexWithFlush < nodeIndex) {
|
||||
this.instructionFn(this._updateCodeFns, span, R3.flushHooksUpTo, [o.literal(nodeIndex)]);
|
||||
this._lastNodeIndexWithFlush = nodeIndex;
|
||||
}
|
||||
this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,7 @@ export {
|
|||
elementStyleProp as ɵelementStyleProp,
|
||||
elementStylingApply as ɵelementStylingApply,
|
||||
elementClassProp as ɵelementClassProp,
|
||||
flushHooksUpTo as ɵflushHooksUpTo,
|
||||
textBinding as ɵtextBinding,
|
||||
template as ɵtemplate,
|
||||
embeddedViewEnd as ɵembeddedViewEnd,
|
||||
|
|
|
@ -29,7 +29,7 @@ import {renderInitialClasses, renderInitialStyles} from './styling/class_and_sty
|
|||
import {publishDefaultGlobalUtils} from './util/global_utils';
|
||||
import {defaultScheduler, renderStringify} from './util/misc_utils';
|
||||
import {getRootContext, getRootView} from './util/view_traversal_utils';
|
||||
import {readPatchedLView} from './util/view_utils';
|
||||
import {readPatchedLView, resetPreOrderHookFlags} from './util/view_utils';
|
||||
|
||||
|
||||
|
||||
|
@ -142,6 +142,7 @@ export function renderComponent<T>(
|
|||
|
||||
refreshDescendantViews(rootView); // creation mode pass
|
||||
rootView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
resetPreOrderHookFlags(rootView);
|
||||
refreshDescendantViews(rootView); // update mode pass
|
||||
} finally {
|
||||
leaveView(oldView);
|
||||
|
@ -248,7 +249,7 @@ export function LifecycleHooksFeature(component: any, def: ComponentDef<any>): v
|
|||
const rootTView = readPatchedLView(component) ![TVIEW];
|
||||
const dirIndex = rootTView.data.length - 1;
|
||||
|
||||
registerPreOrderHooks(dirIndex, def, rootTView);
|
||||
registerPreOrderHooks(dirIndex, def, rootTView, -1, -1, -1);
|
||||
// TODO(misko): replace `as TNode` with createTNode call. (needs refactoring to lose dep on
|
||||
// LNode).
|
||||
registerPostOrderHooks(
|
||||
|
|
|
@ -10,7 +10,7 @@ import {assertEqual} from '../util/assert';
|
|||
|
||||
import {DirectiveDef} from './interfaces/definition';
|
||||
import {TNode} from './interfaces/node';
|
||||
import {FLAGS, HookData, InitPhaseState, LView, LViewFlags, TView} from './interfaces/view';
|
||||
import {FLAGS, HookData, InitPhaseState, LView, LViewFlags, PREORDER_HOOK_FLAGS, PreOrderHookFlags, TView} from './interfaces/view';
|
||||
|
||||
|
||||
|
||||
|
@ -19,34 +19,50 @@ import {FLAGS, HookData, InitPhaseState, LView, LViewFlags, TView} from './inter
|
|||
*
|
||||
* Must be run *only* on the first template pass.
|
||||
*
|
||||
* The TView's hooks arrays are arranged in alternating pairs of directiveIndex and hookFunction,
|
||||
* i.e.: `[directiveIndexA, hookFunctionA, directiveIndexB, hookFunctionB, ...]`. For `OnChanges`
|
||||
* hooks, the `directiveIndex` will be *negative*, signaling {@link callHooks} that the
|
||||
* `hookFunction` must be passed the the appropriate {@link SimpleChanges} object.
|
||||
* Sets up the pre-order hooks on the provided `tView`,
|
||||
* see {@link HookData} for details about the data structure.
|
||||
*
|
||||
* @param directiveIndex The index of the directive in LView
|
||||
* @param directiveDef The definition containing the hooks to setup in tView
|
||||
* @param tView The current TView
|
||||
* @param nodeIndex The index of the node to which the directive is attached
|
||||
* @param initialPreOrderHooksLength the number of pre-order hooks already registered before the
|
||||
* current process, used to know if the node index has to be added to the array. If it is -1,
|
||||
* the node index is never added.
|
||||
* @param initialPreOrderCheckHooksLength same as previous for pre-order check hooks
|
||||
*/
|
||||
export function registerPreOrderHooks(
|
||||
directiveIndex: number, directiveDef: DirectiveDef<any>, tView: TView): void {
|
||||
directiveIndex: number, directiveDef: DirectiveDef<any>, tView: TView, nodeIndex: number,
|
||||
initialPreOrderHooksLength: number, initialPreOrderCheckHooksLength: number): void {
|
||||
ngDevMode &&
|
||||
assertEqual(tView.firstTemplatePass, true, 'Should only be called on first template pass');
|
||||
|
||||
const {onChanges, onInit, doCheck} = directiveDef;
|
||||
if (initialPreOrderHooksLength >= 0 &&
|
||||
(!tView.preOrderHooks || initialPreOrderHooksLength === tView.preOrderHooks.length) &&
|
||||
(onChanges || onInit || doCheck)) {
|
||||
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(nodeIndex);
|
||||
}
|
||||
|
||||
if (initialPreOrderCheckHooksLength >= 0 &&
|
||||
(!tView.preOrderCheckHooks ||
|
||||
initialPreOrderCheckHooksLength === tView.preOrderCheckHooks.length) &&
|
||||
(onChanges || doCheck)) {
|
||||
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(nodeIndex);
|
||||
}
|
||||
|
||||
if (onChanges) {
|
||||
(tView.initHooks || (tView.initHooks = [])).push(directiveIndex, onChanges);
|
||||
(tView.checkHooks || (tView.checkHooks = [])).push(directiveIndex, onChanges);
|
||||
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, onChanges);
|
||||
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(directiveIndex, onChanges);
|
||||
}
|
||||
|
||||
if (onInit) {
|
||||
(tView.initHooks || (tView.initHooks = [])).push(-directiveIndex, onInit);
|
||||
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(-directiveIndex, onInit);
|
||||
}
|
||||
|
||||
if (doCheck) {
|
||||
(tView.initHooks || (tView.initHooks = [])).push(directiveIndex, doCheck);
|
||||
(tView.checkHooks || (tView.checkHooks = [])).push(directiveIndex, doCheck);
|
||||
(tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, doCheck);
|
||||
(tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(directiveIndex, doCheck);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,9 +75,8 @@ export function registerPreOrderHooks(
|
|||
* preserve hook execution order. Content, view, and destroy hooks for projected
|
||||
* components and directives must be called *before* their hosts.
|
||||
*
|
||||
* Sets up the content, view, and destroy hooks on the provided `tView` such that
|
||||
* they're added in alternating pairs of directiveIndex and hookFunction,
|
||||
* i.e.: `[directiveIndexA, hookFunctionA, directiveIndexB, hookFunctionB, ...]`
|
||||
* Sets up the content, view, and destroy hooks on the provided `tView`,
|
||||
* see {@link HookData} for details about the data structure.
|
||||
*
|
||||
* NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up
|
||||
* separately at `elementStart`.
|
||||
|
@ -103,25 +118,49 @@ export function registerPostOrderHooks(tView: TView, tNode: TNode): void {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executing hooks requires complex logic as we need to deal with 2 constraints.
|
||||
*
|
||||
* 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only
|
||||
* once, across many change detection cycles. This must be true even if some hooks throw, or if
|
||||
* some recursively trigger a change detection cycle.
|
||||
* To solve that, it is required to track the state of the execution of these init hooks.
|
||||
* This is done by storing and maintaining flags in the view: the {@link InitPhaseState},
|
||||
* and the index within that phase. They can be seen as a cursor in the following structure:
|
||||
* [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]
|
||||
* They are are stored as flags in LView[FLAGS].
|
||||
*
|
||||
* 2. Pre-order hooks can be executed in batches, because of the flushHooksUpTo instruction.
|
||||
* To be able to pause and resume their execution, we also need some state about the hook's array
|
||||
* that is being processed:
|
||||
* - the index of the next hook to be executed
|
||||
* - the number of init hooks already found in the processed part of the array
|
||||
* They are are stored as flags in LView[PREORDER_HOOK_FLAGS].
|
||||
*/
|
||||
|
||||
/**
|
||||
* Executes necessary hooks at the start of executing a template.
|
||||
*
|
||||
* Executes hooks that are to be run during the initialization of a directive such
|
||||
* as `onChanges`, `onInit`, and `doCheck`.
|
||||
*
|
||||
* Has the side effect of updating the RunInit flag in `lView` to be `0`, so that
|
||||
* this isn't run a second time.
|
||||
*
|
||||
* @param lView The current view
|
||||
* @param tView Static data for the view containing the hooks to be executed
|
||||
* @param checkNoChangesMode Whether or not we're in checkNoChanges mode.
|
||||
* @param @param currentNodeIndex 2 cases depending the the value:
|
||||
* - undefined: execute hooks only from the saved index until the end of the array (pre-order case,
|
||||
* when flushing the remaining hooks)
|
||||
* - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
||||
* case, when executing flushHooksUpTo(number))
|
||||
*/
|
||||
export function executeInitHooks(
|
||||
currentView: LView, tView: TView, checkNoChangesMode: boolean): void {
|
||||
export function executePreOrderHooks(
|
||||
currentView: LView, tView: TView, checkNoChangesMode: boolean,
|
||||
currentNodeIndex: number | undefined): void {
|
||||
if (!checkNoChangesMode) {
|
||||
executeHooks(
|
||||
currentView, tView.initHooks, tView.checkHooks, checkNoChangesMode,
|
||||
InitPhaseState.OnInitHooksToBeRun);
|
||||
currentView, tView.preOrderHooks, tView.preOrderCheckHooks, checkNoChangesMode,
|
||||
InitPhaseState.OnInitHooksToBeRun,
|
||||
currentNodeIndex !== undefined ? currentNodeIndex : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,24 +168,33 @@ export function executeInitHooks(
|
|||
* Executes hooks against the given `LView` based off of whether or not
|
||||
* This is the first pass.
|
||||
*
|
||||
* @param lView The view instance data to run the hooks against
|
||||
* @param currentView The view instance data to run the hooks against
|
||||
* @param firstPassHooks An array of hooks to run if we're in the first view pass
|
||||
* @param checkHooks An Array of hooks to run if we're not in the first view pass.
|
||||
* @param checkNoChangesMode Whether or not we're in no changes mode.
|
||||
* @param initPhaseState the current state of the init phase
|
||||
* @param currentNodeIndex 3 cases depending the the value:
|
||||
* - undefined: all hooks from the array should be executed (post-order case)
|
||||
* - null: execute hooks only from the saved index until the end of the array (pre-order case, when
|
||||
* flushing the remaining hooks)
|
||||
* - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
||||
* case, when executing flushHooksUpTo(number))
|
||||
*/
|
||||
export function executeHooks(
|
||||
currentView: LView, firstPassHooks: HookData | null, checkHooks: HookData | null,
|
||||
checkNoChangesMode: boolean, initPhase: number): void {
|
||||
checkNoChangesMode: boolean, initPhaseState: InitPhaseState,
|
||||
currentNodeIndex: number | null | undefined): void {
|
||||
if (checkNoChangesMode) return;
|
||||
const hooksToCall = (currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase ?
|
||||
const hooksToCall = (currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhaseState ?
|
||||
firstPassHooks :
|
||||
checkHooks;
|
||||
if (hooksToCall) {
|
||||
callHooks(currentView, hooksToCall, initPhase);
|
||||
callHooks(currentView, hooksToCall, initPhaseState, currentNodeIndex);
|
||||
}
|
||||
// The init phase state must be always checked here as it may have been recursively updated
|
||||
if ((currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase &&
|
||||
initPhase !== InitPhaseState.InitPhaseCompleted) {
|
||||
if (currentNodeIndex == null &&
|
||||
(currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhaseState &&
|
||||
initPhaseState !== InitPhaseState.InitPhaseCompleted) {
|
||||
currentView[FLAGS] &= LViewFlags.IndexWithinInitPhaseReset;
|
||||
currentView[FLAGS] += LViewFlags.InitPhaseStateIncrementer;
|
||||
}
|
||||
|
@ -158,25 +206,68 @@ export function executeHooks(
|
|||
*
|
||||
* @param currentView The current view
|
||||
* @param arr The array in which the hooks are found
|
||||
* @param initPhaseState the current state of the init phase
|
||||
* @param currentNodeIndex 3 cases depending the the value:
|
||||
* - undefined: all hooks from the array should be executed (post-order case)
|
||||
* - null: execute hooks only from the saved index until the end of the array (pre-order case, when
|
||||
* flushing the remaining hooks)
|
||||
* - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
||||
* case, when executing flushHooksUpTo(number))
|
||||
*/
|
||||
export function callHooks(currentView: LView, arr: HookData, initPhase?: number): void {
|
||||
let initHooksCount = 0;
|
||||
for (let i = 0; i < arr.length; i += 2) {
|
||||
const isInitHook = arr[i] < 0;
|
||||
const directiveIndex = isInitHook ? -arr[i] : arr[i] as number;
|
||||
const directive = currentView[directiveIndex];
|
||||
function callHooks(
|
||||
currentView: LView, arr: HookData, initPhase: InitPhaseState,
|
||||
currentNodeIndex: number | null | undefined): void {
|
||||
const startIndex = currentNodeIndex !== undefined ?
|
||||
(currentView[PREORDER_HOOK_FLAGS] & PreOrderHookFlags.IndexOfTheNextPreOrderHookMaskMask) :
|
||||
0;
|
||||
const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1;
|
||||
let lastNodeIndexFound = 0;
|
||||
for (let i = startIndex; i < arr.length; i++) {
|
||||
const hook = arr[i + 1] as() => void;
|
||||
if (isInitHook) {
|
||||
initHooksCount++;
|
||||
const indexWithintInitPhase = currentView[FLAGS] >> LViewFlags.IndexWithinInitPhaseShift;
|
||||
// The init phase state must be always checked here as it may have been recursively updated
|
||||
if (indexWithintInitPhase < initHooksCount &&
|
||||
(currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase) {
|
||||
currentView[FLAGS] += LViewFlags.IndexWithinInitPhaseIncrementer;
|
||||
hook.call(directive);
|
||||
if (typeof hook === 'number') {
|
||||
lastNodeIndexFound = arr[i] as number;
|
||||
if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
hook.call(directive);
|
||||
const isInitHook = arr[i] < 0;
|
||||
if (isInitHook)
|
||||
currentView[PREORDER_HOOK_FLAGS] += PreOrderHookFlags.NumberOfInitHooksCalledIncrementer;
|
||||
if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) {
|
||||
callHook(currentView, initPhase, arr, i);
|
||||
currentView[PREORDER_HOOK_FLAGS] =
|
||||
(currentView[PREORDER_HOOK_FLAGS] & PreOrderHookFlags.NumberOfInitHooksCalledMask) + i +
|
||||
2;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute one hook against the current `LView`.
|
||||
*
|
||||
* @param currentView The current view
|
||||
* @param initPhaseState the current state of the init phase
|
||||
* @param arr The array in which the hooks are found
|
||||
* @param i The current index within the hook data array
|
||||
*/
|
||||
function callHook(currentView: LView, initPhase: InitPhaseState, arr: HookData, i: number) {
|
||||
const isInitHook = arr[i] < 0;
|
||||
const hook = arr[i + 1] as() => void;
|
||||
const directiveIndex = isInitHook ? -arr[i] : arr[i] as number;
|
||||
const directive = currentView[directiveIndex];
|
||||
if (isInitHook) {
|
||||
const indexWithintInitPhase = currentView[FLAGS] >> LViewFlags.IndexWithinInitPhaseShift;
|
||||
// The init phase state must be always checked here as it may have been recursively
|
||||
// updated
|
||||
if (indexWithintInitPhase <
|
||||
(currentView[PREORDER_HOOK_FLAGS] >> PreOrderHookFlags.NumberOfInitHooksCalledShift) &&
|
||||
(currentView[FLAGS] & LViewFlags.InitPhaseStateMask) === initPhase) {
|
||||
currentView[FLAGS] += LViewFlags.IndexWithinInitPhaseIncrementer;
|
||||
hook.call(directive);
|
||||
}
|
||||
} else {
|
||||
hook.call(directive);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@ export {
|
|||
elementStyleProp,
|
||||
elementStylingApply,
|
||||
|
||||
flushHooksUpTo,
|
||||
|
||||
listener,
|
||||
store,
|
||||
load,
|
||||
|
|
|
@ -24,7 +24,7 @@ import {attachPatchData, getComponentViewByInstance} from './context_discovery';
|
|||
import {attachLContainerDebug, attachLViewDebug} from './debug';
|
||||
import {diPublicInInjector, getNodeInjectable, getOrCreateInjectable, getOrCreateNodeInjectorForNode, injectAttributeImpl} from './di';
|
||||
import {throwMultipleComponentError} from './errors';
|
||||
import {executeHooks, executeInitHooks, registerPostOrderHooks, registerPreOrderHooks} from './hooks';
|
||||
import {executeHooks, executePreOrderHooks, registerPostOrderHooks, registerPreOrderHooks} from './hooks';
|
||||
import {ACTIVE_INDEX, LContainer, VIEWS} from './interfaces/container';
|
||||
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from './interfaces/definition';
|
||||
import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from './interfaces/injector';
|
||||
|
@ -48,7 +48,7 @@ import {NO_CHANGE} from './tokens';
|
|||
import {attrsStylingIndexOf, setUpAttributes} from './util/attrs_utils';
|
||||
import {INTERPOLATION_DELIMITER, renderStringify} from './util/misc_utils';
|
||||
import {findComponentView, getLViewParent, getRootContext, getRootView} from './util/view_traversal_utils';
|
||||
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, loadInternal, readPatchedLView, unwrapRNode, viewAttachedToChangeDetector} from './util/view_utils';
|
||||
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, isComponent, isComponentDef, isContentQueryHost, isRootView, loadInternal, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from './util/view_utils';
|
||||
|
||||
|
||||
|
||||
|
@ -84,16 +84,17 @@ export function refreshDescendantViews(lView: LView) {
|
|||
if (!creationMode) {
|
||||
const checkNoChangesMode = getCheckNoChangesMode();
|
||||
|
||||
executeInitHooks(lView, tView, checkNoChangesMode);
|
||||
executePreOrderHooks(lView, tView, checkNoChangesMode, undefined);
|
||||
|
||||
refreshDynamicEmbeddedViews(lView);
|
||||
|
||||
// Content query results must be refreshed before content hooks are called.
|
||||
refreshContentQueries(tView, lView);
|
||||
|
||||
resetPreOrderHookFlags(lView);
|
||||
executeHooks(
|
||||
lView, tView.contentHooks, tView.contentCheckHooks, checkNoChangesMode,
|
||||
InitPhaseState.AfterContentInitHooksToBeRun);
|
||||
InitPhaseState.AfterContentInitHooksToBeRun, undefined);
|
||||
|
||||
setHostBindings(tView, lView);
|
||||
}
|
||||
|
@ -180,6 +181,7 @@ export function createLView<T>(
|
|||
const lView = tView.blueprint.slice() as LView;
|
||||
lView[HOST] = host;
|
||||
lView[FLAGS] = flags | LViewFlags.CreationMode | LViewFlags.Attached | LViewFlags.FirstLViewPass;
|
||||
resetPreOrderHookFlags(lView);
|
||||
lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
|
||||
lView[CONTEXT] = context;
|
||||
lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]) !;
|
||||
|
@ -405,6 +407,7 @@ export function renderEmbeddedTemplate<T>(viewToRender: LView, tView: TView, con
|
|||
setPreviousOrParentTNode(null !);
|
||||
|
||||
oldView = enterView(viewToRender, viewToRender[T_HOST]);
|
||||
resetPreOrderHookFlags(viewToRender);
|
||||
namespaceHTML();
|
||||
tView.template !(getRenderFlags(viewToRender), context);
|
||||
// This must be set to false immediately after the first creation run because in an
|
||||
|
@ -459,6 +462,7 @@ function renderComponentOrTemplate<T>(
|
|||
}
|
||||
|
||||
// update mode pass
|
||||
resetPreOrderHookFlags(hostView);
|
||||
templateFn && templateFn(RenderFlags.Update, context);
|
||||
refreshDescendantViews(hostView);
|
||||
} finally {
|
||||
|
@ -807,8 +811,8 @@ export function createTView(
|
|||
firstTemplatePass: true,
|
||||
staticViewQueries: false,
|
||||
staticContentQueries: false,
|
||||
initHooks: null,
|
||||
checkHooks: null,
|
||||
preOrderHooks: null,
|
||||
preOrderCheckHooks: null,
|
||||
contentHooks: null,
|
||||
contentCheckHooks: null,
|
||||
viewHooks: null,
|
||||
|
@ -1056,6 +1060,17 @@ export function elementEnd(): void {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flushes all the lifecycle hooks for directives up until (and excluding) that node index
|
||||
*
|
||||
* @param index The index of the element in the `LView`
|
||||
*/
|
||||
export function flushHooksUpTo(index: number): void {
|
||||
const lView = getLView();
|
||||
executePreOrderHooks(lView, lView[TVIEW], getCheckNoChangesMode(), index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the value of removes an attribute on an Element.
|
||||
*
|
||||
|
@ -1748,6 +1763,10 @@ function resolveDirectives(
|
|||
if (def.providersResolver) def.providersResolver(def);
|
||||
}
|
||||
generateExpandoInstructionBlock(tView, tNode, directives.length);
|
||||
const initialPreOrderHooksLength = (tView.preOrderHooks && tView.preOrderHooks.length) || 0;
|
||||
const initialPreOrderCheckHooksLength =
|
||||
(tView.preOrderCheckHooks && tView.preOrderCheckHooks.length) || 0;
|
||||
const nodeIndex = tNode.index - HEADER_OFFSET;
|
||||
for (let i = 0; i < directives.length; i++) {
|
||||
const def = directives[i] as DirectiveDef<any>;
|
||||
|
||||
|
@ -1758,7 +1777,9 @@ function resolveDirectives(
|
|||
|
||||
// Init hooks are queued now so ngOnInit is called in host components before
|
||||
// any projected components.
|
||||
registerPreOrderHooks(directiveDefIdx, def, tView);
|
||||
registerPreOrderHooks(
|
||||
directiveDefIdx, def, tView, nodeIndex, initialPreOrderHooksLength,
|
||||
initialPreOrderCheckHooksLength);
|
||||
}
|
||||
}
|
||||
if (exportsMap) cacheMatchingLocalNames(tNode, localRefs, exportsMap);
|
||||
|
@ -2276,7 +2297,7 @@ export function containerRefreshStart(index: number): void {
|
|||
|
||||
// We need to execute init hooks here so ngOnInit hooks are called in top level views
|
||||
// before they are called in embedded views (for backwards compatibility).
|
||||
executeInitHooks(lView, tView, getCheckNoChangesMode());
|
||||
executePreOrderHooks(lView, tView, getCheckNoChangesMode(), undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2440,6 +2461,7 @@ export function embeddedViewEnd(): void {
|
|||
refreshDescendantViews(lView); // creation mode pass
|
||||
lView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
}
|
||||
resetPreOrderHookFlags(lView);
|
||||
refreshDescendantViews(lView); // update mode pass
|
||||
const lContainer = lView[PARENT] as LContainer;
|
||||
ngDevMode && assertLContainerOrUndefined(lContainer);
|
||||
|
@ -2834,6 +2856,7 @@ export function checkView<T>(hostView: LView, component: T) {
|
|||
const creationMode = isCreationMode(hostView);
|
||||
|
||||
try {
|
||||
resetPreOrderHookFlags(hostView);
|
||||
namespaceHTML();
|
||||
creationMode && executeViewQueryFn(RenderFlags.Create, hostTView, component);
|
||||
templateFn(getRenderFlags(hostView), component);
|
||||
|
|
|
@ -45,8 +45,9 @@ export const CHILD_HEAD = 14;
|
|||
export const CHILD_TAIL = 15;
|
||||
export const CONTENT_QUERIES = 16;
|
||||
export const DECLARATION_VIEW = 17;
|
||||
export const PREORDER_HOOK_FLAGS = 18;
|
||||
/** Size of LView's header. Necessary to adjust for it when setting slots. */
|
||||
export const HEADER_OFFSET = 19;
|
||||
export const HEADER_OFFSET = 20;
|
||||
|
||||
|
||||
// This interface replaces the real LView interface if it is an arg or a
|
||||
|
@ -215,6 +216,11 @@ export interface LView extends Array<any> {
|
|||
* context.
|
||||
*/
|
||||
[DECLARATION_VIEW]: LView|null;
|
||||
|
||||
/**
|
||||
* More flags for this view. See PreOrderHookFlags for more info.
|
||||
*/
|
||||
[PREORDER_HOOK_FLAGS]: PreOrderHookFlags;
|
||||
}
|
||||
|
||||
/** Flags associated with an LView (saved in LView[FLAGS]) */
|
||||
|
@ -296,6 +302,20 @@ export const enum InitPhaseState {
|
|||
InitPhaseCompleted = 0b11,
|
||||
}
|
||||
|
||||
/** More flags associated with an LView (saved in LView[FLAGS_MORE]) */
|
||||
export const enum PreOrderHookFlags {
|
||||
/** The index of the next pre-order hook to be called in the hooks array, on the first 16
|
||||
bits */
|
||||
IndexOfTheNextPreOrderHookMaskMask = 0b01111111111111111,
|
||||
|
||||
/**
|
||||
* The number of init hooks that have already been called, on the last 16 bits
|
||||
*/
|
||||
NumberOfInitHooksCalledIncrementer = 0b010000000000000000,
|
||||
NumberOfInitHooksCalledShift = 16,
|
||||
NumberOfInitHooksCalledMask = 0b11111111111111110000000000000000,
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of instructions used to process host bindings efficiently.
|
||||
*
|
||||
|
@ -438,21 +458,21 @@ export interface TView {
|
|||
pipeRegistry: PipeDefList|null;
|
||||
|
||||
/**
|
||||
* Array of ngOnInit and ngDoCheck hooks that should be executed for this view in
|
||||
* Array of ngOnInit, ngOnChanges and ngDoCheck hooks that should be executed for this view in
|
||||
* creation mode.
|
||||
*
|
||||
* Even indices: Directive index
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
initHooks: HookData|null;
|
||||
preOrderHooks: HookData|null;
|
||||
|
||||
/**
|
||||
* Array of ngDoCheck hooks that should be executed for this view in update mode.
|
||||
* Array of ngOnChanges and ngDoCheck hooks that should be executed for this view in update mode.
|
||||
*
|
||||
* Even indices: Directive index
|
||||
* Odd indices: Hook function
|
||||
*/
|
||||
checkHooks: HookData|null;
|
||||
preOrderCheckHooks: HookData|null;
|
||||
|
||||
/**
|
||||
* Array of ngAfterContentInit and ngAfterContentChecked hooks that should be executed
|
||||
|
@ -591,8 +611,14 @@ export interface RootContext {
|
|||
/**
|
||||
* Array of hooks that should be executed for a view and their directive indices.
|
||||
*
|
||||
* Even indices: Directive index
|
||||
* Odd indices: Hook function
|
||||
* For each node of the view, the following data is stored:
|
||||
* 1) Node index (optional)
|
||||
* 2) A series of number/function pairs where:
|
||||
* - even indices are directive indices
|
||||
* - odd indices are hook functions
|
||||
*
|
||||
* Special cases:
|
||||
* - a negative directive index flags an init hook (ngOnInit, ngAfterContentInit, ngAfterViewInit)
|
||||
*/
|
||||
export type HookData = (number | (() => void))[];
|
||||
|
||||
|
|
|
@ -99,6 +99,7 @@ export const angularCoreEnv: {[name: string]: Function} = {
|
|||
'ɵelementStylingMap': r3.elementStylingMap,
|
||||
'ɵelementStyleProp': r3.elementStyleProp,
|
||||
'ɵelementStylingApply': r3.elementStylingApply,
|
||||
'ɵflushHooksUpTo': r3.flushHooksUpTo,
|
||||
'ɵtemplate': r3.template,
|
||||
'ɵtext': r3.text,
|
||||
'ɵtextBinding': r3.textBinding,
|
||||
|
|
|
@ -13,6 +13,7 @@ import {executeHooks} from './hooks';
|
|||
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
||||
import {TElementNode, TNode, TViewNode} from './interfaces/node';
|
||||
import {BINDING_INDEX, CONTEXT, DECLARATION_VIEW, FLAGS, InitPhaseState, LView, LViewFlags, OpaqueViewState, TVIEW} from './interfaces/view';
|
||||
import {resetPreOrderHookFlags} from './util/view_utils';
|
||||
|
||||
|
||||
|
||||
|
@ -304,9 +305,10 @@ export function leaveView(newView: LView): void {
|
|||
lView[FLAGS] &= ~LViewFlags.CreationMode;
|
||||
} else {
|
||||
try {
|
||||
resetPreOrderHookFlags(lView);
|
||||
executeHooks(
|
||||
lView, tView.viewHooks, tView.viewCheckHooks, checkNoChangesMode,
|
||||
InitPhaseState.AfterViewInitHooksToBeRun);
|
||||
InitPhaseState.AfterViewInitHooksToBeRun, undefined);
|
||||
} finally {
|
||||
// Views are clean and in update mode after being checked, so these bits are cleared
|
||||
lView[FLAGS] &= ~(LViewFlags.Dirty | LViewFlags.FirstLViewPass);
|
||||
|
|
|
@ -13,7 +13,7 @@ import {ComponentDef, DirectiveDef} from '../interfaces/definition';
|
|||
import {TNode, TNodeFlags} from '../interfaces/node';
|
||||
import {RNode} from '../interfaces/renderer';
|
||||
import {StylingContext} from '../interfaces/styling';
|
||||
import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, TData, TVIEW} from '../interfaces/view';
|
||||
import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, TData, TVIEW} from '../interfaces/view';
|
||||
|
||||
|
||||
|
||||
|
@ -197,3 +197,11 @@ export function viewAttachedToChangeDetector(view: LView): boolean {
|
|||
export function viewAttachedToContainer(view: LView): boolean {
|
||||
return isLContainer(view[PARENT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the pre-order hook flags of the view.
|
||||
* @param lView the LView on which the flags are reset
|
||||
*/
|
||||
export function resetPreOrderHookFlags(lView: LView) {
|
||||
lView[PREORDER_HOOK_FLAGS] = 0;
|
||||
}
|
||||
|
|
|
@ -6,14 +6,18 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, Directive, Input, Type} from '@angular/core';
|
||||
import {Component, Directive, DoCheck, Input, OnChanges, OnInit, SimpleChanges, Type} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {onlyInIvy} from '@angular/private/testing';
|
||||
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('exports', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule(
|
||||
{declarations: [AppComp, ComponentToReference, DirToReference, DirWithCompInput]});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
AppComp, ComponentToReference, DirToReference, DirToReferenceWithPreOrderHooks,
|
||||
DirWithCompInput
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should support export of DOM element', () => {
|
||||
|
@ -36,6 +40,68 @@ describe('exports', () => {
|
|||
expect(fixture.nativeElement.innerHTML).toEqual('<div dir=""></div> Drew');
|
||||
});
|
||||
|
||||
describe('input changes in hooks', () => {
|
||||
it('should support forward reference', () => {
|
||||
const fixture = initWithTemplate(
|
||||
AppComp, '<div dirOnChange #myDir="dirOnChange" [in]="true"></div> {{ myDir.name }}');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.innerHTML)
|
||||
.toEqual('<div dironchange="" ng-reflect-in="true" title="Drew!?@"></div> Drew!?@');
|
||||
});
|
||||
|
||||
modifiedInIvy('Supporting input changes in hooks is limited in Ivy')
|
||||
.it('should support backward reference', () => {
|
||||
const fixture = initWithTemplate(
|
||||
AppComp, '{{ myDir.name }} <div dirOnChange #myDir="dirOnChange" [in]="true"></div>');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.innerHTML)
|
||||
.toEqual('Drew!?@ <div dironchange="" ng-reflect-in="true" title="Drew!?@"></div>');
|
||||
});
|
||||
|
||||
onlyInIvy('Supporting input changes in hooks is limited in Ivy')
|
||||
.it('should not support backward reference', () => {
|
||||
expect(() => {
|
||||
const fixture = initWithTemplate(
|
||||
AppComp,
|
||||
'{{ myDir.name }} <div dirOnChange #myDir="dirOnChange" [in]="true"></div>');
|
||||
fixture.detectChanges();
|
||||
})
|
||||
.toThrowError(
|
||||
/ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked/);
|
||||
});
|
||||
|
||||
modifiedInIvy('Supporting input changes in hooks is limited in Ivy')
|
||||
.it('should support reference on the same node', () => {
|
||||
const fixture = initWithTemplate(
|
||||
AppComp,
|
||||
'<div dirOnChange #myDir="dirOnChange" [in]="true" [id]="myDir.name"></div>');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.innerHTML)
|
||||
.toEqual(
|
||||
'<div dironchange="" ng-reflect-in="true" id="Drew!?@" title="Drew!?@"></div>');
|
||||
});
|
||||
|
||||
onlyInIvy('Supporting input changes in hooks is limited in Ivy')
|
||||
.it('should not support reference on the same node', () => {
|
||||
expect(() => {
|
||||
const fixture = initWithTemplate(
|
||||
AppComp,
|
||||
'<div dirOnChange #myDir="dirOnChange" [in]="true" [id]="myDir.name"></div>');
|
||||
fixture.detectChanges();
|
||||
})
|
||||
.toThrowError(
|
||||
/ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked/);
|
||||
});
|
||||
|
||||
it('should support input referenced by host binding on that directive', () => {
|
||||
const fixture =
|
||||
initWithTemplate(AppComp, '<div dirOnChange #myDir="dirOnChange" [in]="true"></div>');
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.innerHTML)
|
||||
.toEqual('<div dironchange="" ng-reflect-in="true" title="Drew!?@"></div>');
|
||||
});
|
||||
});
|
||||
|
||||
onlyInIvy('Different error message is thrown in View Engine')
|
||||
.it('should throw if export name is not found', () => {
|
||||
expect(() => {
|
||||
|
@ -95,3 +161,12 @@ class DirToReference {
|
|||
class DirWithCompInput {
|
||||
@Input('dirWithInput') comp: ComponentToReference|null = null;
|
||||
}
|
||||
|
||||
@Directive({selector: '[dirOnChange]', exportAs: 'dirOnChange', host: {'[title]': 'name'}})
|
||||
class DirToReferenceWithPreOrderHooks implements OnInit, OnChanges, DoCheck {
|
||||
@Input() in : any = null;
|
||||
name = 'Drew';
|
||||
ngOnChanges(changes: SimpleChanges) { this.name += '!'; }
|
||||
ngOnInit() { this.name += '?'; }
|
||||
ngDoCheck() { this.name += '@'; }
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
|
||||
import {Component, Directive, Input, OnChanges, SimpleChanges} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
describe('ngOnChanges', () => {
|
||||
|
@ -56,4 +56,116 @@ describe('ngOnChanges', () => {
|
|||
fixture.detectChanges();
|
||||
expect(log).toEqual(['c: 0 -> 3']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should call all hooks in correct order when several directives on same node', () => {
|
||||
let log: string[] = [];
|
||||
|
||||
class AllHooks {
|
||||
id: number = -1;
|
||||
|
||||
/** @internal */
|
||||
private _log(hook: string, id: number) { log.push(hook + id); }
|
||||
|
||||
ngOnChanges() { this._log('onChanges', this.id); }
|
||||
ngOnInit() { this._log('onInit', this.id); }
|
||||
ngDoCheck() { this._log('doCheck', this.id); }
|
||||
ngAfterContentInit() { this._log('afterContentInit', this.id); }
|
||||
ngAfterContentChecked() { this._log('afterContentChecked', this.id); }
|
||||
ngAfterViewInit() { this._log('afterViewInit', this.id); }
|
||||
ngAfterViewChecked() { this._log('afterViewChecked', this.id); }
|
||||
}
|
||||
|
||||
@Directive({selector: 'div'})
|
||||
class DirA extends AllHooks {
|
||||
@Input('a') id: number = 0;
|
||||
}
|
||||
|
||||
@Directive({selector: 'div'})
|
||||
class DirB extends AllHooks {
|
||||
@Input('b') id: number = 0;
|
||||
}
|
||||
|
||||
@Directive({selector: 'div'})
|
||||
class DirC extends AllHooks {
|
||||
@Input('c') id: number = 0;
|
||||
}
|
||||
|
||||
@Component({selector: 'app-comp', template: '<div [a]="1" [b]="2" [c]="3"></div>'})
|
||||
class AppComp {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [AppComp, DirA, DirB, DirC]});
|
||||
const fixture = TestBed.createComponent(AppComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(log).toEqual([
|
||||
'onChanges1',
|
||||
'onInit1',
|
||||
'doCheck1',
|
||||
'onChanges2',
|
||||
'onInit2',
|
||||
'doCheck2',
|
||||
'onChanges3',
|
||||
'onInit3',
|
||||
'doCheck3',
|
||||
'afterContentInit1',
|
||||
'afterContentChecked1',
|
||||
'afterContentInit2',
|
||||
'afterContentChecked2',
|
||||
'afterContentInit3',
|
||||
'afterContentChecked3',
|
||||
'afterViewInit1',
|
||||
'afterViewChecked1',
|
||||
'afterViewInit2',
|
||||
'afterViewChecked2',
|
||||
'afterViewInit3',
|
||||
'afterViewChecked3'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should call hooks after setting directives inputs', () => {
|
||||
let log: string[] = [];
|
||||
|
||||
@Directive({selector: 'div'})
|
||||
class DirA {
|
||||
@Input() a: number = 0;
|
||||
ngOnInit() { log.push('onInitA' + this.a); }
|
||||
}
|
||||
|
||||
@Directive({selector: 'div'})
|
||||
class DirB {
|
||||
@Input() b: number = 0;
|
||||
ngOnInit() { log.push('onInitB' + this.b); }
|
||||
ngDoCheck() { log.push('doCheckB' + this.b); }
|
||||
}
|
||||
|
||||
@Directive({selector: 'div'})
|
||||
class DirC {
|
||||
@Input() c: number = 0;
|
||||
ngOnInit() { log.push('onInitC' + this.c); }
|
||||
ngDoCheck() { log.push('doCheckC' + this.c); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-comp',
|
||||
template: '<div [a]="id" [b]="id" [c]="id"></div><div [a]="id" [b]="id" [c]="id"></div>'
|
||||
})
|
||||
class AppComp {
|
||||
id = 0;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [AppComp, DirA, DirB, DirC]});
|
||||
const fixture = TestBed.createComponent(AppComp);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(log).toEqual([
|
||||
'onInitA0', 'onInitB0', 'doCheckB0', 'onInitC0', 'doCheckC0', 'onInitA0', 'onInitB0',
|
||||
'doCheckB0', 'onInitC0', 'doCheckC0'
|
||||
]);
|
||||
|
||||
log = [];
|
||||
fixture.componentInstance.id = 1;
|
||||
fixture.detectChanges();
|
||||
expect(log).toEqual(['doCheckB1', 'doCheckC1', 'doCheckB1', 'doCheckC1']);
|
||||
});
|
||||
|
|
|
@ -107,6 +107,9 @@
|
|||
{
|
||||
"name": "PARENT_INJECTOR"
|
||||
},
|
||||
{
|
||||
"name": "PREORDER_HOOK_FLAGS"
|
||||
},
|
||||
{
|
||||
"name": "QUERIES"
|
||||
},
|
||||
|
@ -194,6 +197,9 @@
|
|||
{
|
||||
"name": "cacheMatchingLocalNames"
|
||||
},
|
||||
{
|
||||
"name": "callHook"
|
||||
},
|
||||
{
|
||||
"name": "callHooks"
|
||||
},
|
||||
|
@ -282,7 +288,7 @@
|
|||
"name": "executeHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeInitHooks"
|
||||
"name": "executePreOrderHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeViewQueryFn"
|
||||
|
@ -599,6 +605,9 @@
|
|||
{
|
||||
"name": "resetComponentState"
|
||||
},
|
||||
{
|
||||
"name": "resetPreOrderHookFlags"
|
||||
},
|
||||
{
|
||||
"name": "resolveDirectives"
|
||||
},
|
||||
|
|
|
@ -92,6 +92,9 @@
|
|||
{
|
||||
"name": "PARENT_INJECTOR"
|
||||
},
|
||||
{
|
||||
"name": "PREORDER_HOOK_FLAGS"
|
||||
},
|
||||
{
|
||||
"name": "RENDERER"
|
||||
},
|
||||
|
@ -149,6 +152,9 @@
|
|||
{
|
||||
"name": "bloomAdd"
|
||||
},
|
||||
{
|
||||
"name": "callHook"
|
||||
},
|
||||
{
|
||||
"name": "callHooks"
|
||||
},
|
||||
|
@ -207,7 +213,7 @@
|
|||
"name": "executeHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeInitHooks"
|
||||
"name": "executePreOrderHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeViewQueryFn"
|
||||
|
@ -428,6 +434,9 @@
|
|||
{
|
||||
"name": "resetComponentState"
|
||||
},
|
||||
{
|
||||
"name": "resetPreOrderHookFlags"
|
||||
},
|
||||
{
|
||||
"name": "setBindingRoot"
|
||||
},
|
||||
|
|
|
@ -179,6 +179,9 @@
|
|||
{
|
||||
"name": "PARENT_INJECTOR"
|
||||
},
|
||||
{
|
||||
"name": "PREORDER_HOOK_FLAGS"
|
||||
},
|
||||
{
|
||||
"name": "QUERIES"
|
||||
},
|
||||
|
@ -431,6 +434,9 @@
|
|||
{
|
||||
"name": "cacheMatchingLocalNames"
|
||||
},
|
||||
{
|
||||
"name": "callHook"
|
||||
},
|
||||
{
|
||||
"name": "callHooks"
|
||||
},
|
||||
|
@ -584,15 +590,15 @@
|
|||
{
|
||||
"name": "executeHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeInitHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeNodeAction"
|
||||
},
|
||||
{
|
||||
"name": "executeOnDestroys"
|
||||
},
|
||||
{
|
||||
"name": "executePreOrderHooks"
|
||||
},
|
||||
{
|
||||
"name": "executeViewQueryFn"
|
||||
},
|
||||
|
@ -620,6 +626,9 @@
|
|||
{
|
||||
"name": "findViaComponent"
|
||||
},
|
||||
{
|
||||
"name": "flushHooksUpTo"
|
||||
},
|
||||
{
|
||||
"name": "forwardRef"
|
||||
},
|
||||
|
@ -1157,6 +1166,9 @@
|
|||
{
|
||||
"name": "resetComponentState"
|
||||
},
|
||||
{
|
||||
"name": "resetPreOrderHookFlags"
|
||||
},
|
||||
{
|
||||
"name": "resolveDirectives"
|
||||
},
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import {ComponentFactoryResolver, OnDestroy, SimpleChange, SimpleChanges, ViewContainerRef} from '../../src/core';
|
||||
import {AttributeMarker, ComponentTemplate, LifecycleHooksFeature, NO_CHANGE, NgOnChangesFeature, defineComponent, defineDirective, injectComponentFactoryResolver} from '../../src/render3/index';
|
||||
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, listener, markDirty, projection, projectionDef, store, template, text} from '../../src/render3/instructions';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, directiveInject, element, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, flushHooksUpTo, listener, markDirty, projection, projectionDef, store, template, text} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
|
||||
import {NgIf} from './common_with_def';
|
||||
|
@ -139,6 +139,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 2);
|
||||
}
|
||||
}, 2, 0, directives);
|
||||
|
@ -289,8 +290,11 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 2);
|
||||
flushHooksUpTo(3);
|
||||
elementProperty(3, 'val', 2);
|
||||
}
|
||||
}, 4, 0, directives);
|
||||
|
@ -345,6 +349,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 5);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
|
@ -385,6 +390,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 5);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
|
@ -623,6 +629,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(3);
|
||||
elementProperty(3, 'val', 4);
|
||||
containerRefreshStart(2);
|
||||
{
|
||||
|
@ -746,6 +753,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 2);
|
||||
}
|
||||
}, 4, 0, directives);
|
||||
|
@ -814,8 +822,11 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 1);
|
||||
flushHooksUpTo(3);
|
||||
elementProperty(3, 'val', 2);
|
||||
flushHooksUpTo(4);
|
||||
elementProperty(4, 'val', 2);
|
||||
}
|
||||
}, 6, 0, directives);
|
||||
|
@ -844,6 +855,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(3);
|
||||
elementProperty(3, 'val', 4);
|
||||
containerRefreshStart(2);
|
||||
{
|
||||
|
@ -1091,6 +1103,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 2);
|
||||
}
|
||||
}, 2, 0, defs);
|
||||
|
@ -1138,8 +1151,11 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 2);
|
||||
flushHooksUpTo(3);
|
||||
elementProperty(3, 'val', 2);
|
||||
}
|
||||
}, 4, 0, defs);
|
||||
|
@ -1162,6 +1178,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', bind(ctx.val));
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', bind(ctx.val));
|
||||
}
|
||||
}, 2, 2, [Comp, ProjectedComp]);
|
||||
|
@ -1177,6 +1194,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 2);
|
||||
}
|
||||
}, 2, 0, [ParentComp]);
|
||||
|
@ -1201,6 +1219,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 4);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
|
@ -1240,6 +1259,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 4);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
|
@ -1325,6 +1345,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 4);
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
|
@ -1486,6 +1507,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf1 & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', bind('1'));
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', bind('2'));
|
||||
}
|
||||
embeddedViewEnd();
|
||||
|
@ -1602,8 +1624,11 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf1 & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 1);
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', 2);
|
||||
flushHooksUpTo(3);
|
||||
elementProperty(3, 'val', 2);
|
||||
}
|
||||
embeddedViewEnd();
|
||||
|
@ -1648,6 +1673,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf1 & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', bind('1'));
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', bind('3'));
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
|
@ -1741,6 +1767,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf1 & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', bind('1'));
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', bind('5'));
|
||||
containerRefreshStart(1);
|
||||
{
|
||||
|
@ -2134,6 +2161,7 @@ describe('lifecycles', () => {
|
|||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val1', bind(1));
|
||||
elementProperty(0, 'publicVal2', bind(1));
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val1', bind(2));
|
||||
elementProperty(1, 'publicVal2', bind(2));
|
||||
}
|
||||
|
@ -2271,6 +2299,7 @@ describe('lifecycles', () => {
|
|||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val1', bind(1));
|
||||
elementProperty(0, 'publicVal2', bind(1));
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val1', bind(2));
|
||||
elementProperty(1, 'publicVal2', bind(2));
|
||||
}
|
||||
|
@ -2318,10 +2347,13 @@ describe('lifecycles', () => {
|
|||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val1', bind(1));
|
||||
elementProperty(0, 'publicVal2', bind(1));
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val1', bind(2));
|
||||
elementProperty(1, 'publicVal2', bind(2));
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val1', bind(3));
|
||||
elementProperty(2, 'publicVal2', bind(3));
|
||||
flushHooksUpTo(3);
|
||||
elementProperty(3, 'val1', bind(4));
|
||||
elementProperty(3, 'publicVal2', bind(4));
|
||||
}
|
||||
|
@ -2452,6 +2484,7 @@ describe('lifecycles', () => {
|
|||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val1', bind(1));
|
||||
elementProperty(0, 'publicVal2', bind(1));
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val1', bind(5));
|
||||
elementProperty(2, 'publicVal2', bind(5));
|
||||
containerRefreshStart(1);
|
||||
|
@ -2538,6 +2571,7 @@ describe('lifecycles', () => {
|
|||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val1', bind(1));
|
||||
elementProperty(0, 'publicVal2', bind(1));
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val1', bind(5));
|
||||
elementProperty(2, 'publicVal2', bind(5));
|
||||
containerRefreshStart(1);
|
||||
|
@ -2757,6 +2791,7 @@ describe('lifecycles', () => {
|
|||
// even though the *value* itself never changed.
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 2);
|
||||
}
|
||||
}, 2, 0, [Comp]);
|
||||
|
@ -2800,6 +2835,7 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', 1);
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', 2);
|
||||
}
|
||||
}, 2, 0, [Parent]);
|
||||
|
@ -2843,6 +2879,7 @@ describe('lifecycles', () => {
|
|||
element(1, 'view');
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', bind(ctx.val));
|
||||
}
|
||||
}, 2, 1, [View]);
|
||||
|
@ -2866,8 +2903,11 @@ describe('lifecycles', () => {
|
|||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementProperty(0, 'val', bind(1));
|
||||
flushHooksUpTo(1);
|
||||
elementProperty(1, 'val', bind(1));
|
||||
flushHooksUpTo(2);
|
||||
elementProperty(2, 'val', bind(2));
|
||||
flushHooksUpTo(3);
|
||||
elementProperty(3, 'val', bind(2));
|
||||
}
|
||||
}, 4, 4, [Parent, Content]);
|
||||
|
|
|
@ -6,43 +6,41 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {fixmeIvy} from '@angular/private/testing';
|
||||
import {ElementArrayFinder, ElementFinder, browser, by, element} from 'protractor';
|
||||
|
||||
import {verifyNoBrowserErrors} from '../../../../test-utils';
|
||||
|
||||
fixmeIvy('FW-1051: Directives are updated after the execution of the template function')
|
||||
.describe('simpleNgModel example', () => {
|
||||
afterEach(verifyNoBrowserErrors);
|
||||
let input: ElementFinder;
|
||||
let paragraphs: ElementArrayFinder;
|
||||
let button: ElementFinder;
|
||||
describe('simpleNgModel example', () => {
|
||||
afterEach(verifyNoBrowserErrors);
|
||||
let input: ElementFinder;
|
||||
let paragraphs: ElementArrayFinder;
|
||||
let button: ElementFinder;
|
||||
|
||||
beforeEach(() => {
|
||||
browser.get('/simpleNgModel');
|
||||
input = element(by.css('input'));
|
||||
paragraphs = element.all(by.css('p'));
|
||||
button = element(by.css('button'));
|
||||
});
|
||||
beforeEach(() => {
|
||||
browser.get('/simpleNgModel');
|
||||
input = element(by.css('input'));
|
||||
paragraphs = element.all(by.css('p'));
|
||||
button = element(by.css('button'));
|
||||
});
|
||||
|
||||
it('should update the domain model as you type', () => {
|
||||
input.click();
|
||||
input.sendKeys('Carson');
|
||||
it('should update the domain model as you type', () => {
|
||||
input.click();
|
||||
input.sendKeys('Carson');
|
||||
|
||||
expect(paragraphs.get(0).getText()).toEqual('Value: Carson');
|
||||
});
|
||||
expect(paragraphs.get(0).getText()).toEqual('Value: Carson');
|
||||
});
|
||||
|
||||
it('should report the validity correctly', () => {
|
||||
expect(paragraphs.get(1).getText()).toEqual('Valid: false');
|
||||
input.click();
|
||||
input.sendKeys('a');
|
||||
it('should report the validity correctly', () => {
|
||||
expect(paragraphs.get(1).getText()).toEqual('Valid: false');
|
||||
input.click();
|
||||
input.sendKeys('a');
|
||||
|
||||
expect(paragraphs.get(1).getText()).toEqual('Valid: true');
|
||||
});
|
||||
expect(paragraphs.get(1).getText()).toEqual('Valid: true');
|
||||
});
|
||||
|
||||
it('should set the value by changing the domain model', () => {
|
||||
button.click();
|
||||
expect(input.getAttribute('value')).toEqual('Nancy');
|
||||
});
|
||||
it('should set the value by changing the domain model', () => {
|
||||
button.click();
|
||||
expect(input.getAttribute('value')).toEqual('Nancy');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue