1
- import { ApplicationRef , ChangeDetectorRef , ComponentFactory , ComponentRef , DestroyRef , Injector , OnChanges , QueryList , Type , ViewContainerRef , reflectComponentType } from '@angular/core' ;
1
+ import { ApplicationRef , ChangeDetectorRef , ComponentFactory , ComponentRef , DestroyRef , EventEmitter , Injector , OnChanges , QueryList , Type , ViewContainerRef , reflectComponentType } from '@angular/core' ;
2
2
import { takeUntilDestroyed } from '@angular/core/rxjs-interop' ;
3
- import { NgElement } from '@angular/elements' ;
4
- import { fromEvent } from 'rxjs' ;
5
- import { takeUntil } from 'rxjs/operators' ;
3
+ import { NgElement , NgElementStrategyEvent } from '@angular/elements' ;
4
+ import { fromEvent , Observable } from 'rxjs' ;
5
+ import { map , takeUntil } from 'rxjs/operators' ;
6
6
import { ComponentConfig , ContentQueryMeta } from './component-config' ;
7
7
8
8
import { ComponentNgElementStrategy , ComponentNgElementStrategyFactory , extractProjectableNodes , isFunction } from './ng-element-strategy' ;
@@ -29,6 +29,8 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy {
29
29
/** Cached child instances per query prop. Used for dynamic components's child templates that normally persist in Angular runtime */
30
30
protected cachedChildComponents : Map < string , ComponentRef < any > [ ] > = new Map ( ) ;
31
31
private setComponentRef : ( value : ComponentRef < any > ) => void ;
32
+ /** The maximum depth at which event arguments are processed and angular components wrapped with Proxies, that handle template set */
33
+ private maxEventProxyDepth = 3 ;
32
34
33
35
/**
34
36
* Resolvable component reference.
@@ -405,6 +407,94 @@ class IgxCustomNgElementStrategy extends ComponentNgElementStrategy {
405
407
super . disconnect ( ) ;
406
408
}
407
409
}
410
+
411
+ //#region Handle event args that return reference to components, since they return angular ref and not custom elements.
412
+ /** Sets up listeners for the component's outputs so that the events stream emits the events. */
413
+ protected override initializeOutputs ( componentRef : ComponentRef < any > ) : void {
414
+ const eventEmitters : Observable < NgElementStrategyEvent > [ ] = this . _componentFactory . outputs . map (
415
+ ( { propName, templateName } ) => {
416
+ const emitter : EventEmitter < any > = componentRef . instance [ propName ] ;
417
+ return emitter . pipe ( map ( ( value : any ) => ( { name : templateName , value : this . patchOutputComponents ( propName , value ) } ) ) ) ;
418
+ } ,
419
+ ) ;
420
+
421
+ ( this as any ) . eventEmitters . next ( eventEmitters ) ;
422
+ }
423
+
424
+ protected patchOutputComponents ( eventName : string , eventArgs : any ) {
425
+ // Single out only `columnInit` event for now. If more events pop up will require a config generation.
426
+ if ( eventName !== "columnInit" ) {
427
+ return eventArgs ;
428
+ }
429
+ return this . createProxyForComponentValue ( eventArgs , 1 ) . value ;
430
+ }
431
+
432
+ /**
433
+ * Nested search of event args that contain angular components and replace them with proxies.
434
+ * If event args are array of angular component instances should return array of proxies of each of those instances.
435
+ * If event args are object that has a single property being angular component should return same object except the angular component being a proxy of itself.
436
+ */
437
+ protected createProxyForComponentValue ( value : any , depth : number ) : { value : any , hasProxies : boolean } {
438
+ if ( depth > this . maxEventProxyDepth ) {
439
+ return { value, hasProxies : false } ;
440
+ }
441
+
442
+ let hasProxies = false ;
443
+ // TO DO!: Not very reliable as it is a very internal API and could be subject to change. If something comes up, should be changed.
444
+ if ( value ?. __ngContext__ ) {
445
+ const componentConfig = this . config . find ( ( info : ComponentConfig ) => value . constructor === info . component ) ;
446
+ if ( componentConfig ?. templateProps ) {
447
+ return { value : this . createElementsComponentProxy ( value , componentConfig ) , hasProxies : true } ;
448
+ }
449
+ } else if ( Array . isArray ( value ) ) {
450
+ if ( ! value [ 0 ] ) {
451
+ return { value, hasProxies : false } ;
452
+ } else {
453
+ // For array limit their parsing to first level and check if first item has created proxy inside.
454
+ const firstItem = this . createProxyForComponentValue ( value [ 0 ] , this . maxEventProxyDepth ) ;
455
+ if ( firstItem . hasProxies ) {
456
+ const mappedArray = value . slice ( 1 , value . length ) . map ( item => this . createProxyForComponentValue ( item , depth + 1 ) ) ;
457
+ mappedArray . unshift ( firstItem ) ;
458
+ return { value : mappedArray , hasProxies : true } ;
459
+ }
460
+ }
461
+ } else if ( typeof value === "object" && Object . entries ( value ) . length && ! ( value instanceof Event ) ) {
462
+ for ( const [ key , item ] of Object . entries ( value ) ) {
463
+ if ( ! item ) {
464
+ value [ key ] = item ;
465
+ } else {
466
+ const parsedItem = this . createProxyForComponentValue ( item , depth + 1 ) ;
467
+ value [ key ] = parsedItem . value ;
468
+ hasProxies = parsedItem . hasProxies || hasProxies ;
469
+ }
470
+ }
471
+ }
472
+
473
+ return { value, hasProxies } ;
474
+ }
475
+
476
+ /** Create proxy for a component that handles setting template props, making sure it provides correct TemplateRef and not Lit template */
477
+ protected createElementsComponentProxy ( component : any , config : ComponentConfig ) {
478
+ const parentThis = this ;
479
+ return new Proxy ( component , {
480
+ set ( target : any , prop : string , newValue : any ) {
481
+ // For now handle only template props
482
+ if ( config . templateProps . includes ( prop ) ) {
483
+ const oldRef = target [ prop ] ;
484
+ const oldValue = oldRef && parentThis . templateWrapper . getTemplateFunction ( oldRef ) ;
485
+ if ( oldValue === newValue ) {
486
+ newValue = oldRef ;
487
+ } else {
488
+ newValue = parentThis . templateWrapper . addTemplate ( newValue ) ;
489
+ }
490
+ }
491
+ target [ prop ] = newValue ;
492
+
493
+ return true ;
494
+ }
495
+ } ) ;
496
+ }
497
+ //#endregion
408
498
}
409
499
410
500
/**
0 commit comments