Repository navigation
Description
I'm submitting a ...
[ ] bug report
[x] feature request
[ ] support request
Current behavior
Currently, component lifecycle hooks are handled by implementing one or more interfaces provided by angular. I find myself often wanting to subscribe to an observable until the component/directive is destroyed. To do this, I have to have a member for each subscription for later disposal in the ngOnDestroy() method.
Example:
/* imports */
@Directive({
selector: '[onDrag]'
})
export class OnDrag implements OnInit, OnDestroy {
dragStart: Subscription;
dragEnter: Subscription;
dragOver: Subscription;
dragLeave: Subscription;
constructor(private element: ElementRef) { }
ngOnInit(): any {
let elem = this.element.nativeElement;
this.dragStart = Observable.fromEvent(elem, 'dragstart')
.do(() =>{ /*do stuff*/ })
.subscribe();
this.dragEnter = Observable.fromEvent(elem, 'dragenter')
.do(() =>{ /*do stuff*/ })
.subscribe();
this.dragOver = Observable.fromEvent(elem, 'dragover')
.do(() =>{ /*do stuff*/ })
.subscribe();
this.dragLeave = Observable.fromEvent(elem, 'dragleave')
.do(() =>{ /*do stuff*/ })
.subscribe();
// etc...
}
ngOnDestroy(): any {
this.dragStart.unsubscribe();
this.dragEnter.unsubscribe();
this.dragOver.unsubscribe();
this.dragLeave.unsubscribe();
}
}Desired behavior
My suggestion is to provide components with an observable for each lifecycle event (via injection perhaps?). The above example could turn into something like:
/* imports */
@Directive({
selector: '[onDrag]'
})
export class OnDrag implements OnInit {
constructor(
private element: ElementRef,
private lifecycle: ThingWithLifecycleObservablesForThisComponent
) { }
ngOnInit(): any {
let elem = this.element.nativeElement;
Observable.fromEvent(elem, 'dragstart')
.do(() =>{ /*do stuff*/ })
.takeUntil(this.lifecycle.ngDestroy$)
.subscribe();
Observable.fromEvent(elem, 'dragenter')
.do(() =>{ /*do stuff*/ })
.takeUntil(this.lifecycle.ngDestroy$)
.subscribe();
Observable.fromEvent(elem, 'dragover')
.do(() =>{ /*do stuff*/ })
.takeUntil(this.lifecycle.ngDestroy$)
.subscribe();
Observable.fromEvent(elem, 'dragleave')
.do(() =>{ /*do stuff*/ })
.takeUntil(this.lifecycle.ngDestroy$)
.subscribe();
// etc...
}
}What is the motivation / use case for changing the behavior?
Ultimately, I would like a way to not have to hold onto subscriptions as class members (perhaps there is a better way to achieve this than my suggestion). It could be convenient/concise to be able to handle lifecycle events as observables within components. As a workaround currently I have been extending my components with a base class that provides (some) of these events:
import { OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs/Subject';
export class ComponentLifeCycle implements OnInit, OnDestroy {
protected ngInit$: Subject<any> = new Subject<any>();
protected ngDestroy$: Subject<any> = new Subject<any>();
ngOnInit(): any {
this.ngInit$.next({});
}
ngOnDestroy(): any {
this.ngDestroy$.next({});
}
}Please tell us about your environment:
- Angular version: 2.0.0-rc.4
- Browser: all
- Language: all
Activity
bobvanderlinden commentedon Aug 28, 2017
To get this further along, would it be possible to hook lifecycle events outside of a component? That would make it possible to create a third party library that provides observable events.
sod commentedon Sep 25, 2017
So there mere presence of a method
ngOnDestroylets angular call it upon component destruction.How about the mere presence of a:
and angular does
componentInstance.ngDestroy.next();upon destruction?bobvanderlinden commentedon Sep 25, 2017
Just to share my findings: I have tried to set
ngOnDestroyinside the constructor, but this will not work. It seems to check whether the method is created using reflection metadata.Jeff-Cortese commentedon Sep 25, 2017
@ohjames I hadn't considered AoT when I first wrote this issue. A year later, we are using AoT'ed builds in production at my work and have never hit the issue you're describing. I tested out that scenario by logging out emitted
ngDestroy$values without callingsuper.ngOnDestroy(), and it appears to be working, meaning the base class'sngOnDestroy()was not compiled out.bygrace1986 commentedon Oct 6, 2017
@sod this may be similar to what you wanted.
I put together an alternate approach using property decorators if anyone prefers that flavor over inheritance. I haven't tested it with AOT yet. If the angular team wrote it then they could get tighter integration without needing to hijack the life-cycle callback methods on the component.
https://stackoverflow.com/questions/40737752/anuglar2-lifecycle-events-as-rxjs-observable/46572739#46572739
life-cycle.decorator.ts
Example Usage
I implement OnInit here to demonstrate that they work together but you could subscribe to onInit instead
insidewhy commentedon Oct 9, 2017
@bygrace1986 Yeah that definitely won't work with AoT. The AoT compiler needs to be able to statically infer the existence of lifecycle methods in the component, it can't infer these from code run as part of a decorator.
In general with Angular 2/4, a million things are trivial to do with decorators, but barely any of these will work once you factor in AoT.
bygrace1986 commentedon Oct 9, 2017
@ohjames your right. I'd rather use the decorators to insert the hooks but I couldn't find any hacky way to make it work so I reverted to using a base class.
sandangel commentedon Oct 9, 2017
any chance this proposal will be implemented?
insidewhy commentedon Oct 11, 2017
Without this proposal there is no way to include lifecycle events as part of rxjs pipelines or to hook into lifecycle events without abusing inheritance.
At my consultancy we're using Angular 2/4 with rxjs heavily. This issue together with the lack of observables to monitor
@Inputmakes us question if VueJS or cycle.js would be a better fit for us given our love of rxjs.There's this uncomfortable middle ground with Angular 4 where most things are trivial to do with observables and yet other things that should be trivial are obstructed by brick walls.
mlc-mlapis commentedon Oct 11, 2017
@ohjames ... for monitoring
@Input()you can usesetterbecause it is invoked also when there is a change.insidewhy commentedon Oct 11, 2017
@mlc-mlapis Yep, that's precisely the approach I'm using in my angular utility library https://github.com/ohjames/observable-input. Having to define a setter + Observable member for every
@Input()is a bit too much boilerplate for me.30 remaining items
fabioemoutinho commentedon Aug 29, 2022
Personally I would love to see something like:
I don't know if this is achievable, but in this way you could have this be implemented only in
@ReactiveComponentand it would not mess with current@ComponentAPI.ngOnDestroy$ and other lifecycle observables would be implemented by the
@ReactiveComponent. And maybe with the@ReactiveInput, we could finally have fully reactive components 🙌tayambamwanza commentedon Oct 10, 2022
I really hope this gets implemented along with observable inputs, ever since I realised this could be done I realised how much it would make the reactive authoring experience in Angular so much more DX friendly. Using the Decorator lifecycle hooks and even the inputs actually forces you out of getting the most from the experience, which is funny because I was introduced to reactivity by Angular and after understanding it better I realised Angulars implementation is missing a few key features to take it from ok to amazing.
I hope along with any other primitives they do lean into the strength of RXJS that many have come to love, I think the fact that many frameworks are developing reactive primitives which mimic rxjs in a more lightweight manner shows once again that Angular was actually ahead of it's time like with typescript.
niklaas commentedon Oct 10, 2022
What do you mean by "observable inputs"? You can already pass in observables to a component using an input binding. Do you also want the subscription to be managed by the component automatically?
tayambamwanza commentedon Oct 10, 2022
@niklaas Basically what I want is to be able to subscribe to the input rather than using ngOnChanges, then I could apply operators to the input directly. So the input itself would be of type observable and emit the values passed to it through that. Forgive me if my terminology is wrong but that should be enough to give you an idea.
niklaas commentedon Oct 31, 2022
That is, you don't mean passing in an observable explicitly, right? Because if you do, this is actually possible.
tayambamwanza commentedon Oct 31, 2022
Not my work btw it's from @tomastrajan
csisy commentedon Jan 11, 2023
There was a PR mentioned in this issue #20682 (back from 2017) which provided a good solution - tho I understand why it wasn't merged. But since then, 5+ years passed. Is there any plan to introduce something similar? This issue is open for so long and we really need this feature, especially with the new
injectfeature introduced with v14.JeanMeche commentedon Mar 16, 2023
This PR will land a
takeUntilDetroyedoperator ! (Target is V16)gavinsawyer commentedon Aug 5, 2023
This would be useful for accessing child components' lifecycles as well. Currently, I can't find a use for ViewChild as it's easier to avoid implementing component lifecycle hooks by just nexting a subject:
Current approach
Defines a reactive value (map: Subject<google.maps.Map>) with no rules and uses the template to set its value:
Proposed approach
Eliminates the use of subjects, integrating directly into the child component's lifecycle:
I left out declarations for
mapOptionsandmapGeolocationEngagedfor brevity, which are reactive values that depend upon the output frommapInitialized. I'm using event listeners on the native Google Maps component to determine whether to keep updatingmapOptionswith the user's live location. If they swipe around or zoom, it pauses recentering the map from the geolocation stream.alxhub commentedon Oct 10, 2024
👋 hey all! We recently revisited this issue in team discussions and decided that we're satisfied with the APIs we have in place today, and we don't plan to implement anything further for component lifecycle hooks. To elaborate:
ngOnInit- we believe that signalinput()s +effect()reduce the need to even use thengOnInitlifecycle hook. UsingtoObservableover signal inputs, an RxJS chain doesn't need any kind of trigger fromngOnInitto express processing of inputs.ngOnChanges- not needed with signal inputsngDoCheck- not needed with signalsngOnDestroy-takeUntilDestroyaddresses the main use case (unsubscribing)ngAfterView*&ngAfterContent*- also not necessary with signal queries & theafterNextRenderAPI, which provide clearer (and often more performant) ways of taking action following Angular's renderingWe will of course consider proposals for new APIs on a case-by-case basis, but I'm going to close this broader issue.
angular-automatic-lock-bot commentedon Nov 10, 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.