1+ /**
2+ * @license
3+ * Copyright Google Inc. All Rights Reserved.
4+ *
5+ * Use of this source code is governed by an MIT-style license that can be
6+ * found in the LICENSE file at https://angular.io/license
7+ */
8+
9+ import { TemplateRef } from '@angular/core' ;
10+
11+ import { ComponentFixture , createComponent , getDirectiveOnNode } from './render_util' ;
12+ import { bind , directiveInject , element , elementContainerStart , elementContainerEnd , elementProperty , template , text } from '../../src/render3/instructions' ;
13+ import { RenderFlags , defineDirective , AttributeMarker } from '../../src/render3/index' ;
14+
15+ import { NgIf } from './common_with_def' ;
16+
17+ describe ( 'TemplateRef' , ( ) => {
18+
19+ describe ( 'rootNodes' , ( ) => {
20+
21+ class DirectiveWithTplRef {
22+ static ngDirectiveDef = defineDirective ( {
23+ type : DirectiveWithTplRef ,
24+ selectors : [ [ '' , 'tplRef' , '' ] ] ,
25+ factory : ( ) => new DirectiveWithTplRef ( directiveInject ( TemplateRef as any ) )
26+ } ) ;
27+
28+ // injecting a ViewContainerRef to create a dynamic container in which embedded views will be
29+ // created
30+ constructor ( public tplRef : TemplateRef < { } > ) { }
31+ }
32+
33+ it ( 'should return root render nodes for an embedded view instance' , ( ) => {
34+ let directiveWithTplRef : DirectiveWithTplRef ;
35+
36+ function embeddedTemplate ( rf : RenderFlags , ctx : any ) {
37+ if ( rf & RenderFlags . Create ) {
38+ element ( 0 , 'div' ) ;
39+ text ( 1 , 'some text' ) ;
40+ element ( 2 , 'span' ) ;
41+ }
42+ }
43+
44+ /*
45+ <ng-template tplRef>
46+ <div></div>
47+ some text
48+ <span></span>
49+ </ng-template>
50+ */
51+ const AppComponent = createComponent ( 'app-cmp' , function ( rf : RenderFlags , ctx : any ) {
52+ if ( rf & RenderFlags . Create ) {
53+ template ( 0 , embeddedTemplate , 3 , 0 , null , [ 'tplRef' , '' ] ) ;
54+ directiveWithTplRef = getDirectiveOnNode ( 0 , 0 ) ;
55+ }
56+ } , 1 , 0 , [ DirectiveWithTplRef ] ) ;
57+
58+
59+ const fixture = new ComponentFixture ( AppComponent ) ;
60+ expect ( directiveWithTplRef ! ) . toBeDefined ( ) ;
61+
62+ const viewRef = directiveWithTplRef ! . tplRef . createEmbeddedView ( { } ) ;
63+ expect ( viewRef . rootNodes . length ) . toBe ( 3 ) ;
64+ } ) ;
65+
66+ /**
67+ * This is different as compared to the view engine implementation which returns a comment node
68+ * in this case:
69+ * https://stackblitz.com/edit/angular-uiqry6?file=src/app/app.component.ts
70+ *
71+ * Returning a comment node for a template ref with no nodes is wrong and should be fixed in
72+ * ivy.
73+ */
74+ it ( 'should return an empty array for embedded view with no nodes' , ( ) => {
75+ let directiveWithTplRef : DirectiveWithTplRef ;
76+
77+ /*
78+ <ng-template tplRef></ng-template>
79+ */
80+ const AppComponent = createComponent ( 'app-cmp' , function ( rf : RenderFlags , ctx : any ) {
81+ if ( rf & RenderFlags . Create ) {
82+ template ( 0 , ( ) => { } , 0 , 0 , null , [ 'tplRef' , '' ] ) ;
83+ directiveWithTplRef = getDirectiveOnNode ( 0 , 0 ) ;
84+ }
85+ } , 1 , 0 , [ DirectiveWithTplRef ] ) ;
86+
87+
88+ const fixture = new ComponentFixture ( AppComponent ) ;
89+ expect ( directiveWithTplRef ! ) . toBeDefined ( ) ;
90+
91+ const viewRef = directiveWithTplRef ! . tplRef . createEmbeddedView ( { } ) ;
92+ expect ( viewRef . rootNodes . length ) . toBe ( 0 ) ;
93+ } ) ;
94+
95+ /**
96+ * This is somehow surprising but the current view engine don't descend into containers when
97+ * getting root nodes of an embedded view:
98+ * https://stackblitz.com/edit/angular-z8zev7?file=src/app/app.component.ts
99+ */
100+ it ( 'should not descend into containers when retrieving root nodes' , ( ) => {
101+ let directiveWithTplRef : DirectiveWithTplRef ;
102+
103+ function ngIfTemplate ( rf : RenderFlags , ctx : any ) {
104+ if ( rf & RenderFlags . Create ) {
105+ text ( 0 , 'text' ) ;
106+ }
107+ }
108+
109+ function embeddedTemplate ( rf : RenderFlags , ctx : any ) {
110+ if ( rf & RenderFlags . Create ) {
111+ template ( 0 , ngIfTemplate , 1 , 0 , null , [ AttributeMarker . SelectOnly , 'ngIf' ] ) ;
112+ }
113+ if ( rf & RenderFlags . Update ) {
114+ elementProperty ( 0 , 'ngIf' , bind ( ctx . showing ) ) ;
115+ }
116+ }
117+
118+ /*
119+ <ng-template tplRef><ng-template [ngIf]="true">text</ng-template></ng-template>
120+ */
121+ const AppComponent = createComponent ( 'app-cmp' , function ( rf : RenderFlags , ctx : any ) {
122+ if ( rf & RenderFlags . Create ) {
123+ template ( 0 , embeddedTemplate , 1 , 1 , null , [ 'tplRef' , '' ] ) ;
124+ directiveWithTplRef = getDirectiveOnNode ( 0 , 0 ) ;
125+ }
126+ } , 1 , 0 , [ DirectiveWithTplRef , NgIf ] ) ;
127+
128+
129+ const fixture = new ComponentFixture ( AppComponent ) ;
130+ expect ( directiveWithTplRef ! ) . toBeDefined ( ) ;
131+
132+ const viewRef = directiveWithTplRef ! . tplRef . createEmbeddedView ( { } ) ;
133+
134+ // assert that we've got a comment node (only!) corresponding to <ng-template [ngIf]="true">
135+ expect ( viewRef . rootNodes . length ) . toBe ( 1 ) ;
136+ expect ( viewRef . rootNodes [ 0 ] . nodeType ) . toBe ( 8 ) ;
137+ } ) ;
138+
139+
140+ /**
141+ * Contrary to containers (<ng-template>) we _do_ descend into element containers
142+ * (<ng-container):
143+ * https://stackblitz.com/edit/angular-yovmmp?file=src/app/app.component.ts
144+ */
145+ it ( 'should descend into element containers when retrieving root nodes' , ( ) => {
146+ let directiveWithTplRef : DirectiveWithTplRef ;
147+
148+ function embeddedTemplate ( rf : RenderFlags , ctx : any ) {
149+ if ( rf & RenderFlags . Create ) {
150+ elementContainerStart ( 0 ) ;
151+ { text ( 1 , 'text' ) ; }
152+ elementContainerEnd ( ) ;
153+ }
154+ }
155+
156+ /*
157+ <ng-template tplRef><ng-container>text</ng-container></ng-template>
158+ */
159+ const AppComponent = createComponent ( 'app-cmp' , function ( rf : RenderFlags , ctx : any ) {
160+ if ( rf & RenderFlags . Create ) {
161+ template ( 0 , embeddedTemplate , 2 , 0 , null , [ 'tplRef' , '' ] ) ;
162+ directiveWithTplRef = getDirectiveOnNode ( 0 , 0 ) ;
163+ }
164+ } , 1 , 0 , [ DirectiveWithTplRef ] ) ;
165+
166+
167+ const fixture = new ComponentFixture ( AppComponent ) ;
168+ expect ( directiveWithTplRef ! ) . toBeDefined ( ) ;
169+
170+ const viewRef = directiveWithTplRef ! . tplRef . createEmbeddedView ( { } ) ;
171+
172+ expect ( viewRef . rootNodes . length ) . toBe ( 2 ) ;
173+ expect ( viewRef . rootNodes [ 0 ] . nodeType )
174+ . toBe ( 8 ) ; // a comment node (only!) corresponding to <ng-container>
175+ expect ( viewRef . rootNodes [ 1 ] . nodeType ) . toBe ( 3 ) ; // a text node
176+ } ) ;
177+ } ) ;
178+ } ) ;
0 commit comments