From f535f31d78ec09d47c525edc56c7626c3c4daac0 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 7 Mar 2019 08:31:31 +0000 Subject: [PATCH] fix(ivy): match attribute selectors for content projection with inline-templates (#29041) The content projection mechanism is static, in that it only looks at the static template nodes before directives are matched and change detection is run. When you have a selector-based content projection the selection is based on nodes that are available in the template. For example: ``` ``` would match ```
``` If you have an inline-template in your projected nodes. For example: ```
``` This gets pre-parsed and converted to a canonical form. For example: ```
``` Note that only structural attributes (e.g. `*ngIf`) stay with the `` node. The other attributes move to the contained element inside the template. When this happens in ivy, the ng-template content is removed from the component template function and is compiled into its own template function. But this means that the information about the attributes that were on the content are lost and the projection selection mechanism is unable to match the original `
`. This commit adds support for this in ivy. Attributes are separated into three groups (Bindings, Templates and "other"). For inline-templates the Bindings and "other" types are hoisted back from the contained node to the `template()` instruction, so that they can be used in content projection matching. PR Close #29041 --- .../compliance/r3_compiler_compliance_spec.ts | 37 ++--- .../r3_view_compiler_directives_spec.ts | 4 +- .../compliance/r3_view_compiler_i18n_spec.ts | 38 +++--- .../r3_view_compiler_listener_spec.ts | 2 +- .../r3_view_compiler_template_spec.ts | 31 ++--- packages/compiler/src/render3/r3_ast.ts | 19 +-- .../src/render3/r3_template_transform.ts | 22 ++- .../compiler/src/render3/view/template.ts | 67 ++++++---- packages/compiler/src/render3/view/util.ts | 4 + .../render3/r3_template_transform_spec.ts | 2 + packages/core/src/render3/di.ts | 3 +- packages/core/src/render3/interfaces/node.ts | 4 - .../core/src/render3/node_selector_matcher.ts | 126 ++++++++++++------ packages/core/src/render3/util/attrs_utils.ts | 11 ++ .../test/render3/common_integration_spec.ts | 91 +++++++++---- packages/core/test/render3/component_spec.ts | 10 +- packages/core/test/render3/content_spec.ts | 14 +- packages/core/test/render3/di_spec.ts | 24 ++-- packages/core/test/render3/directive_spec.ts | 2 +- .../core/test/render3/discovery_utils_spec.ts | 4 +- packages/core/test/render3/exports_spec.ts | 4 +- .../core/test/render3/host_binding_spec.ts | 2 +- packages/core/test/render3/i18n_spec.ts | 6 +- .../core/test/render3/instructions_spec.ts | 5 +- .../core/test/render3/integration_spec.ts | 3 +- packages/core/test/render3/lifecycle_spec.ts | 5 +- .../core/test/render3/pure_function_spec.ts | 4 +- packages/core/test/render3/query_spec.ts | 7 +- 28 files changed, 357 insertions(+), 194 deletions(-) diff --git a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts index 5044fa56b9..96bd5fe292 100644 --- a/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_compiler_compliance_spec.ts @@ -788,7 +788,7 @@ describe('compiler compliance', () => { });`; const MyComponentDefinition = ` const $c1$ = ["foo", ""]; - const $c2$ = ["if", ""]; + const $c2$ = [${AttributeMarker.Template}, "if"]; function MyComponent_li_2_Template(rf, ctx) { if (rf & 1) { $r3$.ɵelementStart(0, "li"); @@ -1223,17 +1223,18 @@ describe('compiler compliance', () => { } }; const output = ` - const $_c0$ = [${AttributeMarker.Bindings}, "ngIf"]; - const $_c1$ = ["id", "second"]; + const $_c0$ = ["id", "second", ${AttributeMarker.Template}, "ngIf"]; + const $_c1$ = ["id", "third", ${AttributeMarker.Template}, "ngIf"]; + const $_c2$ = ["id", "second"]; function Cmp_div_0_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵelementStart(0, "div", $_c1$); - $r3$.ɵprojection(1, 1); - $r3$.ɵelementEnd(); + $r3$.ɵelementStart(0, "div", $_c2$); + $r3$.ɵprojection(1, 1); + $r3$.ɵelementEnd(); } } - const $_c4$ = ["id", "third"]; + const $_c3$ = ["id", "third"]; function Cmp_div_1_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵelementStart(0, "div", $_c4$); + $r3$.ɵelementStart(0, "div", $_c3$); $r3$.ɵtext(1, " No ng-content, no instructions generated. "); $r3$.ɵelementEnd(); } @@ -1244,14 +1245,14 @@ describe('compiler compliance', () => { $r3$.ɵprojection(1); } } - const $_c2$ = [[["span", "title", "tofirst"]]]; - const $_c3$ = ["span[title=toFirst]"]; + const $_c4$ = [[["span", "title", "tofirst"]]]; + const $_c5$ = ["span[title=toFirst]"]; … template: function Cmp_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵprojectionDef($_c2$, $_c3$); + $r3$.ɵprojectionDef($_c4$, $_c5$); $r3$.ɵtemplate(0, Cmp_div_0_Template, 2, 0, "div", $_c0$); - $r3$.ɵtemplate(1, Cmp_div_1_Template, 2, 0, "div", $_c0$); + $r3$.ɵtemplate(1, Cmp_div_1_Template, 2, 0, "div", $_c1$); $r3$.ɵtemplate(2, Cmp_ng_template_2_Template, 2, 0, "ng-template"); } if (rf & 2) { @@ -2112,7 +2113,7 @@ describe('compiler compliance', () => { const MyComponentDefinition = ` const $c1$ = ["foo", ""]; - const $c2$ = ["if", ""]; + const $c2$ = [${AttributeMarker.Template}, "if"]; const $c3$ = ["baz", ""]; const $c4$ = ["bar", ""]; function MyComponent_div_3_span_2_Template(rf, ctx) { @@ -2203,9 +2204,9 @@ describe('compiler compliance', () => { }; const template = ` - const $c0$ = ["ngFor", "" , ${AttributeMarker.Bindings}, "ngForOf"]; + const $c0$ = [${AttributeMarker.Template}, "ngFor", "ngForOf"]; const $c1$ = ["foo", ""]; - const $c2$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $c2$ = [${AttributeMarker.Template}, "ngIf"]; function MyComponent_div_0_span_3_Template(rf, ctx) { if (rf & 1) { @@ -2430,7 +2431,7 @@ describe('compiler compliance', () => { `; const MyComponentDefinition = ` - const $t1_attrs$ = ["for", "", ${AttributeMarker.Bindings}, "forOf"]; + const $t1_attrs$ = [${AttributeMarker.Template}, "for", "forOf"]; function MyComponent__svg_g_1_Template(rf, ctx) { if (rf & 1) { $r3$.ɵnamespaceSVG(); @@ -2509,7 +2510,7 @@ describe('compiler compliance', () => { `; const MyComponentDefinition = ` - const $t1_attrs$ = ["for", "", ${AttributeMarker.Bindings}, "forOf"]; + const $t1_attrs$ = [${AttributeMarker.Template}, "for", "forOf"]; function MyComponent_li_1_Template(rf, ctx) { if (rf & 1) { $r3$.ɵelementStart(0, "li"); @@ -2591,7 +2592,7 @@ describe('compiler compliance', () => { }; const MyComponentDefinition = ` - const $t4_attrs$ = ["for", "", ${AttributeMarker.Bindings}, "forOf"]; + const $t4_attrs$ = [${AttributeMarker.Template}, "for", "forOf"]; function MyComponent_li_1_li_4_Template(rf, ctx) { if (rf & 1) { $r3$.ɵelementStart(0, "li"); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts index 567ea99b76..441b237027 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_directives_spec.ts @@ -236,7 +236,7 @@ describe('compiler compliance: directives', () => { const MyComponentDefinition = ` … - const $_c0$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $_c0$ = ["directiveA", "", ${AttributeMarker.Template}, "ngIf"]; const $_c1$ = ["directiveA", ""]; function MyComponent_ng_container_0_Template(rf, ctx) { if (rf & 1) { @@ -339,7 +339,7 @@ describe('compiler compliance: directives', () => { // MyComponent definition should be: const MyComponentDefinition = ` … - const $c0_a0$ = ["someDirective", ""]; + const $c0_a0$ = [${AttributeMarker.Template}, "someDirective"]; … MyComponent.ngComponentDef = $r3$.ɵdefineComponent({ … diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts index 461e6b8f3d..2a13f675b1 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts @@ -417,7 +417,7 @@ describe('i18n support in the view compiler', () => { `; const output = String.raw ` - const $_c0$ = ["ngFor", "", ${AttributeMarker.Bindings}, "ngForOf"]; + const $_c0$ = [${AttributeMarker.Template}, "ngFor", "ngForOf"]; /** * @desc d * @meaning m @@ -548,7 +548,7 @@ describe('i18n support in the view compiler', () => { `; const output = String.raw ` - const $_c0$ = ["ngFor", "", ${AttributeMarker.Bindings}, "ngForOf"]; + const $_c0$ = [${AttributeMarker.Template}, "ngFor", "ngForOf"]; /** * @desc d * @meaning m @@ -1011,7 +1011,7 @@ describe('i18n support in the view compiler', () => { `; const output = String.raw ` - const $_c0$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $_c0$ = [${AttributeMarker.Template}, "ngIf"]; const $MSG_EXTERNAL_7679414751795588050$$APP_SPEC_TS__1$ = goog.getMsg(" Some other content {$interpolation} {$startTagDiv} More nested levels with bindings {$interpolation_1} {$closeTagDiv}", { "interpolation": "\uFFFD0\uFFFD", "startTagDiv": "\uFFFD#3\uFFFD", @@ -1067,7 +1067,8 @@ describe('i18n support in the view compiler', () => { const output = String.raw ` const $_c0$ = ["src", "logo.png"]; - const $_c1$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $_c1$ = ["src", "logo.png", ${AttributeMarker.Template}, "ngIf"]; + const $_c2$ = ["src", "logo.png", ${AttributeMarker.Bindings}, "title", ${AttributeMarker.Template}, "ngIf"]; function MyComponent_img_1_Template(rf, ctx) { if (rf & 1) { $r3$.ɵelement(0, "img", $_c0$); @@ -1076,11 +1077,11 @@ describe('i18n support in the view compiler', () => { const $MSG_EXTERNAL_2367729185105559721$ = goog.getMsg("App logo #{$interpolation}", { "interpolation": "\uFFFD0\uFFFD" }); - const $_c2$ = ["title", $MSG_EXTERNAL_2367729185105559721$]; + const $_c3$ = ["title", $MSG_EXTERNAL_2367729185105559721$]; function MyComponent_img_2_Template(rf, ctx) { if (rf & 1) { $r3$.ɵelementStart(0, "img", $_c0$); - $r3$.ɵi18nAttributes(1, $_c2$); + $r3$.ɵi18nAttributes(1, $_c3$); $r3$.ɵelementEnd(); } if (rf & 2) { @@ -1096,7 +1097,7 @@ describe('i18n support in the view compiler', () => { if (rf & 1) { $r3$.ɵelement(0, "img", $_c0$); $r3$.ɵtemplate(1, MyComponent_img_1_Template, 1, 0, "img", $_c1$); - $r3$.ɵtemplate(2, MyComponent_img_2_Template, 2, 1, "img", $_c1$); + $r3$.ɵtemplate(2, MyComponent_img_2_Template, 2, 1, "img", $_c2$); } if (rf & 2) { $r3$.ɵflushHooksUpTo(1); @@ -1136,7 +1137,7 @@ describe('i18n support in the view compiler', () => { `; const output = String.raw ` - const $_c0$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $_c0$ = [${AttributeMarker.Template}, "ngIf"]; function MyComponent_div_2_div_4_Template(rf, ctx) { if (rf & 1) { $r3$.ɵi18nStart(0, $I18N_EXTERNAL_1221890473527419724$$APP_SPEC_TS_0$, 2); @@ -1232,7 +1233,7 @@ describe('i18n support in the view compiler', () => { `; const output = String.raw ` - const $_c0$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $_c0$ = [${AttributeMarker.Template}, "ngIf"]; const $MSG_EXTERNAL_119975189388320493$$APP_SPEC_TS__1$ = goog.getMsg("Some other content {$startTagSpan}{$interpolation}{$closeTagSpan}", { "startTagSpan": "\uFFFD#2\uFFFD", "interpolation": "\uFFFD0\uFFFD", @@ -1864,15 +1865,16 @@ describe('i18n support in the view compiler', () => { const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$, { "VAR_SELECT": "\uFFFD0\uFFFD" }); - const $_c0$ = [${AttributeMarker.Bindings}, "ngIf"]; - const $_c1$ = ["title", "icu only"]; + const $_c0$ = ["title", "icu only", ${AttributeMarker.Template}, "ngIf"]; + const $_c1$ = ["title", "icu and text", ${AttributeMarker.Template}, "ngIf"]; + const $_c2$ = ["title", "icu only"]; const $MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} other {other}}"); const $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$, { "VAR_SELECT": "\uFFFD0\uFFFD" }); function MyComponent_div_2_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵelementStart(0, "div", $_c1$); + $r3$.ɵelementStart(0, "div", $_c2$); $r3$.ɵi18n(1, $I18N_EXTERNAL_8806993169187953163$$APP_SPEC_TS__3$); $r3$.ɵelementEnd(); } @@ -1883,7 +1885,7 @@ describe('i18n support in the view compiler', () => { $r3$.ɵi18nApply(1); } } - const $_c2$ = ["title", "icu and text"]; + const $_c3$ = ["title", "icu and text"]; const $MSG_EXTERNAL_1922743304863699161$$APP_SPEC_TS__5$ = goog.getMsg("{VAR_SELECT, select, 0 {no emails} 1 {one email} other {{$interpolation} emails}}", { "interpolation": "\uFFFD1\uFFFD" }); @@ -1892,7 +1894,7 @@ describe('i18n support in the view compiler', () => { }); function MyComponent_div_3_Template(rf, ctx) { if (rf & 1) { - $r3$.ɵelementStart(0, "div", $_c2$); + $r3$.ɵelementStart(0, "div", $_c3$); $r3$.ɵtext(1, " You have "); $r3$.ɵi18n(2, $I18N_EXTERNAL_1922743304863699161$$APP_SPEC_TS__5$); $r3$.ɵtext(3, ". "); @@ -1915,7 +1917,7 @@ describe('i18n support in the view compiler', () => { $r3$.ɵi18n(1, $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_0$); $r3$.ɵelementEnd(); $r3$.ɵtemplate(2, MyComponent_div_2_Template, 2, 1, "div", $_c0$); - $r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 2, "div", $_c0$); + $r3$.ɵtemplate(3, MyComponent_div_3_Template, 4, 2, "div", $_c1$); } if (rf & 2) { $r3$.ɵflushHooksUpTo(1); @@ -2113,7 +2115,7 @@ describe('i18n support in the view compiler', () => { const $I18N_APP_SPEC_TS_2$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS_2$, { "VAR_SELECT": "\uFFFD1\uFFFD" }); - const $_c3$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $_c3$ = [${AttributeMarker.Template}, "ngIf"]; const $MSG_APP_SPEC_TS__4$ = goog.getMsg("{VAR_SELECT, select, male {male} female {female} other {other}}"); const $I18N_APP_SPEC_TS__4$ = $r3$.ɵi18nPostprocess($MSG_APP_SPEC_TS__4$, { "VAR_SELECT": "\uFFFD0:1\uFFFD" @@ -2223,7 +2225,7 @@ describe('i18n support in the view compiler', () => { const $I18N_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7842238767399919809$$APP_SPEC_TS_1$, { "VAR_SELECT": "\uFFFD0\uFFFD" }); - const $_c0$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $_c0$ = [${AttributeMarker.Template}, "ngIf"]; const $MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other}}"); const $I18N_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7068143081688428291$$APP_SPEC_TS__3$, { "VAR_SELECT": "\uFFFD0:1\uFFFD" @@ -2287,7 +2289,7 @@ describe('i18n support in the view compiler', () => { const $I18N_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$ = $r3$.ɵi18nPostprocess($MSG_EXTERNAL_7825031864601787094$$APP_SPEC_TS_1$, { "VAR_SELECT": "\uFFFD0\uFFFD" }); - const $_c0$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $_c0$ = [${AttributeMarker.Template}, "ngIf"]; const $MSG_EXTERNAL_2310343208266678305$$APP_SPEC_TS__3$ = goog.getMsg("{VAR_SELECT, select, 10 {ten} 20 {twenty} 30 {thirty} other {other: {$interpolation}}}", { "interpolation": "\uFFFD1:1\uFFFD" }); diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts index 6fb133db67..041339cb91 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_listener_spec.ts @@ -133,7 +133,7 @@ describe('compiler compliance: listen()', () => { }; const template = ` - const $t0_attrs$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $t0_attrs$ = [${AttributeMarker.Template}, "ngIf"]; const $e_attrs$ = [${AttributeMarker.Bindings}, "click"]; function MyComponent_div_0_Template(rf, ctx) { diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts index 294b71f86b..33ca856a7b 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_template_spec.ts @@ -50,13 +50,14 @@ describe('compiler compliance: template', () => { // The template should look like this (where IDENT is a wild card for an identifier): const template = ` - const $c0$ = ["ngFor", "", ${AttributeMarker.Bindings}, "ngForOf"]; - const $e0_attrs$ = [${AttributeMarker.Bindings}, "title", "click"]; + const $c0$ = [${AttributeMarker.Template}, "ngFor", "ngForOf"]; + const $c1$ = [${AttributeMarker.Bindings}, "title", "click", ${AttributeMarker.Template}, "ngFor", "ngForOf"]; + const $c2$ = [${AttributeMarker.Bindings}, "title", "click"]; function MyComponent_ul_0_li_1_div_1_Template(rf, ctx) { if (rf & 1) { const $s$ = $i0$.ɵgetCurrentView(); - $i0$.ɵelementStart(0, "div", $e0_attrs$); + $i0$.ɵelementStart(0, "div", $c2$); $i0$.ɵlistener("click", function MyComponent_ul_0_li_1_div_1_Template_div_click_0_listener($event){ $i0$.ɵrestoreView($s$); const $inner$ = ctx.$implicit; @@ -83,7 +84,7 @@ describe('compiler compliance: template', () => { function MyComponent_ul_0_li_1_Template(rf, ctx) { if (rf & 1) { $i0$.ɵelementStart(0, "li"); - $i0$.ɵtemplate(1, MyComponent_ul_0_li_1_div_1_Template, 2, 2, "div", _c0); + $i0$.ɵtemplate(1, MyComponent_ul_0_li_1_div_1_Template, 2, 2, "div", $c1$); $i0$.ɵelementEnd(); } if (rf & 2) { @@ -96,7 +97,7 @@ describe('compiler compliance: template', () => { function MyComponent_ul_0_Template(rf, ctx) { if (rf & 1) { $i0$.ɵelementStart(0, "ul"); - $i0$.ɵtemplate(1, MyComponent_ul_0_li_1_Template, 2, 1, "li", _c0); + $i0$.ɵtemplate(1, MyComponent_ul_0_li_1_Template, 2, 1, "li", $c0$); $i0$.ɵelementEnd(); } if (rf & 2) { @@ -108,7 +109,7 @@ describe('compiler compliance: template', () => { // ... template:function MyComponent_Template(rf, ctx){ if (rf & 1) { - $i0$.ɵtemplate(0, MyComponent_ul_0_Template, 2, 1, "ul", _c0); + $i0$.ɵtemplate(0, MyComponent_ul_0_Template, 2, 1, "ul", $c0$); } if (rf & 2) { $i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items)); @@ -144,7 +145,7 @@ describe('compiler compliance: template', () => { }; const template = ` - const $t0_attrs$ = ["ngFor", "", ${AttributeMarker.Bindings}, "ngForOf"]; + const $t0_attrs$ = [${AttributeMarker.Bindings}, "click", ${AttributeMarker.Template}, "ngFor", "ngForOf"]; const $e_attrs$ = [${AttributeMarker.Bindings}, "click"]; function MyComponent_div_0_Template(rf, ctx) { @@ -199,7 +200,7 @@ describe('compiler compliance: template', () => { }; const template = ` - const $c0$ = ["ngFor", "", ${AttributeMarker.Bindings}, "ngForOf"]; + const $c0$ = [${AttributeMarker.Template}, "ngFor", "ngForOf"]; function MyComponent_span_0_Template(rf, ctx) { if (rf & 1) { @@ -253,8 +254,8 @@ describe('compiler compliance: template', () => { }; const template = ` - const $c0$ = ["ngFor", "", ${AttributeMarker.Bindings}, "ngForOf"]; - const $c1$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $c0$ = [${AttributeMarker.Template}, "ngFor", "ngForOf"]; + const $c1$ = [${AttributeMarker.Template}, "ngIf"]; function MyComponent_div_0_span_1_Template(rf, ctx) { if (rf & 1) { @@ -326,7 +327,7 @@ describe('compiler compliance: template', () => { // The template should look like this (where IDENT is a wild card for an identifier): const template = ` - const $c0$ = ["ngFor", "", ${AttributeMarker.Bindings}, "ngForOf"]; + const $c0$ = [${AttributeMarker.Template}, "ngFor", "ngForOf"]; function MyComponent_div_0_div_1_div_1_Template(rf, ctx) { if (rf & 1) { $i0$.ɵelementStart(0, "div"); @@ -344,7 +345,7 @@ describe('compiler compliance: template', () => { function MyComponent_div_0_div_1_Template(rf, ctx) { if (rf & 1) { $i0$.ɵelementStart(0, "div"); - $i0$.ɵtemplate(1, MyComponent_div_0_div_1_div_1_Template, 2, 2, "div", _c0); + $i0$.ɵtemplate(1, MyComponent_div_0_div_1_div_1_Template, 2, 2, "div", $c0$); $i0$.ɵelementEnd(); } if (rf & 2) { @@ -357,7 +358,7 @@ describe('compiler compliance: template', () => { function MyComponent_div_0_Template(rf, ctx) { if (rf & 1) { $i0$.ɵelementStart(0, "div"); - $i0$.ɵtemplate(1, MyComponent_div_0_div_1_Template, 2, 1, "div", _c0); + $i0$.ɵtemplate(1, MyComponent_div_0_div_1_Template, 2, 1, "div", $c0$); $i0$.ɵelementEnd(); } if (rf & 2) { @@ -369,7 +370,7 @@ describe('compiler compliance: template', () => { // ... template:function MyComponent_Template(rf, ctx){ if (rf & 1) { - $i0$.ɵtemplate(0, MyComponent_div_0_Template, 2, 1, "div", _c0); + $i0$.ɵtemplate(0, MyComponent_div_0_Template, 2, 1, "div", $c0$); } if (rf & 2) { $i0$.ɵelementProperty(0, "ngForOf", $i0$.ɵbind(ctx.items)); @@ -645,7 +646,7 @@ describe('compiler compliance: template', () => { }; const template = ` - const $c0$ = [${AttributeMarker.Bindings}, "ngIf"]; + const $c0$ = [${AttributeMarker.Template}, "ngIf"]; function MyComponent_div_0_Template(rf, ctx) { if (rf & 1) { diff --git a/packages/compiler/src/render3/r3_ast.ts b/packages/compiler/src/render3/r3_ast.ts index 79fd5e4477..e51bd6ff89 100644 --- a/packages/compiler/src/render3/r3_ast.ts +++ b/packages/compiler/src/render3/r3_ast.ts @@ -81,10 +81,10 @@ export class Element implements Node { export class Template implements Node { constructor( public tagName: string, public attributes: TextAttribute[], public inputs: BoundAttribute[], - public outputs: BoundEvent[], public children: Node[], public references: Reference[], - public variables: Variable[], public sourceSpan: ParseSourceSpan, - public startSourceSpan: ParseSourceSpan|null, public endSourceSpan: ParseSourceSpan|null, - public i18n?: I18nAST) {} + public outputs: BoundEvent[], public templateAttrs: (BoundAttribute|TextAttribute)[], + public children: Node[], public references: Reference[], public variables: Variable[], + public sourceSpan: ParseSourceSpan, public startSourceSpan: ParseSourceSpan|null, + public endSourceSpan: ParseSourceSpan|null, public i18n?: I18nAST) {} visit(visitor: Visitor): Result { return visitor.visitTemplate(this); } } @@ -189,15 +189,18 @@ export class TransformVisitor implements Visitor { const newAttributes = transformAll(this, template.attributes); const newInputs = transformAll(this, template.inputs); const newOutputs = transformAll(this, template.outputs); + const newTemplateAttrs = transformAll(this, template.templateAttrs); const newChildren = transformAll(this, template.children); const newReferences = transformAll(this, template.references); const newVariables = transformAll(this, template.variables); if (newAttributes != template.attributes || newInputs != template.inputs || - newOutputs != template.outputs || newChildren != template.children || - newReferences != template.references || newVariables != template.variables) { + newOutputs != template.outputs || newTemplateAttrs != template.templateAttrs || + newChildren != template.children || newReferences != template.references || + newVariables != template.variables) { return new Template( - template.tagName, newAttributes, newInputs, newOutputs, newChildren, newReferences, - newVariables, template.sourceSpan, template.startSourceSpan, template.endSourceSpan); + template.tagName, newAttributes, newInputs, newOutputs, newTemplateAttrs, newChildren, + newReferences, newVariables, template.sourceSpan, template.startSourceSpan, + template.endSourceSpan); } return template; } diff --git a/packages/compiler/src/render3/r3_template_transform.ts b/packages/compiler/src/render3/r3_template_transform.ts index 371dda13b4..83521634e5 100644 --- a/packages/compiler/src/render3/r3_template_transform.ts +++ b/packages/compiler/src/render3/r3_template_transform.ts @@ -177,8 +177,9 @@ class HtmlAstToIvyAst implements html.Visitor { const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta); parsedElement = new t.Template( - element.name, attributes, attrs.bound, boundEvents, children, references, variables, - element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n); + element.name, attributes, attrs.bound, boundEvents, [/* no template attributes */], + children, references, variables, element.sourceSpan, element.startSourceSpan, + element.endSourceSpan, element.i18n); } else { const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta); parsedElement = new t.Element( @@ -187,10 +188,25 @@ class HtmlAstToIvyAst implements html.Visitor { } if (elementHasInlineTemplate) { + // If this node is an inline-template (e.g. has *ngFor) then we need to create a template + // node that contains this node. + // Moreover, if the node is an element, then we need to hoist its attributes to the template + // node for matching against content projection selectors. const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta); + const templateAttrs: (t.TextAttribute | t.BoundAttribute)[] = []; + attrs.literal.forEach(attr => templateAttrs.push(attr)); + attrs.bound.forEach(attr => templateAttrs.push(attr)); + const hoistedAttrs = parsedElement instanceof t.Element ? + { + attributes: parsedElement.attributes, + inputs: parsedElement.inputs, + outputs: parsedElement.outputs, + } : + {attributes: [], inputs: [], outputs: []}; // TODO(pk): test for this case parsedElement = new t.Template( - (parsedElement as t.Element).name, attrs.literal, attrs.bound, [], [parsedElement], [], + (parsedElement as t.Element).name, hoistedAttrs.attributes, hoistedAttrs.inputs, + hoistedAttrs.outputs, templateAttrs, [parsedElement], [/* no references */], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n); } diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index aa9dfbb989..5038bf8b44 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -576,9 +576,8 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver attributes.push(...getAttributeNameLiterals(attr.name), o.literal(attr.value)); }); - // this will build the instructions so that they fall into the following syntax - // add attributes for directive matching purposes - attributes.push(...this.prepareBindingsAttrs(allOtherInputs, element.outputs, stylingBuilder)); + // add attributes for directive and projection matching purposes + attributes.push(...this.prepareNonRenderAttrs(allOtherInputs, element.outputs, stylingBuilder)); parameters.push(this.toAttrsParam(attributes)); // local refs (ex.:
) @@ -774,6 +773,7 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver } visitTemplate(template: t.Template) { + const NG_TEMPLATE_TAG_NAME = 'ng-template'; const templateIndex = this.allocateDataSlot(); if (this.i18n) { @@ -794,13 +794,14 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver ]; // find directives matching on a given node - this.matchDirectives('ng-template', template); + this.matchDirectives(NG_TEMPLATE_TAG_NAME, template); // prepare attributes parameter (including attributes used for directive matching) const attrsExprs: o.Expression[] = []; template.attributes.forEach( (a: t.TextAttribute) => { attrsExprs.push(asLiteral(a.name), asLiteral(a.value)); }); - attrsExprs.push(...this.prepareBindingsAttrs(template.inputs, template.outputs)); + attrsExprs.push(...this.prepareNonRenderAttrs( + template.inputs, template.outputs, undefined, template.templateAttrs)); parameters.push(this.toAttrsParam(attrsExprs)); // local refs (ex.: ) @@ -840,23 +841,19 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver // handle property bindings e.g. ɵelementProperty(1, 'ngForOf', ɵbind(ctx.items)); const context = o.variable(CONTEXT_NAME); - template.inputs.forEach(input => { - 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.templatePropertyBindings(template, templateIndex, context, template.templateAttrs); - // Generate listeners for directive output - template.outputs.forEach((outputAst: t.BoundEvent) => { - this.creationInstruction( - outputAst.sourceSpan, R3.listener, - this.prepareListenerParameter('ng_template', outputAst, templateIndex)); - }); + // Only add normal input/output binding instructions on explicit ng-template elements. + if (template.tagName === NG_TEMPLATE_TAG_NAME) { + // Add the input bindings + this.templatePropertyBindings(template, templateIndex, context, template.inputs); + // Generate listeners for directive output + template.outputs.forEach((outputAst: t.BoundEvent) => { + this.creationInstruction( + outputAst.sourceSpan, R3.listener, + this.prepareListenerParameter('ng_template', outputAst, templateIndex)); + }); + } } // These should be handled in the template or element directly. @@ -949,6 +946,23 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver private bindingContext() { return `${this._bindingContext++}`; } + private templatePropertyBindings( + template: t.Template, templateIndex: number, context: o.ReadVarExpr, + attrs: (t.BoundAttribute|t.TextAttribute)[]) { + attrs.forEach(input => { + 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) + ]; + }); + } + }); + } + // Bindings must only be resolved after all local refs have been visited, so all // instructions are queued in callbacks that execute once the initial pass has completed. // Otherwise, we wouldn't be able to support local refs that are defined after their @@ -1051,9 +1065,9 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver * Note that this function will fully ignore all synthetic (@foo) attribute values * because those values are intended to always be generated as property instructions. */ - private prepareBindingsAttrs( - inputs: t.BoundAttribute[], outputs: t.BoundEvent[], - styles?: StylingBuilder): o.Expression[] { + private prepareNonRenderAttrs( + inputs: t.BoundAttribute[], outputs: t.BoundEvent[], styles?: StylingBuilder, + templateAttrs: (t.BoundAttribute|t.TextAttribute)[] = []): o.Expression[] { const alreadySeen = new Set(); const attrExprs: o.Expression[] = []; @@ -1102,6 +1116,11 @@ export class TemplateDefinitionBuilder implements t.Visitor, LocalResolver } } + if (templateAttrs.length) { + attrExprs.push(o.literal(core.AttributeMarker.Template)); + templateAttrs.forEach(attr => addAttrExpr(attr.name)); + } + return attrExprs; } diff --git a/packages/compiler/src/render3/view/util.ts b/packages/compiler/src/render3/view/util.ts index 3e51ebc1bc..876284e988 100644 --- a/packages/compiler/src/render3/view/util.ts +++ b/packages/compiler/src/render3/view/util.ts @@ -173,5 +173,9 @@ export function getAttrsForDirectiveMatching(elOrTpl: t.Element | t.Template): elOrTpl.inputs.forEach(i => { attributesMap[i.name] = ''; }); elOrTpl.outputs.forEach(o => { attributesMap[o.name] = ''; }); + if (elOrTpl instanceof t.Template) { + elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = ''); + } + return attributesMap; } diff --git a/packages/compiler/test/render3/r3_template_transform_spec.ts b/packages/compiler/test/render3/r3_template_transform_spec.ts index d41d598517..4b800f6153 100644 --- a/packages/compiler/test/render3/r3_template_transform_spec.ts +++ b/packages/compiler/test/render3/r3_template_transform_spec.ts @@ -32,6 +32,8 @@ class R3AstHumanizer implements t.Visitor { this.visitAll([ template.attributes, template.inputs, + template.outputs, + template.templateAttrs, template.references, template.variables, template.children, diff --git a/packages/core/src/render3/di.ts b/packages/core/src/render3/di.ts index b75f578d9a..639ebe8b20 100644 --- a/packages/core/src/render3/di.ts +++ b/packages/core/src/render3/di.ts @@ -17,10 +17,11 @@ import {getComponentDef, getDirectiveDef, getPipeDef} from './definition'; import {NG_ELEMENT_ID} from './fields'; import {DirectiveDef} from './interfaces/definition'; import {NO_PARENT_INJECTOR, NodeInjectorFactory, PARENT_INJECTOR, RelativeInjectorLocation, RelativeInjectorLocationFlags, TNODE, isFactory} from './interfaces/injector'; -import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, isNameOnlyAttributeMarker} from './interfaces/node'; +import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType} from './interfaces/node'; import {DECLARATION_VIEW, INJECTOR, LView, TData, TVIEW, TView, T_HOST} from './interfaces/view'; import {assertNodeOfPossibleTypes} from './node_assert'; import {getLView, getPreviousOrParentTNode, setTNodeAndViewData} from './state'; +import {isNameOnlyAttributeMarker} from './util/attrs_utils'; import {getParentInjectorIndex, getParentInjectorView, hasParentInjector} from './util/injector_utils'; import {renderStringify} from './util/misc_utils'; import {findComponentView} from './util/view_traversal_utils'; diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts index 79bf033e9e..7eadf2c58c 100644 --- a/packages/core/src/render3/interfaces/node.ts +++ b/packages/core/src/render3/interfaces/node.ts @@ -149,10 +149,6 @@ export const enum AttributeMarker { Template = 4, } -export function isNameOnlyAttributeMarker(marker: string | AttributeMarker) { - return marker === AttributeMarker.Bindings || marker === AttributeMarker.Template; -} - /** * A combination of: * - attribute names and values diff --git a/packages/core/src/render3/node_selector_matcher.ts b/packages/core/src/render3/node_selector_matcher.ts index b5c63a4980..d4af2755cf 100644 --- a/packages/core/src/render3/node_selector_matcher.ts +++ b/packages/core/src/render3/node_selector_matcher.ts @@ -10,9 +10,10 @@ import '../util/ng_dev_mode'; import {assertDefined, assertNotEqual} from '../util/assert'; -import {AttributeMarker, TAttributes, TNode, TNodeType, isNameOnlyAttributeMarker, unusedValueExportToPlacateAjd as unused1} from './interfaces/node'; +import {AttributeMarker, TAttributes, TNode, TNodeType, unusedValueExportToPlacateAjd as unused1} from './interfaces/node'; import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags, unusedValueExportToPlacateAjd as unused2} from './interfaces/projection'; import {getInitialClassNameValue} from './styling/class_and_style_bindings'; +import {isNameOnlyAttributeMarker} from './util/attrs_utils'; const unusedValueToPlacateAjd = unused1 + unused2; @@ -35,7 +36,7 @@ function isCssClassMatching(nodeClassAttrVal: string, cssClassToMatch: string): /** * Function that checks whether a given tNode matches tag-based selector and has a valid type. * - * Matching can be perfomed in 2 modes: projection mode (when we project nodes) and regular + * Matching can be performed in 2 modes: projection mode (when we project nodes) and regular * directive matching mode. In "projection" mode, we do not need to check types, so if tag name * matches selector, we declare a match. In "directive matching" mode, we also check whether tNode * is of expected type: @@ -53,8 +54,10 @@ function hasTagAndTypeMatch( /** * A utility function to match an Ivy node static data against a simple CSS selector * - * @param node static data to match - * @param selector + * @param node static data of the node to match + * @param selector The selector to try matching against the node. + * @param isProjectionMode if `true` we are matching for content projection, otherwise we are doing + * directive matching. * @returns true if node matches the selector. */ export function isNodeMatchingSelector( @@ -64,14 +67,7 @@ export function isNodeMatchingSelector( const nodeAttrs = tNode.attrs || []; // Find the index of first attribute that has no value, only a name. - let nameOnlyMarkerIdx = nodeAttrs && nodeAttrs.length; - for (let i = 0; i < nodeAttrs.length; i++) { - const nodeAttr = nodeAttrs[i]; - if (isNameOnlyAttributeMarker(nodeAttr)) { - nameOnlyMarkerIdx = i; - break; - } - } + const nameOnlyMarkerIdx = getNameOnlyMarkerIndex(nodeAttrs); // When processing ":not" selectors, we skip to the next ":not" if the // current one doesn't match @@ -114,8 +110,11 @@ export function isNodeMatchingSelector( continue; } + const isInlineTemplate = + tNode.type == TNodeType.Container && tNode.tagName !== NG_TEMPLATE_SELECTOR; const attrName = (mode & SelectorFlags.CLASS) ? 'class' : current; - const attrIndexInNode = findAttrIndexInNode(attrName, nodeAttrs); + const attrIndexInNode = + findAttrIndexInNode(attrName, nodeAttrs, isInlineTemplate, isProjectionMode); if (attrIndexInNode === -1) { if (isPositive(mode)) return false; @@ -125,12 +124,11 @@ export function isNodeMatchingSelector( if (selectorAttrValue !== '') { let nodeAttrValue: string; - const maybeAttrName = nodeAttrs[attrIndexInNode]; if (attrIndexInNode > nameOnlyMarkerIdx) { nodeAttrValue = ''; } else { ngDevMode && assertNotEqual( - maybeAttrName, AttributeMarker.NamespaceURI, + nodeAttrs[attrIndexInNode], AttributeMarker.NamespaceURI, 'We do not match directives on namespaced attributes'); nodeAttrValue = nodeAttrs[attrIndexInNode + 1] as string; } @@ -164,34 +162,64 @@ function readClassValueFromTNode(tNode: TNode): string { } /** - * Examines an attribute's definition array from a node to find the index of the - * attribute with the specified name. + * Examines the attribute's definition array for a node to find the index of the + * attribute that matches the given `name`. * - * NOTE: Will not find namespaced attributes. + * NOTE: This will not match namespaced attributes. + * + * Attribute matching depends upon `isInlineTemplate` and `isProjectionMode`. + * The following table summarizes which types of attributes we attempt to match: + * + * ========================================================================================= + * Modes | Normal Attributes | Bindings Attributes | Template Attributes + * ========================================================================================= + * Inline + Projection | YES | YES | NO + * ----------------------------------------------------------------------------------------- + * Inline + Directive | NO | NO | YES + * ----------------------------------------------------------------------------------------- + * Non-inline + Projection | YES | YES | NO + * ----------------------------------------------------------------------------------------- + * Non-inline + Directive | YES | YES | NO + * ========================================================================================= * * @param name the name of the attribute to find * @param attrs the attribute array to examine + * @param isInlineTemplate true if the node being matched is an inline template (e.g. `*ngFor`) + * rather than a manually expanded template node (e.g ``). + * @param isProjectionMode true if we are matching against content projection otherwise we are + * matching against directives. */ -function findAttrIndexInNode(name: string, attrs: TAttributes | null): number { +function findAttrIndexInNode( + name: string, attrs: TAttributes | null, isInlineTemplate: boolean, + isProjectionMode: boolean): number { if (attrs === null) return -1; - let nameOnlyMode = false; - let i = 0; - while (i < attrs.length) { - const maybeAttrName = attrs[i]; - if (maybeAttrName === name) { - return i; - } else if (maybeAttrName === AttributeMarker.NamespaceURI) { - // NOTE(benlesh): will not find namespaced attributes. This is by design. - i += 4; - } else { - if (isNameOnlyAttributeMarker(maybeAttrName)) { - nameOnlyMode = true; - } - i += nameOnlyMode ? 1 : 2; - } - } - return -1; + let i = 0; + + if (isProjectionMode || !isInlineTemplate) { + let bindingsMode = false; + while (i < attrs.length) { + const maybeAttrName = attrs[i]; + if (maybeAttrName === name) { + return i; + } else if (maybeAttrName === AttributeMarker.Bindings) { + bindingsMode = true; + } else if (maybeAttrName === AttributeMarker.Template) { + // We do not care about Template attributes in this scenario. + break; + } else if (maybeAttrName === AttributeMarker.NamespaceURI) { + // Skip the whole namespaced attribute and value. This is by design. + i += 4; + continue; + } + // In binding mode there are only names, rather than name-value pairs. + i += bindingsMode ? 1 : 2; + } + // We did not match the attribute + return -1; + } else { + return matchTemplateAttribute(attrs, name); + } } export function isNodeMatchingSelectorList( @@ -222,8 +250,8 @@ export function getProjectAsAttrValue(tNode: TNode): string|null { * Checks a given node against matching projection selectors and returns * selector index (or 0 if none matched). * - * This function takes into account the ngProjectAs attribute: if present its value will be compared - * to the raw (un-parsed) CSS selector instead of using standard selector matching logic. + * This function takes into account the ngProjectAs attribute: if present its value will be + * compared to the raw (un-parsed) CSS selector instead of using standard selector matching logic. */ export function matchingProjectionSelectorIndex( tNode: TNode, selectors: CssSelectorList[], textSelectors: string[]): number { @@ -239,3 +267,25 @@ export function matchingProjectionSelectorIndex( } return 0; } + +function getNameOnlyMarkerIndex(nodeAttrs: TAttributes) { + for (let i = 0; i < nodeAttrs.length; i++) { + const nodeAttr = nodeAttrs[i]; + if (isNameOnlyAttributeMarker(nodeAttr)) { + return i; + } + } + return nodeAttrs.length; +} + +function matchTemplateAttribute(attrs: TAttributes, name: string): number { + let i = attrs.indexOf(AttributeMarker.Template); + if (i > -1) { + i++; + while (i < attrs.length) { + if (attrs[i] === name) return i; + i++; + } + } + return -1; +} diff --git a/packages/core/src/render3/util/attrs_utils.ts b/packages/core/src/render3/util/attrs_utils.ts index f810cd6b0d..ec580a25f5 100644 --- a/packages/core/src/render3/util/attrs_utils.ts +++ b/packages/core/src/render3/util/attrs_utils.ts @@ -105,3 +105,14 @@ export function attrsStylingIndexOf(attrs: TAttributes, startIndex: number): num } return -1; } + +/** + * Test whether the given value is a marker that indicates that the following + * attribute values in a `TAttributes` array are only the names of attributes, + * and not name-value pairs. + * @param marker The attribute marker to test. + * @returns true if the marker is a "name-only" marker (e.g. `Bindings` or `Template`). + */ +export function isNameOnlyAttributeMarker(marker: string | AttributeMarker) { + return marker === AttributeMarker.Bindings || marker === AttributeMarker.Template; +} diff --git a/packages/core/test/render3/common_integration_spec.ts b/packages/core/test/render3/common_integration_spec.ts index d50d4b8acd..de1fd83e05 100644 --- a/packages/core/test/render3/common_integration_spec.ts +++ b/packages/core/test/render3/common_integration_spec.ts @@ -48,7 +48,9 @@ describe('@angular/common integration', () => { template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { elementStart(0, 'ul'); - { template(1, liTemplate, 2, 1, 'li', ['ngForOf', '']); } + { + template(1, liTemplate, 2, 1, 'li', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -111,7 +113,9 @@ describe('@angular/common integration', () => { template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { elementStart(0, 'ul'); - { template(1, liTemplate, 2, 3, 'li', ['ngForOf', '']); } + { + template(1, liTemplate, 2, 3, 'li', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -175,7 +179,8 @@ describe('@angular/common integration', () => { vars: 1, template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { - template(0, ngForTemplate, 1, 0, 'comp', ['ngForOf', '']); + template( + 0, ngForTemplate, 1, 0, 'comp', [AttributeMarker.Template, 'ngFor', 'ngForOf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngForOf', bind(ctx.rows)); @@ -247,7 +252,9 @@ describe('@angular/common integration', () => { } elementEnd(); elementStart(2, 'ul'); - { template(3, liTemplate, 2, 1, 'li', ['ngForOf', '']); } + { + template(3, liTemplate, 2, 1, 'li', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -302,7 +309,9 @@ describe('@angular/common integration', () => { template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { elementStart(0, 'ul'); - { template(1, liTemplate, 2, 1, 'li', ['ngForOf', '']); } + { + template(1, liTemplate, 2, 1, 'li', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -317,7 +326,9 @@ describe('@angular/common integration', () => { function liTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'li'); - { template(1, spanTemplate, 2, 3, 'span', ['ngForOf', '']); } + { + template(1, spanTemplate, 2, 3, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -392,7 +403,7 @@ describe('@angular/common integration', () => { vars: 1, template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { - template(0, divTemplate, 2, 1, 'div', ['ngForOf', '']); + template(0, divTemplate, 2, 1, 'div', [AttributeMarker.Template, 'ngFor', 'ngForOf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngForOf', bind(ctx.items)); @@ -406,7 +417,7 @@ describe('@angular/common integration', () => { function divTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div'); - { template(1, pTemplate, 3, 2, 'p', ['ngForOf', '']); } + { template(1, pTemplate, 3, 2, 'p', [AttributeMarker.Template, 'ngFor', 'ngForOf']); } elementEnd(); } if (rf & RenderFlags.Update) { @@ -486,7 +497,7 @@ describe('@angular/common integration', () => { vars: 1, template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { - template(0, divTemplate, 2, 1, 'div', ['ngForOf', '']); + template(0, divTemplate, 2, 1, 'div', [AttributeMarker.Template, 'ngFor', 'ngForOf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngForOf', bind(ctx.items)); @@ -500,7 +511,10 @@ describe('@angular/common integration', () => { function divTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div'); - { template(1, innerDivTemplate, 2, 1, 'div', ['ngForOf', '']); } + { + template( + 1, innerDivTemplate, 2, 1, 'div', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -512,7 +526,9 @@ describe('@angular/common integration', () => { function innerDivTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div'); - { template(1, spanTemplate, 2, 2, 'span', ['ngForOf', '']); } + { + template(1, spanTemplate, 2, 2, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -654,7 +670,8 @@ describe('@angular/common integration', () => { vars: 1, template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { - template(0, itemTemplate0, 2, 1, 'span', ['ngForOf', '']); + template( + 0, itemTemplate0, 2, 1, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngForOf', bind(ctx.items)); @@ -668,7 +685,10 @@ describe('@angular/common integration', () => { function itemTemplate0(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate1, 2, 1, 'span', ['ngForOf', '']); } + { + template( + 1, itemTemplate1, 2, 1, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -680,7 +700,10 @@ describe('@angular/common integration', () => { function itemTemplate1(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate2, 2, 1, 'span', ['ngForOf', '']); } + { + template( + 1, itemTemplate2, 2, 1, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -692,7 +715,10 @@ describe('@angular/common integration', () => { function itemTemplate2(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate3, 2, 1, 'span', ['ngForOf', '']); } + { + template( + 1, itemTemplate3, 2, 1, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -704,7 +730,10 @@ describe('@angular/common integration', () => { function itemTemplate3(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate4, 2, 1, 'span', ['ngForOf', '']); } + { + template( + 1, itemTemplate4, 2, 1, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -716,7 +745,10 @@ describe('@angular/common integration', () => { function itemTemplate4(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate5, 2, 1, 'span', ['ngForOf', '']); } + { + template( + 1, itemTemplate5, 2, 1, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -728,7 +760,10 @@ describe('@angular/common integration', () => { function itemTemplate5(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate6, 2, 1, 'span', ['ngForOf', '']); } + { + template( + 1, itemTemplate6, 2, 1, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -740,7 +775,10 @@ describe('@angular/common integration', () => { function itemTemplate6(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate7, 2, 1, 'span', ['ngForOf', '']); } + { + template( + 1, itemTemplate7, 2, 1, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -752,7 +790,10 @@ describe('@angular/common integration', () => { function itemTemplate7(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'span'); - { template(1, itemTemplate8, 2, 10, 'span', ['ngForOf', '']); } + { + template( + 1, itemTemplate8, 2, 10, 'span', [AttributeMarker.Template, 'ngFor', 'ngForOf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -822,8 +863,8 @@ describe('@angular/common integration', () => { */ template: (rf: RenderFlags, ctx: MyApp) => { if (rf & RenderFlags.Create) { - template(0, templateOne, 2, 1, 'div', ['ngIf', '']); - template(1, templateTwo, 2, 1, 'div', ['ngIf', '']); + template(0, templateOne, 2, 1, 'div', [AttributeMarker.Template, 'ngIf']); + template(1, templateTwo, 2, 1, 'div', [AttributeMarker.Template, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngIf', bind(ctx.showing)); @@ -892,7 +933,7 @@ describe('@angular/common integration', () => { vars: 1, template: (rf: RenderFlags, ctx: AppComponent) => { if (rf & RenderFlags.Create) { - template(0, divTemplate, 2, 1, 'div', ['ngIf', '']); + template(0, divTemplate, 2, 1, 'div', [AttributeMarker.Template, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngIf', bind(ctx.showing)); @@ -906,7 +947,7 @@ describe('@angular/common integration', () => { function divTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div'); - { template(1, outerDivTemplate, 2, 1, 'div', ['ngIf', '']); } + { template(1, outerDivTemplate, 2, 1, 'div', [AttributeMarker.Template, 'ngIf']); } elementEnd(); } if (rf & RenderFlags.Update) { @@ -918,7 +959,7 @@ describe('@angular/common integration', () => { function outerDivTemplate(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div'); - { template(1, innerDivTemplate, 2, 1, 'div', ['ngIf', '']); } + { template(1, innerDivTemplate, 2, 1, 'div', [AttributeMarker.Template, 'ngIf']); } elementEnd(); } if (rf & RenderFlags.Update) { diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index 6a4064d6fc..ec3e944626 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -186,7 +186,7 @@ it('should not invoke renderer destroy method for embedded views', () => { elementStart(0, 'div'); text(1, 'Root view'); elementEnd(); - template(2, MyComponent_div_Template_2, 2, 0, null, [AttributeMarker.Bindings, 'ngIf']); + template(2, MyComponent_div_Template_2, 2, 0, 'div', [AttributeMarker.Template, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(2, 'ngIf', bind(ctx.visible)); @@ -514,8 +514,12 @@ describe('recursive components', () => { if (rf & RenderFlags.Create) { text(0); - template(1, IfTemplate, 1, 1, 'ng-if-tree', [AttributeMarker.Bindings, 'ngIf']); - template(2, IfTemplate2, 1, 1, 'ng-if-tree', [AttributeMarker.Bindings, 'ngIf']); + template( + 1, IfTemplate, 1, 1, 'ng-if-tree', + [AttributeMarker.Bindings, 'data', AttributeMarker.Template, 'ngIf']); + template( + 2, IfTemplate2, 1, 1, 'ng-if-tree', + [AttributeMarker.Bindings, 'data', AttributeMarker.Template, 'ngIf']); } if (rf & RenderFlags.Update) { textBinding(0, bind(ctx.data.value)); diff --git a/packages/core/test/render3/content_spec.ts b/packages/core/test/render3/content_spec.ts index c2cfe38ae2..caf51bbc71 100644 --- a/packages/core/test/render3/content_spec.ts +++ b/packages/core/test/render3/content_spec.ts @@ -945,7 +945,7 @@ describe('content projection', () => { projectionDef([[['div']]], ['div']); projection(0); text(1, 'Before-'); - template(2, IfTemplate, 1, 0, '', [AttributeMarker.Bindings, 'ngIf']); + template(2, IfTemplate, 1, 0, 'ng-template', [AttributeMarker.Bindings, 'ngIf']); text(3, '-After'); } if (rf & RenderFlags.Update) { @@ -1184,7 +1184,7 @@ describe('content projection', () => { const Child = createComponent('child', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { projectionDef(); - { template(0, ForTemplate, 3, 1, undefined, ['ngForOf', '']); } + { template(0, ForTemplate, 3, 1, 'div', [AttributeMarker.Template, 'ngFor', 'ngForOf']); } } if (rf & RenderFlags.Update) { elementProperty(0, 'ngForOf', bind(items)); @@ -1461,7 +1461,7 @@ describe('content projection', () => { // // - // + // // // function MyApp_ng_container_1_child_comp_1_Template(rf: RenderFlags, ctx: any) { @@ -1472,7 +1472,9 @@ describe('content projection', () => { function MyApp_ng_container_1_Template(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementContainerStart(0); - template(1, MyApp_ng_container_1_child_comp_1_Template, 1, 0, 'nested-comp', [3, 'ngIf']); + template( + 1, MyApp_ng_container_1_child_comp_1_Template, 1, 0, 'nested-comp', + [AttributeMarker.Template, 'ngIf']); elementContainerEnd(); } if (rf & RenderFlags.Update) { @@ -1495,7 +1497,7 @@ describe('content projection', () => { elementStart(0, 'root-comp'); template( 1, MyApp_ng_container_1_Template, 2, 1, 'ng-container', - ['ngFor', '', 3, 'ngForOf']); + [AttributeMarker.Template, 'ngFor', 'ngForOf']); elementEnd(); } if (rf & RenderFlags.Update) { @@ -2089,7 +2091,7 @@ describe('content projection', () => { const Parent = createComponent('parent', function(rf: RenderFlags, ctx: {value: any}) { if (rf & RenderFlags.Create) { elementStart(0, 'child'); - { template(1, IfTemplate, 2, 0, 'div', [AttributeMarker.Bindings, 'ngIf']); } + { template(1, IfTemplate, 2, 0, 'div', [AttributeMarker.Template, 'ngIf']); } elementEnd(); } if (rf & RenderFlags.Update) { diff --git a/packages/core/test/render3/di_spec.ts b/packages/core/test/render3/di_spec.ts index bbd6952bea..f972dabf03 100644 --- a/packages/core/test/render3/di_spec.ts +++ b/packages/core/test/render3/di_spec.ts @@ -490,7 +490,7 @@ describe('di', () => { const App = createComponent('app', (rf: RenderFlags, ctx: any) => { if (rf & RenderFlags.Create) { elementStart(0, 'div', ['dirB', '']); - { template(1, IfTemplate, 4, 1, 'div', [AttributeMarker.Bindings, 'ngIf', '']); } + { template(1, IfTemplate, 4, 1, 'div', [AttributeMarker.Template, 'ngIf']); } elementEnd(); } if (rf & RenderFlags.Update) { @@ -1245,7 +1245,10 @@ describe('di', () => { const App = createComponent('app', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div', ['dirB', '']); - { template(1, IfTemplate, 1, 0, 'div', ['ngIf', '']); } + { + template( + 1, IfTemplate, 1, 0, 'div', ['dirA', '', AttributeMarker.Template, 'ngIf']); + } elementEnd(); } if (rf & RenderFlags.Update) { @@ -1787,7 +1790,7 @@ describe('di', () => { it('should inject current component ChangeDetectorRef into directives on the same node as components', () => { - /** {{ dir.value }} */ + /** {{ dir.value }} */ const MyApp = createComponent('my-app', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { element(0, 'my-comp', ['dir', '', 'dirSame', ''], ['dir', 'dir']); @@ -1821,7 +1824,7 @@ describe('di', () => { consts: 3, vars: 1, factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), - /**
{{ dir.value }}
*/ + /**
{{ dir.value }}
*/ template: function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'div', ['dir', '', 'dirSame', ''], ['dir', 'dir']); @@ -1859,7 +1862,7 @@ describe('di', () => { factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), /** * - *
+ *
*
* {{ dir.value }} */ @@ -1903,7 +1906,7 @@ describe('di', () => { vars: 0, /** * % if (showing) { - *
{{ dir.value }}
+ *
{{ dir.value }}
* % } */ template: function(rf: RenderFlags, ctx: MyApp) { @@ -1967,10 +1970,15 @@ describe('di', () => { factory: () => new MyApp(directiveInject(ChangeDetectorRef as any)), consts: 1, vars: 0, - /**
{{ dir.value }}
*/ + /**
{{ dir.value }}
*/ template: function(rf: RenderFlags, ctx: MyApp) { if (rf & RenderFlags.Create) { - template(0, C1, 3, 1, 'div', ['ngIf', 'showing']); + template( + 0, C1, 3, 1, 'div', + ['dir', '', 'dirSame', '', AttributeMarker.Template, 'ngIf']); + } + if (rf & RenderFlags.Update) { + elementProperty(0, 'ngIf', bind(ctx.showing)); } }, directives: directives diff --git a/packages/core/test/render3/directive_spec.ts b/packages/core/test/render3/directive_spec.ts index 045bbcf373..ab65c7828f 100644 --- a/packages/core/test/render3/directive_spec.ts +++ b/packages/core/test/render3/directive_spec.ts @@ -208,7 +208,7 @@ describe('directive', () => { if (rf & RenderFlags.Create) { template( 0, MyComponent_ng_container_Template_0, 2, 0, 'ng-container', - [AttributeMarker.Bindings, 'ngIf']); + ['directiveA', '', AttributeMarker.Template, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngIf', bind(ctx.visible)); diff --git a/packages/core/test/render3/discovery_utils_spec.ts b/packages/core/test/render3/discovery_utils_spec.ts index 1a9a2a8637..52b16866d5 100644 --- a/packages/core/test/render3/discovery_utils_spec.ts +++ b/packages/core/test/render3/discovery_utils_spec.ts @@ -7,7 +7,7 @@ */ import {StaticInjector} from '../../src/di/injector'; import {createInjector} from '../../src/di/r3_injector'; -import {ProvidersFeature, RenderFlags, defineComponent, defineDirective, elementContainerEnd, elementContainerStart, getHostElement, i18n, i18nApply, i18nExp} from '../../src/render3/index'; +import {AttributeMarker, ProvidersFeature, RenderFlags, defineComponent, defineDirective, elementContainerEnd, elementContainerStart, getHostElement, i18n, i18nApply, i18nExp} from '../../src/render3/index'; import {getComponent, getContext, getDirectives, getInjectionTokens, getInjector, getListeners, getLocalRefs, getRootComponents, getViewComponent, loadLContext} from '../../src/render3/util/discovery_utils'; import {element, elementEnd, elementStart, elementStyling, elementStylingApply, template, bind, elementProperty, text, textBinding, markDirty, listener} from '../../src/render3/instructions'; @@ -122,7 +122,7 @@ describe('discovery utils', () => { if (rf & RenderFlags.Create) { element(0, 'child'); } - }, 1, 0, 'child', ['ngIf', '']); + }, 1, 0, 'child', ['dirA', AttributeMarker.Template, 'ngIf']); elementStart(9, 'i18n'); i18n(10, MSG_DIV); elementEnd(); diff --git a/packages/core/test/render3/exports_spec.ts b/packages/core/test/render3/exports_spec.ts index 4e3b8d0ff9..01188cf90e 100644 --- a/packages/core/test/render3/exports_spec.ts +++ b/packages/core/test/render3/exports_spec.ts @@ -232,7 +232,7 @@ describe('exports', () => { if (rf & RenderFlags.Create) { elementStart(0, 'input', ['value', 'one'], ['outerInput', '']); elementEnd(); - template(2, outerTemplate, 5, 2, 'div', [AttributeMarker.Bindings, 'ngIf']); + template(2, outerTemplate, 5, 2, 'div', [AttributeMarker.Template, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(2, 'ngIf', bind(app.outer)); @@ -246,7 +246,7 @@ describe('exports', () => { text(1); elementStart(2, 'input', ['value', 'two'], ['innerInput', '']); elementEnd(); - template(4, innerTemplate, 2, 2, 'div', [AttributeMarker.Bindings, 'ngIf']); + template(4, innerTemplate, 2, 2, 'div', [AttributeMarker.Template, 'ngIf']); } elementEnd(); } diff --git a/packages/core/test/render3/host_binding_spec.ts b/packages/core/test/render3/host_binding_spec.ts index 9bb9813c05..e74b9630fa 100644 --- a/packages/core/test/render3/host_binding_spec.ts +++ b/packages/core/test/render3/host_binding_spec.ts @@ -506,7 +506,7 @@ describe('host bindings', () => { */ const App = createComponent('parent', (rf: RenderFlags, ctx: any) => { if (rf & RenderFlags.Create) { - template(0, NgForTemplate, 2, 0, 'div', ['ngForOf', '']); + template(0, NgForTemplate, 2, 0, 'div', [AttributeMarker.Template, 'ngFor', 'ngForOf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngForOf', bind(ctx.rows)); diff --git a/packages/core/test/render3/i18n_spec.ts b/packages/core/test/render3/i18n_spec.ts index 2a43416bbc..a23584352e 100644 --- a/packages/core/test/render3/i18n_spec.ts +++ b/packages/core/test/render3/i18n_spec.ts @@ -603,7 +603,7 @@ describe('Runtime i18n', () => { if (rf & RenderFlags.Create) { i18nStart(0, MSG_DIV, 1); elementStart(1, 'div'); - template(2, subTemplate_2, 2, 0, 'span', ['ngIf', '']); + template(2, subTemplate_2, 2, 0, 'span', [AttributeMarker.Template, 'ngIf']); elementEnd(); i18nEnd(); } @@ -632,7 +632,7 @@ describe('Runtime i18n', () => { if (rf & RenderFlags.Create) { elementStart(0, 'div'); i18nStart(1, MSG_DIV); - template(2, subTemplate_1, 3, 1, 'div', ['ngIf', '']); + template(2, subTemplate_1, 3, 1, 'div', [AttributeMarker.Template, 'ngIf']); i18nEnd(); elementEnd(); } @@ -739,7 +739,7 @@ describe('Runtime i18n', () => { if (rf & RenderFlags.Create) { elementStart(0, 'div'); i18nStart(1, MSG_DIV); - template(2, subTemplate_1, 2, 2, 'span', [3, 'ngIf']); + template(2, subTemplate_1, 2, 2, 'span', [AttributeMarker.Template, 'ngIf']); i18nEnd(); elementEnd(); } diff --git a/packages/core/test/render3/instructions_spec.ts b/packages/core/test/render3/instructions_spec.ts index f69ec88ee5..577c6e4748 100644 --- a/packages/core/test/render3/instructions_spec.ts +++ b/packages/core/test/render3/instructions_spec.ts @@ -304,12 +304,13 @@ describe('instructions', () => { describe('performance counters', () => { it('should create tViews only once for each nested level', () => { - const _c0 = ['ngFor', '', 'ngForOf', '']; + const _c0 = [AttributeMarker.Template, 'ngFor', 'ngForOf']; + const _c1 = [AttributeMarker.Template, 'ngFor', 'ngForOf']; function ToDoAppComponent_NgForOf_Template_0(rf: RenderFlags, ctx0: NgForOfContext) { if (rf & RenderFlags.Create) { elementStart(0, 'ul'); - template(1, ToDoAppComponent_NgForOf_NgForOf_Template_1, 2, 1, 'li', _c0); + template(1, ToDoAppComponent_NgForOf_NgForOf_Template_1, 2, 1, 'li', _c1); elementEnd(); } if (rf & RenderFlags.Update) { diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 6e825ebd0f..bf09baa1a2 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -721,8 +721,7 @@ describe('render3 integration test', () => { const TestCmpt = createComponent('test-cmpt', function(rf: RenderFlags, ctx: {value: any}) { if (rf & RenderFlags.Create) { - template( - 0, ngIfTemplate, 2, 0, 'ng-template', [AttributeMarker.Bindings, 'ngIf']); + template(0, ngIfTemplate, 2, 0, 'ng-template', [AttributeMarker.Bindings, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngIf', bind(ctx.value)); diff --git a/packages/core/test/render3/lifecycle_spec.ts b/packages/core/test/render3/lifecycle_spec.ts index eb9c497948..980195ba87 100644 --- a/packages/core/test/render3/lifecycle_spec.ts +++ b/packages/core/test/render3/lifecycle_spec.ts @@ -198,7 +198,7 @@ describe('lifecycles', () => { /** */ const App = createComponent('app', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - template(0, IfTemplate, 1, 0, 'comp', ['ngIf', '']); + template(0, IfTemplate, 1, 0, 'comp', [AttributeMarker.Template, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngIf', bind(ctx.showing)); @@ -2969,8 +2969,7 @@ describe('lifecycles', () => { function conditionTpl(rf: RenderFlags, ctx: Cmpt) { if (rf & RenderFlags.Create) { - template( - 0, null, 0, 1, 'ng-template', [AttributeMarker.Bindings, 'onDestroyDirective']); + template(0, null, 0, 1, 'ng-template', [AttributeMarker.Bindings, 'onDestroyDirective']); } } diff --git a/packages/core/test/render3/pure_function_spec.ts b/packages/core/test/render3/pure_function_spec.ts index 73e3566d6b..61499cc15f 100644 --- a/packages/core/test/render3/pure_function_spec.ts +++ b/packages/core/test/render3/pure_function_spec.ts @@ -87,7 +87,9 @@ describe('array literals', () => { */ const App = createComponent('app', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - template(0, IfTemplate, 1, 3, 'my-comp', [AttributeMarker.Bindings, 'ngIf']); + template( + 0, IfTemplate, 1, 3, 'my-comp', + [AttributeMarker.Bindings, 'names', AttributeMarker.Template, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngIf', bind(ctx.showing)); diff --git a/packages/core/test/render3/query_spec.ts b/packages/core/test/render3/query_spec.ts index eccb58954e..46c20f5376 100644 --- a/packages/core/test/render3/query_spec.ts +++ b/packages/core/test/render3/query_spec.ts @@ -1392,7 +1392,8 @@ describe('query', () => { 'cmpt', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { - template(0, Cmpt_Template_1, 2, 0, 'ng-template', ['ngIf', '']); + template( + 0, Cmpt_Template_1, 2, 0, 'ng-template', [AttributeMarker.Bindings, 'ngIf']); } if (rf & RenderFlags.Update) { elementProperty(0, 'ngIf', bind(ctx.value)); @@ -2207,7 +2208,7 @@ describe('query', () => { function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { template( - 0, AppComponent_Template_1, 1, 0, 'div', [AttributeMarker.Bindings, 'someDir']); + 0, AppComponent_Template_1, 1, 0, 'div', [AttributeMarker.Template, 'someDir']); element(1, 'div', null, ['foo', '']); } }, @@ -2392,7 +2393,7 @@ describe('query', () => { const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) { if (rf & RenderFlags.Create) { elementStart(0, 'shallow-comp'); - { template(1, IfTemplate, 2, 0, 'div', [AttributeMarker.Bindings, 'ngIf', '']); } + { template(1, IfTemplate, 2, 0, 'div', [AttributeMarker.Template, 'ngIf', '']); } elementEnd(); } if (rf & RenderFlags.Update) {