From fe5c4e086add655bf53315d71b0736ff758c7199 Mon Sep 17 00:00:00 2001 From: Alex Rickabaugh Date: Mon, 26 Aug 2024 12:54:52 -0700 Subject: [PATCH] fix(elements): support `output()`-shaped outputs (#57535) Previously Elements was assuming that every output was an RxJS `Subject` and supports `.pipe()`. This is not true for `output()`-based outputs which have `.subscribe()` but not `.pipe()`. This commit fixes such outputs by using a `new Observable` instead of `map` to forward outputs. PR Close #57535 --- .../elements/src/component-factory-strategy.ts | 10 +++++++--- .../test/component-factory-strategy_spec.ts | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/elements/src/component-factory-strategy.ts b/packages/elements/src/component-factory-strategy.ts index cc86f51314b..2f571daa5ac 100644 --- a/packages/elements/src/component-factory-strategy.ts +++ b/packages/elements/src/component-factory-strategy.ts @@ -18,9 +18,10 @@ import { ɵChangeDetectionScheduler as ChangeDetectionScheduler, ɵNotificationSource as NotificationSource, ɵViewRef as ViewRef, + OutputRef, } from '@angular/core'; import {merge, Observable, ReplaySubject} from 'rxjs'; -import {map, switchMap} from 'rxjs/operators'; +import {switchMap} from 'rxjs/operators'; import { NgElementStrategy, @@ -219,8 +220,11 @@ export class ComponentNgElementStrategy implements NgElementStrategy { protected initializeOutputs(componentRef: ComponentRef): void { const eventEmitters: Observable[] = this.componentFactory.outputs.map( ({propName, templateName}) => { - const emitter: EventEmitter = componentRef.instance[propName]; - return emitter.pipe(map((value) => ({name: templateName, value}))); + const emitter: EventEmitter | OutputRef = componentRef.instance[propName]; + return new Observable((observer) => { + const sub = emitter.subscribe((value) => observer.next({name: templateName, value})); + return () => sub.unsubscribe(); + }); }, ); diff --git a/packages/elements/test/component-factory-strategy_spec.ts b/packages/elements/test/component-factory-strategy_spec.ts index 4e53f7e3e38..5fb8f7aef70 100644 --- a/packages/elements/test/component-factory-strategy_spec.ts +++ b/packages/elements/test/component-factory-strategy_spec.ts @@ -16,6 +16,7 @@ import { Input, NgZone, Output, + OutputEmitterRef, SimpleChange, SimpleChanges, createComponent, @@ -113,6 +114,18 @@ describe('ComponentFactoryNgElementStrategy', () => { ]); }); + it('should listen to output() emitters', () => { + const events: NgElementStrategyEvent[] = []; + strategy.events.subscribe((e) => events.push(e)); + + componentRef.instance.output3.emit('output-a'); + componentRef.instance.output3.emit('output-b'); + expect(events).toEqual([ + {name: 'templateOutput3', value: 'output-a'}, + {name: 'templateOutput3', value: 'output-b'}, + ]); + }); + it('should initialize the component with initial values', () => { expect(strategy.getInputValue('fooFoo')).toBe('fooFoo-1'); expect(componentRef.instance.fooFoo).toBe('fooFoo-1'); @@ -369,6 +382,7 @@ export class CdTrackerDir { export class TestComponent { @Output('templateOutput1') output1 = new Subject(); @Output('templateOutput2') output2 = new Subject(); + @Output('templateOutput3') output3 = new OutputEmitterRef(); @Input() fooFoo: unknown; @Input({alias: 'my-bar-bar'}) barBar: unknown;