Join my mailing list for more exclusive content and access to the archive of my private tips of the week: mobirony.ck.page/4a331b9076
@Fyasco_AlanChoufa2 жыл бұрын
Separate streams.. so simple and so reactive ! Love this !
@JoshuaMorony2 жыл бұрын
EDIT: I've added an example of how you would handle an API/Http request with this approach in the source code: github.com/joshuamorony/async-error-handling/blob/main/src/app/shared/data-access/user/user.service.ts Pinning my response to another comment here because it's an important concept that my dummy observables aren't highlighting: "by default there would be two separate requests if we were making a GET request. So if you were hitting an API with an Http request I would make sure to have the service (e.g. from the getUser method) set up a hot observable (e.g. cache the observable stream using a class member in the service, and use shareReplay) - this way both streams would share the same data emissions and the GET request would only be performed once." I have another tutorial on caching/sharing observables here: kzbin.info/www/bejne/fmaXY42JrtqVqKc
@Matrium0 Жыл бұрын
Thanks for the example. Usually everyone is praising async pipe, without showing error handling AT ALL. I got to say I am not really convinced though. It's just so BLOATED and easy to get wrong (like if you forgot the shareReplay and fire multiple requests). Your github example has like 35 codes dedicated to just handling one simple async function and it's potential errors. I'd rather subscribe manually and handle next and error myself, that's like 10 lines of code AT MOST and pretty much zero room to fuck up. Also seems so much more more readable - though I get that a declarative coding die-hard might drop dead from shock when seeing such blasphemy :)
@PlerbyMcFlerb Жыл бұрын
@@Matrium0 I'm not a fan of this strategy (tons of foot guns) , and I agree in isolation a subscription isn't a big deal... until you have like 10 of them and they start having short transient lifetimes... at which point congrats you've reinvented callback hell
@codeSurvivor Жыл бұрын
Signals to the rescue
@coreytollerud72982 жыл бұрын
Assuming cold observables, doesn't using both user$ and userError$ in the view cause two subscriptions to user$ (i.e. maybe double-fetching data from the server?)
@cheatman05 Жыл бұрын
As a temporary workaround, you can use the shareReplay(1) operator in the pipe of the original source to avoid triggering the cold observable each time a subscription occurs.
@PlerbyMcFlerb Жыл бұрын
@@cheatman05 yep agreed (as long as you remember to also enable refCount as well). This video's advice can be used effectively with multicast observables, but I imagine that this video has led a lot of people down a painful, confusing path since it came out.
@KamelJabber12 жыл бұрын
I've been hooked on your videos lately, thanks! Good stuff!
@diehypotenuse49082 жыл бұрын
solved my daily evening problems in 15 minutes. thats awesome
@beeman-dev2 жыл бұрын
Awesome stuff Josh 🙌
@RobertLejeuneQc2 жыл бұрын
Really helpful, thank you for sharing!
@wangguanghui30672 жыл бұрын
Great video! I wonder if it's possible to extract this loading error and data pieces in a reusable component? The data is projected into ng-content so we can focus on the data.
@reidyoung2982 жыл бұрын
A util function or error handling service could do this nicely for you
@rafiquemohammed3029 Жыл бұрын
Alternatively you can simply combine all the observables and use it as a vm$ . for ex: const vm$=combineLatest(obs1,obs2, ...obsN) and in html to keep more clean and readable
@codeSurvivor Жыл бұрын
Hi! I think this would not be the same since combineLatest won't emit until all the streams it contains emit, therefore the template in the video should be changed, and the logic can become not so clear. You could add startWith(null) for each of the combineLatests streams to have exactly the same behavior, though
@captain_knoxx2 жыл бұрын
really helpful!
@tommclellan2 жыл бұрын
Great stuff, thanks for this example!
@danlevy94782 жыл бұрын
great stuff, love your videos
@shemmuthanga63522 жыл бұрын
Great approach, Josh. Could you make a follow up video showing how one can update the data e.g deleting/adding an item in an array, pagination. Thanks
@JoshuaMorony2 жыл бұрын
Is there some specific problem you face in regard to this? Let's say the getUser method is actually a getList method that returns a stream of an array of items that we display in the template. If we wanted to add an item then we would just do something like call addItem which would handle adding the item in the service, and then cause the getList method to emit the new data on that stream, which will then be picked up by the subscription we already have.
@shemmuthanga63522 жыл бұрын
@@JoshuaMorony Right. That makes sense. Especially if you are using a state management lib or the (Behaviour)Subject pattern. My approach involved trying to append/delete to the response from within the template or component instead of emitting a new response from the observable in the service
@JoshuaMorony2 жыл бұрын
@@shemmuthanga6352 Can you give me a specific scenario (e.g. you have an array of items, you are displaying that in the template, you want to add an item to the array - why is it that you want to add an item but not to the source observable/what do you want to do with it etc) and I might be able to give some advice on doing this in a reactive way :)
@shemmuthanga63522 жыл бұрын
@@JoshuaMorony OK. Let's say you have an api which you pass a number to fetch paginated data. e.g /api/posts/1. Page 2 will be /api/posts/2, etc. Page 1 will be initialised in the ngOnInit hook . How would you fetch and append the next page results, reactively :) e.g through infinite scroll event or button click. The response for each request will be something like {success: boolean, results: [posts] }. I hope I am comprehensible enough.
@JoshuaMorony2 жыл бұрын
@@shemmuthanga6352 Cool that makes sense - so in this case what you would want is a stream of that page number changing. Maybe you have a reactive form set up to handle the page input and then you utilise the valueChanges stream for that input. That will be the stream you subscribe to with the async pipe in the template, but of course we don't want to display the page number we want to display the data for that page number. So you pipe on a switchMap operator to that valueChanges stream - this will allow you to take the value from valueChanges, but pass it into a new observable stream instead. You would have the switchMap operator return the observable stream from the http request that fetches the data from the API (and that switchMap operator will have access to that page value from the original stream). Now every time the page number changes, your stream will emit the new page data. This probably would make a good video so maybe I'll do it!
2 жыл бұрын
Great video, thanks!
@Dorchwoods Жыл бұрын
To add to this, in the app I'm working on, the card could have 4 states: success state (showing data), a loading state, the error state, and......an empty state. So basically showing some sort of graphic and a message that says 'No users' if !users.length. Thoughts on how to best handle that? I've never found a good, clean way of handling that without bringing in NGRX, which most of the time is overkill on small applications
@JoshuaMorony Жыл бұрын
I use NgRx Component Store for these sorts of scenarios - if you're not familiar with Component Store specifically it is a much more lightweight solution than the full NgRx Store. I have a video on doing pretty much this scenario here: kzbin.info/www/bejne/nYrdcpuNlMyfp8U
@lautarobernati2557 Жыл бұрын
Couldn't you just mutate the original observable with the throwError function and return an empty response, so that the original observable doesn't error out, and the async pipe doesn't break? Rather than managing errors on a separate observable? Great video anyway
@leonardopillay42002 жыл бұрын
Hi there i love your videos. Can you do an implementation of a login screen with a ion menu and nested routing in an application
@ttma10462 жыл бұрын
is this two async on the same stream?which means subscribing on the same stream twice?
@JoshuaMorony2 жыл бұрын
There are two separate streams: user$ and userError$ and they are each being subscribed to once.
@ytamb012 жыл бұрын
@@JoshuaMorony if these were http requests would there just be one http request for each user even though there are two streams/subscriptions?
@JoshuaMorony2 жыл бұрын
@@ytamb01 this is a good point to bring up - by default there would be two separate requests. So if you were getting an API with an Http request I would make sure to have the service (e.g. from the getUser method) set up a hot observable (e.g. cache the observable stream using a class member in the service, and use shareReplay) - this way both streams would share the same data emissions and the GET request would only be performed once.
@ytamb012 жыл бұрын
@@JoshuaMorony Thanks. I'm never sure when sharing streams has this effect without watching the network tab!
@JoboyJordan2 жыл бұрын
@joshua morony, how would you cache the observable if there's an input paramter to getUser like userId?
@TayambaMwanza2 жыл бұрын
Genius
@pauleth2 жыл бұрын
lol metamask
@natqe40492 жыл бұрын
shareReply instead of vm
@JoshuaMorony2 жыл бұрын
Is there a particular reason you prefer shareReplay in this instance instead of creating a vm? shareReplay would deal with the issue of creating multiple subscriptions, but what I like about the vm is that I can just use the async pipe in one place rather than having to use it every time I want to use the value (just from a syntactic perspective).
@natqe40492 жыл бұрын
@@JoshuaMorony shareReply is straight forward. vm looks like a hack and need explanation, it isn’t understandable when reviewing the code
@JoshuaMorony2 жыл бұрын
@@natqe4049 thanks for your perspective, I do disagree on it being a hack though. Using a vm is a pretty common Angular pattern, and I'm creating it directly in the template here but you can also just create a single separate vm$ observable in the class to clean the template up even more.
@nickolaizein74652 жыл бұрын
@@JoshuaMorony Hello! Can you show how vm$ can looks in this way ? In template I use now: *ngIf="{ channels: channels$ | async, error: channelsError$ | async } as vm", so if I understand correctly it can be just: *ngIf=" (vm$ | async) as vm" ? But I dont understand how to build vm$ in class in this case. Thanks!