1- import { Directive , EmbeddedViewRef , Input , OnChanges , ChangeDetectorRef , SimpleChange , SimpleChanges , TemplateRef , ViewContainerRef , NgZone , Output , EventEmitter , inject } from '@angular/core' ;
2-
1+ import {
2+ Directive ,
3+ EmbeddedViewRef ,
4+ Input ,
5+ OnChanges ,
6+ SimpleChange ,
7+ SimpleChanges ,
8+ TemplateRef ,
9+ ViewContainerRef ,
10+ Output ,
11+ EventEmitter ,
12+ inject
13+ } from '@angular/core' ;
314import { IBaseEventArgs } from 'igniteui-angular/core' ;
415
516/**
@@ -10,9 +21,17 @@ import { IBaseEventArgs } from 'igniteui-angular/core';
1021 standalone : true
1122} )
1223export class IgxTemplateOutletDirective implements OnChanges {
13- public _viewContainerRef = inject ( ViewContainerRef ) ;
14- private _zone = inject ( NgZone ) ;
15- public cdr = inject ( ChangeDetectorRef ) ;
24+ private readonly _viewContainerRef = inject ( ViewContainerRef ) ;
25+
26+ /**
27+ * The embedded views cache. Collection is key-value paired.
28+ * Key is the template type, value is another key-value paired collection
29+ * where the key is the template id and value is the embedded view for the related template.
30+ */
31+ private readonly _embeddedViewsMap : Map < string , Map < any , EmbeddedViewRef < any > > > = new Map ( ) ;
32+
33+ private _viewRef ! : EmbeddedViewRef < any > ;
34+
1635
1736 @Input ( ) public igxTemplateOutletContext ! : any ;
1837
@@ -30,67 +49,68 @@ export class IgxTemplateOutletDirective implements OnChanges {
3049 @Output ( )
3150 public beforeViewDetach = new EventEmitter < IViewChangeEventArgs > ( ) ;
3251
33- private _viewRef ! : EmbeddedViewRef < any > ;
34-
35- /**
36- * The embedded views cache. Collection is key-value paired.
37- * Key is the template type, value is another key-value paired collection
38- * where the key is the template id and value is the embedded view for the related template.
39- */
40- private _embeddedViewsMap : Map < string , Map < any , EmbeddedViewRef < any > > > = new Map ( ) ;
41-
4252 public ngOnChanges ( changes : SimpleChanges ) {
43- const actionType : TemplateOutletAction = this . _getActionType ( changes ) ;
53+ const { actionType, cachedView } = this . _getActionType ( changes ) ;
54+
4455 switch ( actionType ) {
4556 case TemplateOutletAction . CreateView : this . _recreateView ( ) ; break ;
4657 case TemplateOutletAction . MoveView : this . _moveView ( ) ; break ;
47- case TemplateOutletAction . UseCachedView : this . _useCachedView ( ) ; break ;
58+ case TemplateOutletAction . UseCachedView : this . _useCachedView ( cachedView ) ; break ;
4859 case TemplateOutletAction . UpdateViewContext : this . _updateExistingContext ( this . igxTemplateOutletContext ) ; break ;
4960 }
5061 }
5162
52- public cleanCache ( ) {
53- this . _embeddedViewsMap . forEach ( ( collection ) => {
54- collection . forEach ( ( item => {
63+ public cleanCache ( ) : void {
64+ for ( const collection of this . _embeddedViewsMap . values ( ) ) {
65+ for ( const item of collection . values ( ) ) {
5566 if ( ! item . destroyed ) {
5667 item . destroy ( ) ;
5768 }
58- } ) ) ;
69+ }
5970 collection . clear ( ) ;
60- } ) ;
71+ }
72+
6173 this . _embeddedViewsMap . clear ( ) ;
6274 }
6375
64- public cleanView ( tmplID ) {
65- const embViewCollection = this . _embeddedViewsMap . get ( tmplID . type ) ;
66- const embView = embViewCollection ?. get ( tmplID . id ) ;
67- if ( embView ) {
68- embView . destroy ( ) ;
69- this . _embeddedViewsMap . get ( tmplID . type ) . delete ( tmplID . id ) ;
76+ public cleanView ( templateId : { type : string ; id : any } ) : void {
77+ const viewCollection = this . _embeddedViewsMap . get ( templateId . type ) ;
78+ const view = viewCollection ?. get ( templateId . id ) ;
79+
80+ if ( view ) {
81+ view . destroy ( ) ;
82+ this . _embeddedViewsMap . get ( templateId . type ) . delete ( templateId . id ) ;
7083 }
7184 }
7285
73- private _recreateView ( ) {
74- const prevIndex = this . _viewRef ? this . _viewContainerRef . indexOf ( this . _viewRef ) : - 1 ;
86+ private _recreateView ( ) : void {
87+ const prevIndex = this . _viewContainerRef . indexOf ( this . _viewRef ) ;
88+
7589 // detach old and create new
7690 if ( prevIndex !== - 1 ) {
7791 this . beforeViewDetach . emit ( { owner : this , view : this . _viewRef , context : this . igxTemplateOutletContext } ) ;
7892 this . _viewContainerRef . detach ( prevIndex ) ;
7993 }
94+
8095 if ( this . igxTemplateOutlet ) {
8196 this . _viewRef = this . _viewContainerRef . createEmbeddedView (
8297 this . igxTemplateOutlet , this . igxTemplateOutletContext ) ;
8398 this . viewCreated . emit ( { owner : this , view : this . _viewRef , context : this . igxTemplateOutletContext } ) ;
84- const tmplId = this . igxTemplateOutletContext [ 'templateID' ] ;
85- if ( tmplId ) {
99+ const templateId = this . igxTemplateOutletContext [ 'templateID' ] ;
100+
101+ if ( templateId ) {
86102 // if context contains a template id, check if we have a view for that template already stored in the cache
87103 // if not create a copy and add it to the cache in detached state.
88104 // Note: Views in detached state do not appear in the DOM, however they remain stored in memory.
89- const resCollection = this . _embeddedViewsMap . get ( this . igxTemplateOutletContext [ 'templateID' ] . type ) ;
90- const res = resCollection ?. get ( this . igxTemplateOutletContext [ 'templateID' ] . id ) ;
91- if ( ! res ) {
92- this . _embeddedViewsMap . set ( this . igxTemplateOutletContext [ 'templateID' ] . type ,
93- new Map ( [ [ this . igxTemplateOutletContext [ 'templateID' ] . id , this . _viewRef ] ] ) ) ;
105+ let resCollection = this . _embeddedViewsMap . get ( templateId . type ) ;
106+
107+ if ( ! resCollection ) {
108+ resCollection = new Map ( ) ;
109+ this . _embeddedViewsMap . set ( templateId . type , resCollection ) ;
110+ }
111+
112+ if ( ! resCollection . has ( templateId . id ) ) {
113+ resCollection . set ( templateId . id , this . _viewRef ) ;
94114 }
95115 }
96116 }
@@ -100,16 +120,22 @@ export class IgxTemplateOutletDirective implements OnChanges {
100120 // using external view and inserting it in current view.
101121 const view = this . igxTemplateOutletContext [ 'moveView' ] ;
102122 const owner = this . igxTemplateOutletContext [ 'owner' ] ;
123+
103124 if ( view !== this . _viewRef ) {
104- if ( owner . _viewContainerRef . indexOf ( view ) !== - 1 ) {
125+ const viewIndex = owner . _viewContainerRef . indexOf ( view ) ;
126+ const viewRefIndex = this . _viewContainerRef . indexOf ( this . _viewRef ) ;
127+
128+ if ( viewIndex !== - 1 ) {
105129 // detach in case view it is attached somewhere else at the moment.
106130 this . beforeViewDetach . emit ( { owner : this , view : this . _viewRef , context : this . igxTemplateOutletContext } ) ;
107- owner . _viewContainerRef . detach ( owner . _viewContainerRef . indexOf ( view ) ) ;
131+ owner . _viewContainerRef . detach ( viewIndex ) ;
108132 }
109- if ( this . _viewRef && this . _viewContainerRef . indexOf ( this . _viewRef ) !== - 1 ) {
133+
134+ if ( this . _viewRef && viewRefIndex !== - 1 ) {
110135 this . beforeViewDetach . emit ( { owner : this , view : this . _viewRef , context : this . igxTemplateOutletContext } ) ;
111- this . _viewContainerRef . detach ( this . _viewContainerRef . indexOf ( this . _viewRef ) ) ;
136+ this . _viewContainerRef . detach ( viewRefIndex ) ;
112137 }
138+
113139 this . _viewRef = view ;
114140 this . _viewContainerRef . insert ( view , 0 ) ;
115141 this . _updateExistingContext ( this . igxTemplateOutletContext ) ;
@@ -118,12 +144,9 @@ export class IgxTemplateOutletDirective implements OnChanges {
118144 this . _updateExistingContext ( this . igxTemplateOutletContext ) ;
119145 }
120146 }
121- private _useCachedView ( ) {
147+
148+ private _useCachedView ( cachedView : EmbeddedViewRef < any > ) {
122149 // use view for specific template cached in the current template outlet
123- const tmplID = this . igxTemplateOutletContext [ 'templateID' ] ;
124- const cachedView = tmplID ?
125- this . _embeddedViewsMap . get ( tmplID . type ) ?. get ( tmplID . id ) :
126- null ;
127150 // if view exists, but template has been changed and there is a view in the cache with the related template
128151 // then detach old view and insert the stored one with the matching template
129152 // after that update its context.
@@ -133,7 +156,7 @@ export class IgxTemplateOutletDirective implements OnChanges {
133156 }
134157
135158 this . _viewRef = cachedView ;
136- const oldContext = this . _cloneContext ( cachedView . context ) ;
159+ const oldContext = { ... cachedView . context } ;
137160 this . _viewContainerRef . insert ( this . _viewRef , 0 ) ;
138161 this . _updateExistingContext ( this . igxTemplateOutletContext ) ;
139162 this . cachedViewLoaded . emit ( { owner : this , view : this . _viewRef , context : this . igxTemplateOutletContext , oldContext } ) ;
@@ -145,54 +168,41 @@ export class IgxTemplateOutletDirective implements OnChanges {
145168 }
146169
147170 private _hasContextShapeChanged ( ctxChange : SimpleChange ) : boolean {
148- const prevCtxKeys = Object . keys ( ctxChange . previousValue || { } ) ;
149- const currCtxKeys = Object . keys ( ctxChange . currentValue || { } ) ;
171+ const prevKeys = new Set ( Object . keys ( ctxChange . previousValue || { } ) ) ;
172+ const currKeys = new Set ( Object . keys ( ctxChange . currentValue || { } ) ) ;
150173
151- if ( prevCtxKeys . length === currCtxKeys . length ) {
152- for ( const propName of currCtxKeys ) {
153- if ( prevCtxKeys . indexOf ( propName ) === - 1 ) {
154- return true ;
155- }
156- }
157- return false ;
158- } else {
174+
175+ if ( prevKeys . size !== currKeys . size ) {
159176 return true ;
160177 }
161- }
162178
163- private _updateExistingContext ( ctx : any ) : void {
164- for ( const propName of Object . keys ( ctx ) ) {
165- this . _viewRef . context [ propName ] = this . igxTemplateOutletContext [ propName ] ;
166- }
179+ return currKeys . difference ( prevKeys ) . size > 0 ;
167180 }
168181
169- private _cloneContext ( ctx : any ) : any {
170- const clone = { } ;
171- for ( const propName of Object . keys ( ctx ) ) {
172- clone [ propName ] = ctx [ propName ] ;
173- }
174- return clone ;
182+ private _updateExistingContext ( ctx : any ) : void {
183+ Object . assign ( this . _viewRef . context , ctx ) ;
175184 }
176185
177- private _getActionType ( changes : SimpleChanges ) {
186+ private _getActionType ( changes : SimpleChanges ) : { actionType : TemplateOutletAction ; cachedView : EmbeddedViewRef < any > | null } {
178187 const movedView = this . igxTemplateOutletContext [ 'moveView' ] ;
179- const tmplID = this . igxTemplateOutletContext [ 'templateID' ] ;
180- const cachedView = tmplID ?
181- this . _embeddedViewsMap . get ( tmplID . type ) ?. get ( tmplID . id ) :
188+ const templateId = this . igxTemplateOutletContext [ 'templateID' ] ;
189+ const cachedView = templateId ?
190+ this . _embeddedViewsMap . get ( templateId . type ) ?. get ( templateId . id ) :
182191 null ;
183192 const shouldRecreate = this . _shouldRecreateView ( changes ) ;
193+
184194 if ( movedView ) {
185195 // view is moved from external source
186- return TemplateOutletAction . MoveView ;
196+ return { actionType : TemplateOutletAction . MoveView , cachedView } ;
187197 } else if ( shouldRecreate && cachedView ) {
188198 // should recreate (template or context change) and there is a matching template in cache
189- return TemplateOutletAction . UseCachedView ;
199+ return { actionType : TemplateOutletAction . UseCachedView , cachedView } ;
190200 } else if ( ! this . _viewRef || shouldRecreate ) {
191201 // no view or should recreate
192- return TemplateOutletAction . CreateView ;
202+ return { actionType : TemplateOutletAction . CreateView , cachedView } ;
193203 } else if ( this . igxTemplateOutletContext ) {
194204 // has context, update context
195- return TemplateOutletAction . UpdateViewContext ;
205+ return { actionType : TemplateOutletAction . UpdateViewContext , cachedView } ;
196206 }
197207 }
198208}
@@ -212,8 +222,3 @@ export interface IViewChangeEventArgs extends IBaseEventArgs {
212222export interface ICachedViewLoadedEventArgs extends IViewChangeEventArgs {
213223 oldContext : any ;
214224}
215-
216- /**
217- * @hidden
218- */
219-
0 commit comments