Skip to content

Commit ed3d8d5

Browse files
committed
fix(lib): simulate late initialization of events stream on element strategies
1 parent e67bd28 commit ed3d8d5

File tree

4 files changed

+48
-22
lines changed

4 files changed

+48
-22
lines changed

projects/ngx-element-boundary/element-strategy/default/src/element-boundary-ng-element-strategy.ts

+6-13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
import { ComponentRef, Injector, Type } from '@angular/core';
2-
import {
3-
NgElementStrategy,
4-
NgElementStrategyEvent,
5-
NgElementStrategyFactory,
6-
} from '@angular/elements';
2+
import { NgElementStrategy, NgElementStrategyFactory } from '@angular/elements';
73
import {
84
ElementBoundaryNgElementStrategy,
95
ElementBoundaryNgElementStrategyFactory,
6+
maybeLateInitStream,
107
} from 'ngx-element-boundary';
11-
import { Observable } from 'rxjs';
128

139
import {
1410
DefaultNgElementStrategyFactory,
@@ -64,13 +60,10 @@ export class DefaultElementBoundaryNgElementStrategyOptionsDefault
6460
*/
6561
export class DefaultElementBoundaryNgElementStrategy
6662
implements ElementBoundaryNgElementStrategy {
67-
get events() {
68-
return this.defaultStrategy.events;
69-
}
70-
71-
set events(events: Observable<NgElementStrategyEvent>) {
72-
this.defaultStrategy.events = events;
73-
}
63+
// HACK: In Angular Elements before v10 `events` property was not set
64+
// before `this.connect()` was not called resulting in `undefined`
65+
// so we are using late initialization of stream
66+
events = maybeLateInitStream(this.defaultStrategy, 'events');
7467

7568
private options: DefaultElementBoundaryNgElementStrategyOptionsDefault;
7669

projects/ngx-element-boundary/src/lib/cross-boundary-ng-element-strategy.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ import {
44
NgElementStrategyEvent,
55
NgElementStrategyFactory,
66
} from '@angular/elements';
7-
import { Observable, of, Subject } from 'rxjs';
8-
import { take, takeUntil, timeoutWith } from 'rxjs/operators';
7+
import { Observable, of, Subject, ReplaySubject } from 'rxjs';
8+
import { take, takeUntil, timeoutWith, switchAll } from 'rxjs/operators';
99

1010
import {
1111
ElementBoundaryNgElementStrategy,
1212
ElementBoundaryNgElementStrategyFactory,
1313
} from './element-boundary-ng-element-strategy';
1414
import { ElementBoundaryService } from './element-boundary.service';
1515
import { HookableInjector } from './hookable-injector';
16+
import { maybeLateInitStream } from './util';
1617

1718
/**
1819
* Options for {@link CrossBoundaryNgElementStrategy}
@@ -67,13 +68,10 @@ export class CrossBoundaryNgElementStrategyOptionsDefault
6768
* To disable the timeout you may set it to `0` (zero, number)
6869
*/
6970
export class CrossBoundaryNgElementStrategy implements NgElementStrategy {
70-
get events() {
71-
return this.baseStrategy.events;
72-
}
73-
74-
set events(events: Observable<NgElementStrategyEvent>) {
75-
this.baseStrategy.events = events;
76-
}
71+
// HACK: In Angular Elements before v10 `events` property was not set
72+
// before `this.connect()` was not called resulting in `undefined`
73+
// so we are using late initialization of stream
74+
events = maybeLateInitStream(this.baseStrategy, 'events');
7775

7876
private elementBoundaryService: ElementBoundaryService = this.hookableInjector.get(
7977
ElementBoundaryService,
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,36 @@
1+
import { ReplaySubject, Observable, ObservedValueOf } from 'rxjs';
2+
import { switchAll } from 'rxjs/operators';
3+
14
/** @internal */
25
export function isDefined<T>(value: T): value is NonNullable<T> {
36
return value !== null && value !== undefined;
47
}
8+
9+
/**
10+
* Allows to defer initialization of the stream
11+
* by creating initial stream before hand and intercept the value
12+
* once it's available in setter and switch to it from initial stream
13+
*
14+
* @internal
15+
*/
16+
export function maybeLateInitStream<
17+
T,
18+
P extends keyof T,
19+
R = ObservedValueOf<T[P]>
20+
>(object: T, prop: P): Observable<R> {
21+
if (object[prop]) {
22+
return object[prop] as any;
23+
}
24+
25+
const setValue$ = new ReplaySubject<Observable<R>>(1);
26+
const value$ = setValue$.pipe(switchAll());
27+
28+
Object.defineProperty(object, prop, {
29+
configurable: true,
30+
enumerable: true,
31+
get: () => value$,
32+
set: (value) => setValue$.next(value),
33+
});
34+
35+
return value$;
36+
}

projects/ngx-element-boundary/src/public-api.ts

+3
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ export * from './lib/component-selector-strategy/regex-component-selector-strate
1818
export * from './lib/boundary-sharing-strategy/boundary-sharing-strategy';
1919
export * from './lib/boundary-sharing-strategy/single-app-boundary-sharing-strategy';
2020
export * from './lib/boundary-sharing-strategy/global-boundary-sharing-strategy';
21+
22+
// Internal shared utils
23+
export * from './lib/util';

0 commit comments

Comments
 (0)