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