1
- import { Inject , Injectable , NgZone , Optional , Renderer2 , RendererFactory2 , RendererStyleFlags2 , RendererType2 , ViewEncapsulation } from '@angular/core' ;
1
+ import { Inject , Injectable , Injector , NgZone , Optional , Renderer2 , RendererFactory2 , RendererStyleFlags2 , RendererType2 , ViewEncapsulation , inject , runInInjectionContext } from '@angular/core' ;
2
2
import { addTaggedAdditionalCSS , Application , ContentView , Device , getViewById , Observable , profile , Utils , View } from '@nativescript/core' ;
3
3
import { getViewClass , isKnownView } from './element-registry' ;
4
4
import { getFirstNativeLikeView , NgView , TextNode } from './views' ;
5
5
6
6
import { NamespaceFilter , NAMESPACE_FILTERS } from './property-filter' ;
7
- import { APP_ROOT_VIEW , ENABLE_REUSABE_VIEWS , NATIVESCRIPT_ROOT_MODULE_ID } from './tokens' ;
7
+ import { APP_ROOT_VIEW , ENABLE_REUSABE_VIEWS , NATIVESCRIPT_ROOT_MODULE_ID , PREVENT_SPECIFIC_EVENTS_DURING_CD } from './tokens' ;
8
8
import { NativeScriptDebug } from './trace' ;
9
9
import { ViewUtil } from './view-util' ;
10
10
@@ -34,17 +34,68 @@ function inRootZone() {
34
34
} ;
35
35
}
36
36
37
+ @Injectable ( {
38
+ providedIn : 'root' ,
39
+ } )
40
+ export class NativeScriptRendererHelperService {
41
+ private _executingDomChanges = 0 ;
42
+ get executingDomChanges ( ) {
43
+ return this . _executingDomChanges ;
44
+ }
45
+ get isExecutingDomChanges ( ) {
46
+ return this . _executingDomChanges > 0 ;
47
+ }
48
+ beginDomChanges ( ) {
49
+ this . _executingDomChanges ++ ;
50
+ }
51
+ endDomChanges ( ) {
52
+ this . _executingDomChanges -- ;
53
+ }
54
+ executeDomChange < T > ( fn : ( ) => T ) : T {
55
+ try {
56
+ this . beginDomChanges ( ) ;
57
+ return fn ( ) ;
58
+ } finally {
59
+ this . endDomChanges ( ) ;
60
+ }
61
+ }
62
+ }
63
+
64
+ function modifiesDom ( ) {
65
+ return function (
66
+ target : {
67
+ _rendererHelper : NativeScriptRendererHelperService ;
68
+ } ,
69
+ key : string | symbol ,
70
+ descriptor : PropertyDescriptor ,
71
+ ) {
72
+ const childFunction = descriptor . value ;
73
+ descriptor . value = function ( ...args : unknown [ ] ) {
74
+ const fn = childFunction . bind ( this ) ;
75
+ return this . _rendererHelper . executeDomChange ( ( ) => fn ( ...args ) ) ;
76
+ } ;
77
+ return descriptor ;
78
+ } ;
79
+ }
80
+
37
81
export class NativeScriptRendererFactory implements RendererFactory2 {
38
82
private componentRenderers = new Map < string , Renderer2 > ( ) ;
39
83
private defaultRenderer : Renderer2 ;
40
84
// backwards compatibility with RadListView
85
+ private rootView = inject ( APP_ROOT_VIEW ) ;
86
+ private namespaceFilters = inject ( NAMESPACE_FILTERS ) ;
87
+ private rootModuleID = inject ( NATIVESCRIPT_ROOT_MODULE_ID ) ;
88
+ private reuseViews = inject ( ENABLE_REUSABE_VIEWS , {
89
+ optional : true ,
90
+ } ) ;
91
+ private injector = inject ( Injector ) ;
41
92
private viewUtil = new ViewUtil ( this . namespaceFilters , this . reuseViews ) ;
42
93
43
- constructor ( @ Inject ( APP_ROOT_VIEW ) private rootView : View , @ Inject ( NAMESPACE_FILTERS ) private namespaceFilters : NamespaceFilter [ ] , @ Inject ( NATIVESCRIPT_ROOT_MODULE_ID ) private rootModuleID : string | number , @ Optional ( ) @ Inject ( ENABLE_REUSABE_VIEWS ) private reuseViews ) {
94
+ constructor ( ) {
44
95
if ( typeof this . reuseViews !== 'boolean' ) {
45
96
this . reuseViews = false ; // default to false
46
97
}
47
- this . defaultRenderer = new NativeScriptRenderer ( rootView , namespaceFilters , this . reuseViews ) ;
98
+ this . defaultRenderer = new NativeScriptRenderer ( this . rootView ) ;
48
99
}
49
100
createRenderer ( hostElement : any , type : RendererType2 ) : Renderer2 {
50
101
if ( NativeScriptDebug . enabled ) {
@@ -77,7 +128,9 @@ export class NativeScriptRendererFactory implements RendererFactory2 {
77
128
type . styles . map ( ( s ) => s . toString ( ) ) . forEach ( ( v ) => addStyleToCss ( v , this . rootModuleID ) ) ;
78
129
renderer = this . defaultRenderer ;
79
130
} else {
80
- renderer = new EmulatedRenderer ( type , hostElement , this . namespaceFilters , this . rootModuleID , this . reuseViews ) ;
131
+ runInInjectionContext ( this . injector , ( ) => {
132
+ renderer = new EmulatedRenderer ( type , hostElement ) ;
133
+ } ) ;
81
134
( < EmulatedRenderer > renderer ) . applyToHost ( hostElement ) ;
82
135
}
83
136
@@ -126,9 +179,23 @@ export class NativeScriptRendererFactory implements RendererFactory2 {
126
179
}
127
180
128
181
class NativeScriptRenderer implements Renderer2 {
182
+ private namespaceFilters = inject ( NAMESPACE_FILTERS ) ;
183
+ private reuseViews = inject ( ENABLE_REUSABE_VIEWS , {
184
+ optional : true ,
185
+ } ) ;
129
186
private viewUtil = new ViewUtil ( this . namespaceFilters , this . reuseViews ) ;
187
+ _rendererHelper = inject ( NativeScriptRendererHelperService ) ;
188
+ private specificPreventedEvents = new Set (
189
+ inject ( PREVENT_SPECIFIC_EVENTS_DURING_CD , {
190
+ optional : true ,
191
+ } ) ?? [ ] ,
192
+ ) ;
193
+ private preventChangeEvents =
194
+ inject ( PREVENT_SPECIFIC_EVENTS_DURING_CD , {
195
+ optional : true ,
196
+ } ) ?? false ;
130
197
131
- constructor ( private rootView : View , private namespaceFilters ?: NamespaceFilter [ ] , private reuseViews ?: boolean ) { }
198
+ constructor ( private rootView : View ) { }
132
199
get data ( ) : { [ key : string ] : any } {
133
200
throw new Error ( 'Method not implemented.' ) ;
134
201
}
@@ -138,6 +205,7 @@ class NativeScriptRenderer implements Renderer2 {
138
205
}
139
206
}
140
207
@inRootZone ( )
208
+ @modifiesDom ( )
141
209
createElement ( name : string , namespace ?: string ) {
142
210
if ( NativeScriptDebug . enabled ) {
143
211
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.createElement: ${ name } ` ) ;
@@ -154,13 +222,15 @@ class NativeScriptRenderer implements Renderer2 {
154
222
return view ;
155
223
}
156
224
@inRootZone ( )
225
+ @modifiesDom ( )
157
226
createComment ( value : string ) {
158
227
if ( NativeScriptDebug . enabled ) {
159
228
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.createComment ${ value } ` ) ;
160
229
}
161
230
return this . viewUtil . createComment ( value ) ;
162
231
}
163
232
@inRootZone ( )
233
+ @modifiesDom ( )
164
234
createText ( value : string ) {
165
235
if ( NativeScriptDebug . enabled ) {
166
236
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.createText ${ value } ` ) ;
@@ -177,20 +247,23 @@ class NativeScriptRenderer implements Renderer2 {
177
247
}
178
248
} ) ;
179
249
@inRootZone ( )
250
+ @modifiesDom ( )
180
251
appendChild ( parent : View , newChild : View ) : void {
181
252
if ( NativeScriptDebug . enabled ) {
182
253
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.appendChild child: ${ newChild } parent: ${ parent } ` ) ;
183
254
}
184
255
this . viewUtil . appendChild ( parent , newChild ) ;
185
256
}
186
257
@inRootZone ( )
258
+ @modifiesDom ( )
187
259
insertBefore ( parent : any , newChild : any , refChild : any ) : void {
188
260
if ( NativeScriptDebug . enabled ) {
189
261
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.insertBefore child: ${ newChild } ` + `parent: ${ parent } refChild: ${ refChild } ` ) ;
190
262
}
191
263
this . viewUtil . insertBefore ( parent , newChild , refChild ) ;
192
264
}
193
265
@inRootZone ( )
266
+ @modifiesDom ( )
194
267
removeChild ( parent : any , oldChild : any , isHostElement ?: boolean ) : void {
195
268
if ( NativeScriptDebug . enabled ) {
196
269
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.removeChild child: ${ oldChild } parent: ${ parent } ` ) ;
@@ -231,6 +304,7 @@ class NativeScriptRenderer implements Renderer2 {
231
304
return node . nextSibling ;
232
305
}
233
306
@inRootZone ( )
307
+ @modifiesDom ( )
234
308
setAttribute ( el : any , name : string , value : string , namespace ?: string ) : void {
235
309
if ( NativeScriptDebug . enabled ) {
236
310
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.setAttribute ${ namespace ? namespace + ':' : '' } ${ el } .${ name } = ${ value } ` ) ;
@@ -243,40 +317,47 @@ class NativeScriptRenderer implements Renderer2 {
243
317
}
244
318
}
245
319
@inRootZone ( )
320
+ @modifiesDom ( )
246
321
addClass ( el : any , name : string ) : void {
247
322
if ( NativeScriptDebug . enabled ) {
248
323
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.addClass ${ name } ` ) ;
249
324
}
250
325
this . viewUtil . addClass ( el , name ) ;
251
326
}
252
327
@inRootZone ( )
328
+ @modifiesDom ( )
253
329
removeClass ( el : any , name : string ) : void {
254
330
if ( NativeScriptDebug . enabled ) {
255
331
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.removeClass ${ name } ` ) ;
256
332
}
257
333
this . viewUtil . removeClass ( el , name ) ;
258
334
}
259
335
@inRootZone ( )
336
+ @modifiesDom ( )
260
337
setStyle ( el : any , style : string , value : any , flags ?: RendererStyleFlags2 ) : void {
261
338
if ( NativeScriptDebug . enabled ) {
262
339
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.setStyle: ${ el } , ${ style } = ${ value } ` ) ;
263
340
}
264
341
this . viewUtil . setStyle ( el , style , value ) ;
265
342
}
266
343
@inRootZone ( )
344
+ @modifiesDom ( )
267
345
removeStyle ( el : any , style : string , flags ?: RendererStyleFlags2 ) : void {
268
346
if ( NativeScriptDebug . enabled ) {
269
347
NativeScriptDebug . rendererLog ( 'NativeScriptRenderer.removeStyle: ${styleName}' ) ;
270
348
}
271
349
this . viewUtil . removeStyle ( el , style ) ;
272
350
}
273
351
@inRootZone ( )
352
+ @modifiesDom ( )
274
353
setProperty ( el : any , name : string , value : any ) : void {
275
354
if ( NativeScriptDebug . enabled ) {
276
355
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.setProperty ${ el } .${ name } = ${ value } ` ) ;
277
356
}
278
357
this . viewUtil . setProperty ( el , name , value ) ;
279
358
}
359
+ @inRootZone ( )
360
+ @modifiesDom ( )
280
361
setValue ( node : any , value : string ) : void {
281
362
if ( NativeScriptDebug . enabled ) {
282
363
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.setValue renderNode: ${ node } , value: ${ value } ` ) ;
@@ -291,17 +372,26 @@ class NativeScriptRenderer implements Renderer2 {
291
372
if ( NativeScriptDebug . enabled ) {
292
373
NativeScriptDebug . rendererLog ( `NativeScriptRenderer.listen: ${ eventName } ` ) ;
293
374
}
294
- target . on ( eventName , callback ) ;
375
+ let modifiedCallback = callback ;
376
+ if ( ( this . preventChangeEvents && eventName . endsWith ( 'Change' ) ) || this . specificPreventedEvents . has ( eventName ) ) {
377
+ modifiedCallback = ( ...args ) => {
378
+ if ( this . _rendererHelper . isExecutingDomChanges ) {
379
+ return ;
380
+ }
381
+ return callback ( ...args ) ;
382
+ } ;
383
+ }
384
+ target . on ( eventName , modifiedCallback ) ;
295
385
if ( eventName === View . loadedEvent && target . isLoaded ) {
296
386
// we must create a new obervable here to ensure that the event goes through whatever zone patches are applied
297
387
const obs = new Observable ( ) ;
298
- obs . once ( eventName , callback ) ;
388
+ obs . once ( eventName , modifiedCallback ) ;
299
389
obs . notify ( {
300
390
eventName,
301
391
object : target ,
302
392
} ) ;
303
393
}
304
- return ( ) => target . off ( eventName , callback ) ;
394
+ return ( ) => target . off ( eventName , modifiedCallback ) ;
305
395
}
306
396
}
307
397
@@ -328,9 +418,10 @@ const addScopedStyleToCss = profile(`"renderer".addScopedStyleToCss`, function a
328
418
export class EmulatedRenderer extends NativeScriptRenderer {
329
419
private contentAttr : string ;
330
420
private hostAttr : string ;
421
+ private rootModuleId = inject ( NATIVESCRIPT_ROOT_MODULE_ID ) ;
331
422
332
- constructor ( component : RendererType2 , rootView : View , namespaceFilters : NamespaceFilter [ ] , private rootModuleId : string | number , reuseViews : boolean ) {
333
- super ( rootView , namespaceFilters , reuseViews ) ;
423
+ constructor ( component : RendererType2 , rootView : View ) {
424
+ super ( rootView ) ;
334
425
335
426
const componentId = component . id . replace ( ATTR_SANITIZER , '_' ) ;
336
427
this . contentAttr = replaceNgAttribute ( CONTENT_ATTR , componentId ) ;
@@ -357,6 +448,8 @@ export class EmulatedRenderer extends NativeScriptRenderer {
357
448
}
358
449
359
450
@profile
451
+ @inRootZone ( )
452
+ @modifiesDom ( )
360
453
private addStyles ( styles : ( string | any [ ] ) [ ] , componentId : string ) {
361
454
styles
362
455
. map ( ( s ) => s . toString ( ) )
0 commit comments