Closed NetanelBasal closed 2 years ago
Hey, I’ll reply a bit later, I’m onto phone for next the week.
Sure, take your time.
Considering the above example, how the operator will be used for non-component classes? Services, NgModules, pipes, etc (since they all may implement the OnDestroy interface)?
It should work the same.
@Injectable()
export class BarService {
destroy$ = untilDestroyed();
init() {
interval(1000).pipe(
this.destroy$
).subscribe(console.log)
}
}
@Component({
selector: 'app-foo',
templateUrl: './foo.component.html',
providers: [
BarService
],
})
There're some cases I've noticed where it doesn't work compared to the existing behavior:
@NgModule()
export class SomeModule {
destroy$ = untilDestroyed(); // No provider for ChangeDetectorRef!
}
@Injectable({ providedIn: 'root' })
export class RootService {
destroy$ = untilDestroyed(); // No provider for ChangeDetectorRef!
}
Embedded views:
@Pipe({ name: 'impure', pure: false })
export class ImpurePipe implements PipeTransform {
destroy$ = untilDestroyed();
constructor() {
new Subject()
.pipe(
this.destroy$,
finalize(() => console.log('Finalized')) // Not called
)
.subscribe();
}
transform(value: string) {
return 'Hey';
}
ngOnDestroy(): void {
console.log('Called when `shown` becomes `false`.');
}
}
@Directive({ selector: '[myDirective]' })
export class MyDirective {
destroy$ = untilDestroyed();
constructor() {
new Subject()
.pipe(
this.destroy$,
finalize(() => console.log('Finalized')) // Not called
)
.subscribe();
}
ngOnDestroy(): void {
console.log('Called when `shown` becomes `false`.');
}
}
@Component({
selector: 'app-root',
template: `
<button (click)="shown = !shown">Toggle</button>
<ng-template [ngIf]="shown">
<div myDirective></div>
{{ "" | impure }}
</ng-template>
`
})
export class AppComponent {
shown = true;
}
NgModule
- I ignored it on purpose because it's a rare use case, IMO.providedIn: root
- That's make sense. It only works when used with component/directive providers.Embedded views
- That's a nice catch. Actually, it makes sense because we are injecting ChangeDetectorRef.
Hmm what about doing something like this (quick pseudo code):
const symbol = Symbol('untilDestroyed');
const patched = Symbol('patched');
export function untilDestroyed(instance: any) {
const proto = Object.getPrototypeOf(instance);
if (!proto[patched]) {
proto[patched] = true;
const original = proto.ngOnDestroy;
proto.ngOnDestroy = function () {
original?.apply(this, arguments);
this[symbol].next();
this[symbol].complete();
}
}
instance[symbol] = new Subject<void>();
return takeUntil(instance[symbol].asObservable())
}
This code works, but I don't see any benefit over our current approach. I'm closing the issue for now.
Since Angular 14 was released, as I mentioned in my previous blog post, we can now create something like:
Should we remove the decorator approach or provide both solutions? @arturovt