Repository navigation
Closed
Labels
area: coreIssues related to the framework runtimeIssues related to the framework runtimecore: lifecycle hookscross-cutting: observablesfeatureLabel used to distinguish feature request from other issuesLabel used to distinguish feature request from other issuesfeature: under considerationFeature request for which voting has completed and the request is now under considerationFeature request for which voting has completed and the request is now under consideration
Milestone
Description
I'm submitting a...
[ ] Regression (a behavior that used to work and stopped working in a new release)
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Current behavior
For every observable I consume in the components I need to manually unsubscribe from it on component destruction.
What does it mean:
- For every subscription I need to create a property on a component (or an array of those subscriptions)
- In
ngOnDestroyI need to unsubscribe from all subscriptions above
Normally this is a little bit painful. That's why I have a helper library that creates a pipeable operator that tracks the component destruction and kinda extends standard takeUntil pipeable:
export function untilDestroyed<T>(componentInstance: any, destructorName = 'ngOnDestroy'): MonoTypeOperatorFunction<T> {
if (!componentInstance[metaProperty]) {
const originalDestructor = componentInstance[destructorName];
if (!originalDestructor && aot) {
console.warn('untilDestroyed limitation: ngOnDestroy is not present on the component which is a problem for AoT. See https://github.com/angular/angular/issues/16023 for more details');
}
componentInstance[metaProperty] = new Subject();
componentInstance[destructorName] = function() {
if (originalDestructor) {
originalDestructor.call(componentInstance);
}
componentInstance[metaProperty].next();
componentInstance[metaProperty].complete();
};
}
return takeUntil(componentInstance[metaProperty]);
}Everything is cool, it is really comfortable to use as
interval(1000).pipe(untilDestroyed(this)).subscribe(console.log.bind(console));where this is a component instance.
But here comes AoT and forces to create an empty ngOnDestroy according to this issue #16023.
Expected behavior
- As long as now RxJS is very flexible with those pipeables, can we have a similar standard pipeable shipped by angular?
- Can we embed this pipeable under the hood to bind to the component destructor that one is not forced to write
ngOnDestroy?
Minimal reproduction of the problem with instructions
What is the motivation / use case for changing the behavior?
I need to manually unsubscribe from every observable in ngOnDestroy. Even using the custom pipeable I still need to create an empty ngOnDestroy. This could be done in a way better manner
Environment
Angular version: >=5.0.0
Browser:
- [ ] Chrome (desktop) version XX
- [ ] Chrome (Android) version XX
- [ ] Chrome (iOS) version XX
- [ ] Firefox version XX
- [ ] Safari (desktop) version XX
- [ ] Safari (iOS) version XX
- [ ] IE version XX
- [ ] Edge version XX
For Tooling issues:
- Node version: XX
- Platform:
Others:
Reacted by Bagaev Dmitry, Lazar Ljubenović, Simon Bobrov, Jes Wulfsberg Nielsen, Nikita Mikhailov, Rodrigo Alves, Étienne Corbillé, Brachi Packter, Alexander Tømmerås, Paul Crockett and 23 more
Metadata
Metadata
Assignees
Labels
area: coreIssues related to the framework runtimeIssues related to the framework runtimecore: lifecycle hookscross-cutting: observablesfeatureLabel used to distinguish feature request from other issuesLabel used to distinguish feature request from other issuesfeature: under considerationFeature request for which voting has completed and the request is now under considerationFeature request for which voting has completed and the request is now under consideration
Activity
bcandullo commentedon Mar 28, 2018
I dig that helper you've created. Have you tried creating a base component to handle this which would automatically invoke the
ngOnDestroyfor sub components? Pseudo example:smnbbrv commentedon Mar 28, 2018
hi @bcandullo
this definitely works, however it has some disadvantages.
super()call (so, thengOnDestroywith empty body already pays off if you count the lines of code)this.subscribe(x)pushes the attention away from the meaningful part of the code (the actualx). This is especially visible when there is an observable with multiple pipeables which most likely takes more than one linengOnDestroyfor something else (e.g. cleaning up something not observable-like) it is quite easy to forget to callsuper.ngOnDestroy()inMyComponentwhich automatically introduces a memory leakextendsmight be a bad architecture because a class can extend only once, in other words this codeclass MyComponent extends DestroyableComponent, SomethingElseis invalid and one needs to really pick what to extend. Example: let's say I have a library that provides me some class I am forced to extend. That means here I need to choose between the library andDestroyableComponent=> it is clear that I will choose the library (because normally the library does the heavy stuff andDestroyableComponentis just a syntax sugar), which means in the end of the day that I would need to manually unsubscribe from everything in those kind of components.The proposed pipeable solves all the issues above. It just does not have any of them.
bcandullo commentedon Mar 28, 2018
I agree, especially about #3. It seems the operator is elegant and most effective when presented with the alternatives, I’d love to see it implemented.
wulfsberg commentedon Mar 28, 2018
I was half on my way to propose a standard Observable which fires when the component is destroyed, so we could .takeUntil that, but creating it as a pipe would be more elegant. Now, if we could then also get
@Inputas observables using this pipe... :-)We definitely need a more elegant way to handle Observables on destroy, and this sounds like a good idea for it.
ericmartinezr commentedon Mar 29, 2018
Have you seen this PR #20682 (See the issue referenced for more discussion on this matter)? Is it what you're asking for?
dawidgarus commentedon Mar 29, 2018
I'm not sure how new tree-shakeable providers will work, because documentation is lacking for now, but assuming that it would be possible make them provided at component level where they are injected, this could be easily done with something like this:
I think it's as easy as it gets, no hacks required and
TakeUntilDestroyedcan be implement without any changes in angular internals, something like:smnbbrv commentedon Mar 29, 2018
@ericmartinezr yes, it is a good solution. I would be happy if this pull request gets merged / released. Both solutions could co-exist together, still
cedvdb commentedon Mar 30, 2018
I personally use this
then in my component extending the class
takeUntil(this._destroy$)Ultimately I think we should have a
@Observablethat'd do everything it the background.amcdnl commentedon Apr 16, 2018
This would be awesome to have implemented but I think in a more generic way the ability to listen in on lifecycle events in things like decorators would be a even better improvement. For example:
Similar syntax to what the router uses with
eventsand filtering techniques this would allow developers to listen for events in the component lifecycle and thus making decorators like mentioned here possible. A nice to have would be the ability to get theinjectoroff the target without having to inject it.I realize this suggestion would be modifying the target properties in the component decorator (could be considered bad) and typescript might not be able to infer it correctly. Its just a suggestion to get us thinking about how we might accomplish this. On the flip side, you could just black list compilation of methods like
ngOnDestroyout from AoT.Reference material on this subject:
cc/ @robwormald
NetanelBasal commentedon Apr 16, 2018
+1
1 remaining item
MickL commentedon Apr 1, 2019
I would rather like to see this happen instead, which has 85 upvotes: #10185 then simply use
takeUntil(this.destroy$)instead of writing a new rxjs operator.Until then, instead of using
.unsubscribe()it is easier to use the operatortakeUntil():frankiDotNet commentedon Mar 6, 2020
Back in 2020, is there still no way to auto unsubscribe if a component is destroyed?
Bjeaurn commentedon Mar 6, 2020
Yes there is, if you use
AsyncPipe. It's been there "since 2018." It also does not help this issue's discussion at allsmnbbrv commentedon Mar 6, 2020
How does async pipe help the discussion?
frankiDotNet commentedon Mar 6, 2020
I was also thinking what AsyncPipe has to do with this?
Bjeaurn commentedon Mar 6, 2020
AsyncPipe does this for you. But unfortunately, still no lifecycle hooks provided by the framework we can hook into no.
fabioemoutinho commentedon Sep 19, 2022
@pkozlowski-opensource is it possible we introduce something like a
@AutoUnsubscribe() myObs$ = ...that includes the observable into the logic view, and then when ngOnDestroy is called, it looks at the subscriptions added in logic view and unsubscribes them? I was thinking of something similar that was done for@Output()JeanMeche commentedon Mar 16, 2023
This PR will land a
takeUntilDestroyedoperator !JeanMeche commentedon Feb 19, 2024
The
takeUntilDestroyedoperator is available since v16.angular-automatic-lock-bot commentedon Mar 21, 2024
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.