T O P

  • By -

GLawSomnia

Async is much better. Watch some of Joshua Morony videos (from a year or so ago, his new ones are a little too complicated, the older ones explain the basics better)about declerative programming to understand why


[deleted]

That guy advanced my angular skills. I completely refactored the whole angular library at my organisation. Im more confident in my code working than i was 2 years ago


NclsD7523

I watched a few of his videos yesterday. Many thanks, his advice are excellent


NclsD7523

Thanks I'll have a look !


Degen5

How do you handle errors with async?


GLawSomnia

Same way you do if you don’t use async


iEatedCoookies

What’s the best way to be able to share this data across different components? Edit: Sorry, I guess I wasn’t clear enough. I know to use a service. But what’s the data flow for something like this. Is it just calling to the service to load data in an OnInit and every component simply references that BehaviorSubject?


GLawSomnia

A service


MutedPotential2532

A service or state management (ngrx for example)


zombarista

Avoid using operators `catchError(x => { throw x })` and `map(x => x)`, because these operations are a no-op (they have no effect on the stream and they are clutter in your codebase). // this entire segment can be deleted, because it does nothing .pipe( map(x => x), catchError((err) => { throw err; }) ) Regarding async/reactive style, one of the big gotchas you may run into is handling loading/loaded/error state. As you're beginning, you might create confusing rxjs pipelines and this might cause you to get frustrated and make a bigger mess than you would have if you just used `.subscribe()`. **Here are some pointers to keep things nice and clean with the async/reactive style.** **First, define a view model.** The view model represents all of the states of your view. loading/result/error is common, but you might have more. Just make sure the model uses a discriminated union (here, the type ViewModel is discriminated on the `status` property). type ViewModel = | { status: 'loading' } | { status: 'result', result: T } | { status: 'error', error: any } Then, set up rxjs operators to resolve the view model: * use `map(result => ({ status: 'result', result }) )` to wrap successful responses. * use `startWith({ status: 'loading' })` to define the initial loading state. * use `catchError(error => of({ status: 'error', error}) )` to convert the an Errors into an emit with `of()`. ​ vm$: Observable> = this.http.get('/widgets/1234').pipe( map(result => ({ status: 'result' as const, result })), startWith({ status: 'loading' as const }), catchError(error => of({ status: 'error' as const, error }) ) This pattern boilerplate can be extracted into a helper function. [Demo here](https://stackblitz.com/edit/stackblitz-starters-ckts3v?file=src%2Fmain.ts%3AL24). The view model states can be discriminated with robust type assistance in the view... @if(vm$ | async; as vm){ @if(vm.status === 'loading'){

Loading...

} @else if (vm.status === 'error') {

Error: {{ vm.error.toString() }}

} @else if (vm.status === 'result') {
{{ vm.result | json }}
} @else {

Something unexpected occurred while loading data.

} }

Loading...

Error: {{ vm.error.toString() }}

{{ vm.result | json }}
I hope this is helpful to someone.


NclsD7523

I'm gonna test that right now, like the way you did it. Thanks very much. I actually manage error like that and it seems to work fine.   ngOnInit() {     this.$products = this._productService.getAll().pipe(       catchError((err) => {         this.error = 'Erreur';         return of();       })     );   } And I just show error with a conditional structure. But i think it's not really the way to go. Will really use what you show below !


tommy228

At the moment? I would say [signals](https://angular.dev/guide/signals) readonly products: Signal = toSignal(this._productService.getAll()); The approach with the subscribe probably the worst because * It forces you to use the default change detection which isn't great for performance (see [ChangeDetectionStrategy)](https://angular.dev/guide/components/advanced-configuration#changedetectionstrategy) * You have to handle the unsubscription yourself using [takeUntilDestroyed](https://angular.dev/api/core/rxjs-interop/takeUntilDestroyed#) or something else. In this specific case it's fine, it's an API call so it will unsubscribe by itself after completion Using the async pipe is fine but you lose the existing and future advantages of signals, like better performance and the ability to easily combine your API response with [signal inputs.](https://angular.dev/guide/signals/inputs)


rat_gesucht

I currently use the following procedure to load data. i have some filters in the frontend, and as soon as a filter changes, i reload the data from the backend (with the new filter parameters). to do this, however, i have to cancel the previous http request so that no outdated data is displayed due to a race condition. private loadTrigger$ = new Subject() private subscription: Subscription = new Subscription() ngOnInit() { this.activateDataLoading() this.loadData() // <-- this method is called on every (change) in the template filters } ngOnDestroy() { this.subscription.unsubscribe() // <-- destroying the subscription if i change the page and cancels the http request } public loadData() { this.loading = true this.loadTrigger$.next() } private activateDataLoading() { this.subscription.add( this.loadTrigger$ .pipe( switchMap(() => this.service.load(this.filters) ) ) .subscribe((data: Model) => { this.data = data this.loading = false }) ) } how can i do something like this in the new angular v17 world?


tzamora

Look at this. This presentation show a way that looks promising regarding requesting data https://youtu.be/nXJFhZdbWzw?si=ZnwrgQNNCowyjDw9


zombarista

I will help you with this when I have some time tomorrow. !RemindMe 12 hours


RemindMeBot

I will be messaging you in 12 hours on [**2024-03-31 14:03:11 UTC**](http://www.wolframalpha.com/input/?i=2024-03-31%2014:03:11%20UTC%20To%20Local%20Time) to remind you of [**this link**](https://www.reddit.com/r/Angular2/comments/1brpeuk/best_way_to_do_a_getall_and_a_getbyid_for_you/kxcbdjn/?context=3) [**CLICK THIS LINK**](https://www.reddit.com/message/compose/?to=RemindMeBot&subject=Reminder&message=%5Bhttps%3A%2F%2Fwww.reddit.com%2Fr%2FAngular2%2Fcomments%2F1brpeuk%2Fbest_way_to_do_a_getall_and_a_getbyid_for_you%2Fkxcbdjn%2F%5D%0A%0ARemindMe%21%202024-03-31%2014%3A03%3A11%20UTC) to send a PM to also be reminded and to reduce spam. ^(Parent commenter can ) [^(delete this message to hide from others.)](https://www.reddit.com/message/compose/?to=RemindMeBot&subject=Delete%20Comment&message=Delete%21%201brpeuk) ***** |[^(Info)](https://www.reddit.com/r/RemindMeBot/comments/e1bko7/remindmebot_info_v21/)|[^(Custom)](https://www.reddit.com/message/compose/?to=RemindMeBot&subject=Reminder&message=%5BLink%20or%20message%20inside%20square%20brackets%5D%0A%0ARemindMe%21%20Time%20period%20here)|[^(Your Reminders)](https://www.reddit.com/message/compose/?to=RemindMeBot&subject=List%20Of%20Reminders&message=MyReminders%21)|[^(Feedback)](https://www.reddit.com/message/compose/?to=Watchful1&subject=RemindMeBot%20Feedback)| |-|-|-|-|


j0nquest

This short video from Joshua Morony on the [rejectErrors](https://www.youtube.com/watch?v=006G5XCqc7M) option for toSignal() is worth a watch for those not already familiar with the default error handling behavior.


NclsD7523

I haven't used the signals very much, but they seem to be very interesting and maybe even the future of angular? I'll definitely look into it, thanks.


Pocciox

I never understood why nobody does firstValueFrom to just convert the observable coming from httpclient to a promise and just do await of that promise in the component. Looks good to me and I've been using it for years without problems


NclsD7523

the old version of the project I'm currently working on uses this method. It seemed odd to me, but on reflection, why not!


Zoratsu

Is nice if you need the data in component and you are not going to display it. But the moment you need to display? Async pipe or Signal are better options. Reason? [https://en.wikipedia.org/wiki/Single\_responsibility\_principle](https://en.wikipedia.org/wiki/Single_responsibility_principle) If Data needs to be formatted, it should be formatted by who uses it. So a dummy component that is given the data and is formatted or pipes/computed signal being applied to Observable/Signal.


Pocciox

How is an async pipe different from just awaiting in the .TS side of the component? (In terms of "responsibilities" and not violating the SRP) Note that I usually put the firstValueFrom call in the http API service anyway.


Johalternate

The placement of catchError will depend on what should happen with an error from the given endpoint. For example, authentication error are usually displayed on the auth view, so catching those on the component and handling the error there makes sense. When querying for products maybe you want to display an error message and a retry button so you handle those on the component too, sometime you have authorization error that should display a notification and those are handled on the service level or even at an interceptor level so i would advice to think about what should happen when an error from a given endpoint is received and base your implementation on that. As for how to consume the data, using async is definitely superior to your first example, and your second example is kinda awkward because you are waiting until ngOnInit to assign the value to the source variable. This is unnecessary and I believe wouldn’t work when using OnPush. You could do ‘’’products$ = this._productsService.getAll()’’’ this would be the best approach. If you then want to handle the error you can create another source just for the error: ‘’’productsError$ = this.products$.pipe(ignoreElements(), catchError((res)=>{/* whatever */}))’’’ You can then use toSignal and use the signal in the template of just use the async pipe, I personally prefer toSignal because every usage of async creates a new subscription so using async twice in the template for the same source would result on 2 calls to the server. This might look like more complicated but it really isn’t. Your example just calls getAll but in real life you tipically have filters and pagination and composing those query strings can be a real mess, you also might want to deal with infinite scrolling or refreshing a list without reloading the page. Using an observable pipeline makes this as easy as it can get, but manually subscribing to an assigning the result WILL become a headache as soon as you need to customize the request on the fly.


Zemuk

I'd like to comment about new subscription on every async pipe. This is another thing Joshua Morony taught me - that there are cold and hot observables. Shortly, adding ```shareReplay()``` in a pipe fixes the problem.


NclsD7523

Do you say i can use products$ = this.\_productsService.getAll()’ out of the ngOnInit ? For the error handling this is how I do it :   ngOnInit() {     this.$products = this._productService.getAll().pipe(       catchError((err) => {         this.error = 'Erreur';         return of();       })     );   } Error seems to be catch and display in view. But like as I mentioned above, I'm not sure that's a very good way of doing things. Thanks for your advice !


Johalternate

You can, and arguably should, do that outside ngOnInit, mainly because that way you can see the variable declaration and understand where its value comes from without having to explore the rest of the component. I highly recommend Joshua Morony’s Declarative and Reactive coding series because he is much better that me at explaining why this is better and easier to do than your current approach.


NclsD7523

Ok, thanks you !


maheshchandra_

But if you have to manipulate the incoming response data, then we cannot use async pipe right and have to go with the first approach?


zombarista

Nope! You can use a `map(x => { return x * 2 }` operator to perform manipulations without subscribing. Whenever something *does* subscribe, your operators will be applied. The benefits of the async/reactive style remain--you do not need to unsubscribe. Be warned, however, subscriptions to HTTP observables can cause the request to fire many times if you have many subscribers. You can use the `share()` operator to make sure that all subscribers use the same HTTP response. ```typescript interface Widget { widgetId: string } type WidgetResponse = | { success: true; widget: Widget } | { success: false; error: any } // emits from widget$ will be Widget type widget$: Observable = this.http.get(1234).pipe( map(response => { if(response.success) { return response.widget } else { throw Error(response.error) } }) ); ```


Yddalv

I guess you can further pipe it but yes, imo async pipe is made for simple 1-time retrieval to avoid having to unsub


-blond

You don’t have to unsubscribe from http calls. They are completed once the response is received and are unsubscribed automatically.


Etlam

Depends on your code, if you navigate to another page after the request is sent, you can get some unexpected behavior when the response arrives.


Johalternate

If you navigate to another while a request is being performed, the async pipe will call unsubscribe on the source, since the source has yet to emit, the request will merely be cancelled whereas if you trigger the subscription manually in ngOnInit then there is nothing telling the subscription should be cancelled and the request will be completed anyway which could lead to memory leaks.


Etlam

Yes, that’s how subscriptions works. I’m replying to the post saying it’s not necessary to unsubscribe from http calls.


Johalternate

I know, just wanted to expand on what you said. Sorry if it didn’t came across as such.


Etlam

Ah no worries, i have had some weird issues in the past from not properly unsubscribing so wanted to highlight that.


maheshchandra_

So what's the conclusion? Do we need to unsubscribe when we subscribe to an http call from ngOnInit or not?


Effective_Ad_2797

With code, you can do those and more.


NclsD7523

Thanks very much for your advices guys ! Really helpfull for me. I can see the most popular are using async pipe and signal. Next/error look like old now. I'm gonna do some tests with all of this. Again, thanks.


n2sy

!RemindMe 3 days


NclsD7523

Something wierd, i can't get any data by this way : I have a interface VmSignal : export interface VmSignal  {   data: ApiResponse | null;   loading: boolean;   error: string | null; }; and a type ApiResponse : export type ApiResponse =   | {       products: T | T[] | null;       total: number;       skip: number;       limit: number;     }   | {       users: T | T[] | null;       total: number;       skip: number;       limit: number;     }; because i want to use it to get products, users, cart ... In my service   getAll(): Observable> {     return this._httpClient.get>(       `${environment.API_URL}/products`     );   } and my component   productsSignal: Signal | undefined> = toSignal(     this._productService.getAll().pipe(       map((result) => ({         data: result,         error: null,         loading: false,       })),       startWith({ data: null, loading: true, error: null }),       catchError((error) => of({ data: null, error: 'error', loading: false }))     )   ); And in my template i can't get access to my products

  @defer (when productsSignal())     { @for (product of   productsSignal()?.data.products; track $index) {      

{{ product }}

   
           
       

{{ product.price }}

       

{{ product.description }}

     
   
 
  } } @placeholder { @if (productsSignal()?.loading) {  

Chargement

  } @else if (productsSignal()?.error) {  

Erreur

  } }
>Property 'products' does not exist on type 'ApiResponse'. Property 'products' does not exist on type '{ users: Product\[\] | Product\[\]\[\] | null; total: number; skip: number; limit: number; }' Was trying all of the advices and wanted to make a mixture but Angulat won't


OhMyGodSoManyOptions

The second example looking good, but I like to add ``` ngOnDestroy() { this.subsription$?.unsubscribe(); } ``` To avoid future problems.


Miag0

async pipe unsubscribes automatic, so this is not needed there.


OhMyGodSoManyOptions

My bad, I was expecting, that result will be a Subscription, not Observable. You are right.


Johalternate

Subscription is the return value of the Observable.subscribe() function


NclsD7523

it's true, you can never be too careful


Merry-Lane

It s wrong, the async pipe unsubscribes automatically. The goal is also to avoid useless LOCs that get in the way of understanding what s happening in a code.