Тёмный

💥Angular Mistakes #1: ✋ DON'T Overuse the Async Pipe  

Angular University
Подписаться 50 тыс.
Просмотров 6 тыс.
50% 1

Опубликовано:

 

2 окт 2024

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 55   
@AngularUniversity
@AngularUniversity 8 месяцев назад
Here is the playlist for the whole Angular Mistakes Series, enjoy 😉😊 ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-3oq-gnDzz9k.html Also, check out my courses at the Angular University, with content from Beginner to Advanced - angular-university.io
@nunoarruda
@nunoarruda 8 месяцев назад
Tip: If you're using the latest version of RxJS, you can "simplify" the data$ observable further. 1. Define data$ directly in its declaration. No need for ngOnInit. 2. Use type inference. No need for manually typing Observable, and no need for an extra interface. 3. Use an object directly in combineLatest. No need for an array + mapping to an object. data$ = combineLatest({ courses: this.courses$, lessons: this.lessons$, users: this.users$ });
@AngularUniversity
@AngularUniversity 8 месяцев назад
Very helpful @nunoarruda, thank you for sharing. 😊👍
@LarsRyeJeppesen
@LarsRyeJeppesen 8 месяцев назад
combineLatest will not emit an initial value until each observable emits at least one value. In this example it would be a dangerous operator I think
@zoltanboros8963
@zoltanboros8963 8 месяцев назад
​@@LarsRyeJeppesenIf you need initial value, pipe the combined observable to first rxjs function. combineLatest(...).pipe(first({[], [], []}) Update: it's wrong, forget it.
@Framoio
@Framoio 8 месяцев назад
​@@zoltanboros8963don't first operator block the stream after the observable created over combineLastest emits the first time? You probably are referring to add startWith over each source observable..
@zoltanboros8963
@zoltanboros8963 8 месяцев назад
@@Framoio Yes, you're right, I wrote completely wrong.
@antonisakov
@antonisakov 7 месяцев назад
What about writing @if ({ courses: courses$ | async, lessons: lessons$ | async, users: users$ | async } as params) { ... } at the top of the component template? I think it is easier than using combineLatest in the component, isn't it? 4:31
@AngularUniversity
@AngularUniversity 7 месяцев назад
Yes I don't dislike that pattern, it works for a lot of cases. I also show it also in one of my videos here on the Angular Mistakes series. This gives you less control over the flow of the data I think, it might be important in certain cases. For example, you might want to apply a filter to your observable to make sure you only show data if at least the user an the courses is present, to know if its an admin or not. 😊
@gary6212
@gary6212 7 месяцев назад
So basically create a view model
@AngularUniversity
@AngularUniversity 7 месяцев назад
Yes also, but the main thing is to create a single observable that has exactly the behavior that you need. 👍
@nomoredarts8918
@nomoredarts8918 6 месяцев назад
PageData should be type (alias), not interface
@AngularUniversity
@AngularUniversity 6 месяцев назад
I've been using types instead of interfaces in my latest videos, I've grown to like them more for custom object types, they describe better what we are trying to do. 😊 They are generally interchangeable for most practical situations. 👍
@aleksandrm3466
@aleksandrm3466 8 месяцев назад
Super, thanks for sharing. Looking forward for new one.
@AngularUniversity
@AngularUniversity 8 месяцев назад
I've already finished recording Mistake #2, I think you will like it 😊 I will release it in a couple of days 👍
@ico0z
@ico0z 8 месяцев назад
We can something like this as well
@AngularUniversity
@AngularUniversity 8 месяцев назад
Yes I think that works too, but writing the single data observable manually can give you more control about the behavior of the Observable. For example, you might want to use startWith and initially all values to null, and then emit the data as it arrives from the backend without waiting for everything to load. Here is this example, we have to wait for all observables to emit before the screen showing something. But it would work just fine. 👍
@KiffinGish
@KiffinGish 8 месяцев назад
If a given component requires data from multiple sources, I always use a single resolver with a fork join. In my opinion, placing (complex) data handling in a component with conditional rendering is poor design. The whole idea behind resolvers is to avoid these extra calculations so that the component can be assured that the data is already available when the component is initialized.
@AngularUniversity
@AngularUniversity 8 месяцев назад
Yes this is correct, 👍 resolvers avoid having to use the async pipe in the container component, if you resolve all the data upfront and provide it synchronously via the router. This assumes that from a user experience perspective it's OK to wait until all the data of the screen is loaded before showing anything to the user, and it also assumes that the data won't change after it gets loaded. I think this approach works for most typical enterprise form and table-based screens. But sometimes in certain screens, we want to load multiple sources and start showing the data as soon as possible to the user, while the other data sources continue loading. Imagine the Netflix home screen where the data is streaming in in response to the user scrolling down. In those cases, we can't use resolvers, we need the data loading to happen in the container component, so the async pipe has it's use cases, just like resolvers. 👍The main concept that I wanted to say in the video was: "if you ever find yourself in a situation where you are using the async pipe all over the template, consider refactoring to a single data observable".😉
@mayasingh1910
@mayasingh1910 Месяц назад
Single Data Observability Pattern
@theGoldyMan
@theGoldyMan 8 месяцев назад
I would just use an ngTemplateOutlet. I also think that an if statement with an object will always be truthy, even if one of the properties is undefined or null, because combineLatest will emit when any of the included observables emit, so you might get an issue here if you remove completely some of the ifs
@AngularUniversity
@AngularUniversity 8 месяцев назад
combineLatest will also wait for all of the observables to emit at least once before emitting the first tuple, but you actually might prefer to emit an initial null value for all data and then display what it arrives as it arrives, independently of the order of arrival. You can emit the initial value with startWith; This is just an example of what you can do in terms of customizing the behavior that you want. You might add a filter requiring the user to be present so that you know the permissions of the user before displaying anything, etc. The video was just a plain example to illustrate the pattern, we can use any combination of RxJs operators that we need to get the exact behavior we want. 👍
@LarsRyeJeppesen
@LarsRyeJeppesen 8 месяцев назад
combineLatest will not emit an initial value until each observable emits at least one value.
@nunoarruda
@nunoarruda 8 месяцев назад
Yes, that's correct. This needs to be addressed, depending on the use case. For HTTP requests I usually use something like: @if (data$ | async as data) {...} @else { Loading... } plus some error handling on the observable with catchError.
@mvandijk5927
@mvandijk5927 8 месяцев назад
Exactly what I wanted to comment. To fix this, use operator 'startWith' to set a default value (could be null also), to get the combineLatest emit at least once.
@AngularUniversity
@AngularUniversity 8 месяцев назад
Yes correct, that might or might not be be what we need in that situation, usually with HTTP for example that is not an issue. We usually need some form of combineLatest, but we should combine it with any other operators that we need to achieve the end result we are aiming for. The pattern is not just necessarily using combineLatest alone, that was just a bare bones example to illustrate the idea. The main idea is to create a single observable, and it might take any combination of operators to achieve the behavior we need. 👍
@gambarle
@gambarle 8 месяцев назад
This is great stuff, thanks 🙂
@AngularUniversity
@AngularUniversity 8 месяцев назад
Thank you. Stay tuned for more to come!
@Mattakattak
@Mattakattak 8 месяцев назад
How would you manage errors with this pattern?
@Framoio
@Framoio 8 месяцев назад
Usually I handle them separately in each source observable.
@AngularUniversity
@AngularUniversity 8 месяцев назад
This might be useful if we need to give specific messages to the user per source observable. If we only need a more generic screen-wide message, handling in the main observable works well. Notice that handing the error in each source observable might give a weird user experience if several of the source observables fail, and the end user ends up seeing multiple error messages at the same time, instead of just one.
@Framoio
@Framoio 8 месяцев назад
@@AngularUniversity yes; by saying "in each source observable" I was meaning before updating my state. If a REST fails, I simply pop an error message and then set a default value (i.e. null,[] - depends on context) in that state property
@Framoio
@Framoio 8 месяцев назад
@@AngularUniversity btw, awesome video series. Waiting the next episode!
@AngularUniversity
@AngularUniversity 8 месяцев назад
@@Framoio THank you, the next episode is already ready, it will come out tomorrow 👍
@MrDrogoyonk
@MrDrogoyonk 7 месяцев назад
Many thanks for sharing! I really appreciate your work! Just one question, what if you need to display a loading for each block of data (courses, lessons, users)? Would you still have a single observable and async pipe and you would emit each time the data is available? If so, would not it be a "performance issue"? I mean, if I update courses loading, it will emit this change to the single data observable and it will refresh the whole template, isn't it?
@AngularUniversity
@AngularUniversity 7 месяцев назад
While building the single data observable, you can use the operators that you need to give it any behavior that you want. You can initialize it with startWith, so that the data gets emitted as it arrives, without waiting for everything to arrive, etc. you can then use @if to detect if the data is present, and show the loading indicator that way. I don't think there would be any performance issue. 👍
@codeSurvivor
@codeSurvivor 7 месяцев назад
@@AngularUniversityThe problem with this solution is that you cannot tell whether the data was loaded and it's an empty array or not... You can use also undefined or null, but this complicates typings. I would use a little more complex but robust approach mapping each data stream to an object with the data itself plus a loading property. startWith({ data: [], loading: true), and once you get the data: set loading to false.
@ratg97
@ratg97 8 месяцев назад
can we use computedAsync with signas and solve the same problem?
@AngularUniversity
@AngularUniversity 8 месяцев назад
I could not find a reference to computedAsync, can you tell me more about it?
@benjamincederholm4489
@benjamincederholm4489 7 месяцев назад
Continue your inspiring work! 💡
@AngularUniversity
@AngularUniversity 7 месяцев назад
Thank you 😊
@abees81
@abees81 7 месяцев назад
Thank you for the video. I personally prefer using toSignal for each observable, then simply use computed to combine the three signal into a single source which could be easily consumed in the template.
@AngularUniversity
@AngularUniversity 7 месяцев назад
I like that one too, thanks for sharing! 😊
@paulh6933
@paulh6933 8 месяцев назад
I like the short vids.
@AngularUniversity
@AngularUniversity 8 месяцев назад
Thank you, stay tuned I will be publishing more shorts this week, starting tomorrow. 😊
@esayed95
@esayed95 8 месяцев назад
Thanks a lot. Your videos and blog posts combine simplicity and depth in a perfect way. Thanks again.
@AngularUniversity
@AngularUniversity 8 месяцев назад
I really thank you for your kind words. Enjoy the videos and courses!
@andriesvanwyk3226
@andriesvanwyk3226 8 месяцев назад
Thank you so much, sir! Your content is extremely valuable! Please keep up the good work.
@AngularUniversity
@AngularUniversity 8 месяцев назад
Thank you. Stay tuned for more videos!
@antondoit
@antondoit 8 месяцев назад
Thanks
@AngularUniversity
@AngularUniversity 8 месяцев назад
You're welcome! 😊
@tranquillityEnthusiast
@tranquillityEnthusiast 8 месяцев назад
Thanks man
@AngularUniversity
@AngularUniversity 8 месяцев назад
You're welcome! 😊