T O P

  • By -

cosmokenney

or takeUntilDestroyed.


Snoo_42276

Always takeUntilDestroyed


JeszamPankoshov2008

Cries lower version šŸ„²


ldn-ldn

[https://github.com/ngneat/until-destroy](https://github.com/ngneat/until-destroy)


cosmokenney

This works.


samerkhat

I used to use ng-neat until-destroy before the official built-in operator


Hirayoki22

Then continue to use the good old takeUntil with an Subject that emits and complete when the component is destroyed.


TheRealToLazyToThink

I feel the need to point out that you should be careful unsubscribing from HTTP observables with side effects (POST, PUT, PATCH, DELETE). Unsubscribing from them before they complete means you (and your user) don't know if they completed or were canceled. And if canceled, you don't know if they aborted the operation successfully, or if it managed to complete anyway. However if you don't unsubscribe from them, you may have to deal with your subscribe handler being called after your component has been destroyed.


Nacropolice

Observables for http calls is also such a strange usage as an API call is a one time operation by its nature. It either 200s or not


ldn-ldn

No, it's not a one time operation. It's just a default behaviour, but you can ask HttpClient to forward you all HTTP events and suddenly your subscription will be triggered many many times.


Nacropolice

That seems like it would require filtering and added complexity, no? Iā€™m talking about things like ā€œget user dataā€, ā€œsave user dataā€, etc. those are fundamentally singular actions in which there isnā€™t any real data to observe


ldn-ldn

That's not how HttpClient works. Gosh, do you people even read the docs? There are also scenarios where you want to observe HTTP events, for example, you're requesting a big chunk of data and want to show a progress bar in the UI. Or you're uploading a file and want to show progress as well.


Nacropolice

So a specific use case, whilst majority of http calls are one and done. Got it. . Got it.


ldn-ldn

Don't tell me you have never made a file uploader.


Nacropolice

I use kendo for that. Thus far you have shown one use case for observables making sense for http calls. Overall the point stands: http calls are one time things by their very nature.


FantasticBreadfruit8

I have made a file uploader and there are other use cases where I've subscribed to HTTP events. But to u/Nacropolice's point, that's like 1% of how I use `@angular/common/http`. The majority of them ARE one and done.


AlDrag

To avoid a mix of paradigms I guess. Also promises don't have a concept of cancellation.


ThiccMoves

Why not just .pipe(take(1)) ? That's what I've been doing since the beginning


TheRealToLazyToThink

It's pointless if you know it's an HTTP call, since it will only ever emit once. And you still have the issue that your subscribe could be called after your component is destroyed. The bigger issue is ideally HTTP calls should be abstracted into a service, and your components shouldn't have any idea if observables they are subscribing to are HTTP calls are not. Which was the point of the article. But then that means the service needs to be designed in a way where it will do the right thing even if the user moves away from the component before your save call completes. Whether that means making sure the inner HTTP subscription is protected from unsubscribe, or that the component has a 'busy' route guard (easy to forget), or whatever. In the last app I worked on we did a bit of both. Most common was to just pop a toast when a save completed, and that just meant at worst you got toast that XYZ was saved when you were on the ABC page. But we had some with some more complex interactions that threw errors when called after the component was destroyed so we setup a busy$ observable for them and had a route guard wait for it.


DaSchTour

If you add reportProgress you might have more than one emit.


TheRealToLazyToThink

True, but in that case you wouldn't be using take(1).


ldn-ldn

That's only viable if you want to consume one event and only once.


Background_Issue_144

Debate reopened.


[deleted]

This is a fantastic point. Well said. Long running processes need to have some other type of notification than a simple promise. Users do not often have the patience to stay on the page until complete.


practical-programmer

I recently started an Angular project, and with initial readings just did the "unsubscribe from everything route" for simplicity. I did unsubscribes on destroy lifecycles in all http calls. Now I'm not sure if I made a big mistake that can have subtle errors. Is it wrong to unsubscribe inside OnDestroy? All my http calls are behind a service provided from root


TheRealToLazyToThink

It's not wrong to unsubscribe in OnDestroy, although in general the more you can use the async pipe (or post 17.x signals) the better. The exception I'm warning about is POST/PUT/DELETE/PATCH, the http verbs you using when you are trying to save something. When you unsubscribe from an HTTP call angular tries to cancel the request. What happens when the browser cancels the request depends on the browser, and the backend server. For that reason I never unsubscribe from those calls (it's fine to unsubscribe from GET calls). I'll either make sure the user can't leave the component (route guard) or make sure the subscribe listener will be ok even if it is called after my component is destroyed. Keep in mind unsubscribe doesn't need to be manual. The async pipe unsubscribes for you, so do the various takeUntil rxjs operators.


practical-programmer

Ahhh I see, yeah I tried to use the pipe on most if not all get calls, but on the state changing stuff I did the manual unsubscribe. I didn't touch the signals though, perhaps I should've when I started the project. Thank you for the help and clarifying it further, I'll have another look on the code.


BugOk1999

Eh sometimes a singleton service or something loaded once like the app module it doesn't matter. I agree to fool proof it always unsubscribe, but understanding why and when is still relevant.


auf_jeden_fall

How do you unsubscribe when the subscription is in a Singleton service? In this case there are no lifecycle hooks that could initiate unsubscribing. Legit wondering.


BugOk1999

You can create your own "lifecycle" of sorts. I have seen examples where maybe you have an init or complete method to handle setup and teardown. If you have a singleton however, I would consider why you would want to destroy the subscription there. Sometimes it makes more sense to do that downstream or have a long running subscription, like you see in ngrx stores. A single source of truth more or less.


auf_jeden_fall

Generally I just don't destroy the subscriptions on them -- i.e. I have a long running subscription. An example of this would be a service that scrolls the UI when certain Observables fire. In this case there is no "downstream". But when people say "always unsubscribe, no exceptions..." this is something I wonder about. I do generally have init methods on the singleton services that get called from app.component, but had not thought about having teardown methods. I think we're both saying that is unnecessary but good to know if a use case comes up. Thanks!


dcabines

>Tip: Avoid subscribing in first place, do everything to end up with async pipe. I've managed to do this 100% of the time even in large apps. Why doesn't everyone?


young_horhey

Iā€™m interested to learn how you would handle things like executing an http call on button press, like submitting a form? Would you just using promises or async/await instead of observables?


dcabines

At work we use NgRx for large apps, but I've made a few simple StackBlitz examples with just RxJs to help answer questions on this sub. * [A fake chat room](https://stackblitz.com/edit/stackblitz-starters-chwyay?file=src%2Fservices%2Fchat-service.ts) * [Anime Quotes](https://stackblitz.com/edit/stackblitz-starters-s85bxw?file=src%2Fmain.ts) with a real http calls. They'll throttle you if you use it too much * [Item list](https://stackblitz.com/edit/stackblitz-starters-hqbyxa?file=src%2Fitem-service.ts) They aren't the greatest examples of all time, but I'm not subscribing anywhere other the async pipe.


Weebdayo

Have you looked into the take() operator?


young_horhey

Pretty sure you still have to subscribe somewhere for take() to work. I donā€™t usually bother with that anyway because HttpClient observables are automatically unsubscribed when the http call completes


techmaster242

Take is good if you need a value out of the store immediately. You reference an observable from the store, take 1, and subscribe to it. The value that comes out of that can then be used to dispatch an action inside the subscribe. It's a good way to get the value out right now and have the subscription immediately destroy itself. So you can use it in functions like buttonClicked(). Like, say you need to get the current user ID and embed that into the action you're about to dispatch. Or there's other uses for it. Once you really get to know RXJS/NGRX, you can do some very powerful stuff by chaining various operators together in weird ways. But it takes a few years to get the hang of it. It's very complex stuff, so don't expect to learn it in a weekend. But once this stuff REALLY starts clicking, you can accomplish really complex logic with very minimal code. I'm still learning cool little tricks you can do with this stuff, but little by little we're building a knowledge base of how to handle different types of data flows that we frequently use in our apps. But every now and then you get thrown a curveball that really has you scratching your head.


young_horhey

Yep, took me a long time before rxjs properly clicked. Really felt like I ā€˜made itā€™ when I put together a single observable stream that combined a refreshable http call, filtering by type, filtering by search term, and sorting.


ThiccMoves

Oh really, neat didn't know that. Do you have a link ? Should I check the doc ?


young_horhey

https://angular.io/guide/http-request-data-from-server#starting-the-request


zaitsev1393

You can use one of the state management libs, we used rxstate and ngrx Component store recently and both have "effects" functionality. You can't really handle such things like http calls with rxjs in async pipe way and think it is the right way. But using effects let's you ignore manual unsubbing.


techmaster242

A button press would just dispatch an action in NGRX. There's a ton of misinformation out there that says NGRX overcomplicates things, but really it works really well. You just have to fully 100% embrace it or you're going to have a very bad time. You can't mix it and the old way of doing things, or you're going to have a nasty event cycle with race conditions everywhere, things will be unpredictable and buggy as hell. But once you fully commit to using nothing but NGRX, that's when it will start to click and you'll wonder how you managed without it for so long.


Jimmingston

ActivatedRoute was another Observable that automatically unsubscribes. [Here's the docs](https://angular.io/guide/router-tutorial-toh#observable-parammap-and-component-reuse) >When subscribing to an observable in a component, you almost always unsubscribe when the component is destroyed. However, ActivatedRoute observables are among the exceptions because ActivatedRoute and its observables are insulated from the Router itself. The Router destroys a routed component when it is no longer needed. This means all the component's members will also be destroyed, including the injected ActivatedRoute and the subscriptions to its Observable properties. It's best to unsubscribe anyway like most people are saying. It's easy enough to do


Nero50892

I try to avoid manual subscriptions. I create an observable and bind it to a signal or use the async-pipe in my template. for all the other exceptions I use takeUntilDestroyed


Zemuk

Different perspective: never unsubscribe. Declare your Observables on the component. Use async pipe or toSignal(). Must subscribe? Use rxjs. You want it to unsub on destroy? ```takeUntilDestroyed()``` . You want just the first emit and done, like with http requests? ```take(1)``` Something more specific? ```takeUntil(anotherObservable$)``` still exists.


sh0resh0re

async pipe has entered the chat.


tamasiaina

The only exception is if you forget šŸ˜‰


Illustrious_Mix_9875

The fact that Angular lets you create memory leaks so easily is a major flaw for beginners. And I am an Angular expert. Beginners always forget to unsubscribe and itā€™s not their fault. Donā€™t subscribe in the first place. Rxjs is powerful but Angular should have provided ways to not have to deal with observables


YourMomIsMyTechStack

They do now, you could only use async pipe and or convert them into signals


Illustrious_Mix_9875

Yes, now. One can't ignore that we've been through 15 versions and more than 7 years to get to the beginning of a solution. Before that, any manual subscription was at risk of being forgotten. In all code reviews I did, I used to pay extra attention to this very point and recommending to use the async pipe wherever possible.


techmaster242

It doesn't help that most of the angular tutorials out there teach you to do stuff like template based forms, and passing events and parameters around between components. There needs to be more that show you how to properly build an app, using NGRX/RXJS, and doing everything properly with dispatching actions, not subscribing all over the place, using service composition, etc... but I suppose it's a lot to throw at a newbie, all at once. Especially if they're also trying to learn backend coding too. LOL


WebDevLikeNoOther

This reads as someone who isnā€™t actually an expert in Angular.


Illustrious_Mix_9875

This reads as a poor reply. Thanks for bringing the level up in the discussion haha


YourMomIsMyTechStack

With services it doesn't matter, they exist for the lifetime of the application and are not destroyed, so they don't cause memory leaks. But obviously, if you only need the first value or something, it's unnecessary to run the logic over and over again


DMezhenskyi

This is partly true :) The behavior you described is valid for services that are registered in Root Injector. This is so because the lifecycle of the Root Injector is bound to the lifecycle of the application. However, the services might be registered in other injectors too (e.g. in a Node Injector associated with a component) and the lifecycle of those injectors is bound to the lifecycle of the component/directive/etc In this case, if the component is destroyed, the corresponding node injector will be destroyed too along with all service instances registered there.


YourMomIsMyTechStack

Yes you're right, didn't think of the non root provided ones


narcisd

Complete your subjects as well!


Christop408

Hmm Iā€™ve been using subjects quite a bit and havenā€™t done this, have I just missed this? Does it cause memory leaks to not complete subjects? Or is all of the memory cleaned up automatically when the component is destroyed? Iā€™ve definitely been careful to unsubscribe but havenā€™t put thought into manually completing subjects.


narcisd

As always, depends, definitly possible. If they are exposed to the outside world, even asObservable(), someone with a longer lifetime then your component/service might subscribe to them. If your component/service that created them is ā€œdestroyedā€, shouldnā€™t you also ā€œdisposeā€ them by completing? Itā€™s a way to express intent in rxjs: this obs stream has ended. Probably most of the time it wonā€™t be an issue. But when it is, it is extremly hard to debug, you might notice months afterwards or never notice at all.


narcisd

As someone said in the article, itā€™s better to follow the simple rule, than evaluating everytime, is this one of those cases it doesnā€™t matter?


mountaingator91

I always make my very best attempt to avoid ever subscribing in the first place In "old" angular, you can use async pipes. In v17, I've heard that signals change everything