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

แชร์
ฝัง
  • เผยแพร่เมื่อ 17 ม.ค. 2025

ความคิดเห็น • 55

  • @AngularUniversity
    @AngularUniversity  11 หลายเดือนก่อน +3

    Here is the playlist for the whole Angular Mistakes Series, enjoy 😉😊 th-cam.com/video/3oq-gnDzz9k/w-d-xo.html Also, check out my courses at the Angular University, with content from Beginner to Advanced - angular-university.io

  • @nunoarruda
    @nunoarruda 11 หลายเดือนก่อน +33

    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  11 หลายเดือนก่อน +1

      Very helpful @nunoarruda, thank you for sharing. 😊👍

    • @LarsRyeJeppesen
      @LarsRyeJeppesen 11 หลายเดือนก่อน +3

      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 11 หลายเดือนก่อน

      ​@@LarsRyeJeppesenIf you need initial value, pipe the combined observable to first rxjs function. combineLatest(...).pipe(first({[], [], []})
      Update: it's wrong, forget it.

    • @Framoio
      @Framoio 11 หลายเดือนก่อน +4

      ​@@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 11 หลายเดือนก่อน +1

      @@Framoio Yes, you're right, I wrote completely wrong.

  • @benjamincederholm4489
    @benjamincederholm4489 11 หลายเดือนก่อน +1

    Continue your inspiring work! 💡

  • @aleksandrm3466
    @aleksandrm3466 11 หลายเดือนก่อน +2

    Super, thanks for sharing. Looking forward for new one.

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน +2

      I've already finished recording Mistake #2, I think you will like it 😊 I will release it in a couple of days 👍

  • @esayed95
    @esayed95 11 หลายเดือนก่อน

    Thanks a lot. Your videos and blog posts combine simplicity and depth in a perfect way. Thanks again.

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน

      I really thank you for your kind words. Enjoy the videos and courses!

  • @andriesvanwyk3226
    @andriesvanwyk3226 11 หลายเดือนก่อน

    Thank you so much, sir! Your content is extremely valuable! Please keep up the good work.

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน

      Thank you. Stay tuned for more videos!

  • @antonisakov
    @antonisakov 11 หลายเดือนก่อน +1

    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  11 หลายเดือนก่อน

      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. 😊

  • @gambarle
    @gambarle 11 หลายเดือนก่อน

    This is great stuff, thanks 🙂

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน

      Thank you. Stay tuned for more to come!

  • @ico0z
    @ico0z 11 หลายเดือนก่อน +4

    We can something like this as well

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน +1

      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. 👍

  • @antondoit
    @antondoit 11 หลายเดือนก่อน +1

    Thanks

  • @MrDrogoyonk
    @MrDrogoyonk 11 หลายเดือนก่อน +1

    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  11 หลายเดือนก่อน +1

      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 11 หลายเดือนก่อน

      @@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.

  • @Mattakattak
    @Mattakattak 11 หลายเดือนก่อน +3

    How would you manage errors with this pattern?

    • @Framoio
      @Framoio 11 หลายเดือนก่อน +2

      Usually I handle them separately in each source observable.

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน +3

      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 11 หลายเดือนก่อน +2

      @@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 11 หลายเดือนก่อน +1

      @@AngularUniversity btw, awesome video series. Waiting the next episode!

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน +2

      @@Framoio THank you, the next episode is already ready, it will come out tomorrow 👍

  • @abees81
    @abees81 11 หลายเดือนก่อน

    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  11 หลายเดือนก่อน

      I like that one too, thanks for sharing! 😊

  • @MagicPepe97
    @MagicPepe97 11 หลายเดือนก่อน +1

    can we use computedAsync with signas and solve the same problem?

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน

      I could not find a reference to computedAsync, can you tell me more about it?

  • @gary6212
    @gary6212 11 หลายเดือนก่อน +3

    So basically create a view model

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน

      Yes also, but the main thing is to create a single observable that has exactly the behavior that you need. 👍

  • @tranquillityEnthusiast
    @tranquillityEnthusiast 11 หลายเดือนก่อน

    Thanks man

  • @theGoldyMan
    @theGoldyMan 11 หลายเดือนก่อน +1

    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  11 หลายเดือนก่อน +1

      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 11 หลายเดือนก่อน +3

    combineLatest will not emit an initial value until each observable emits at least one value.

    • @nunoarruda
      @nunoarruda 11 หลายเดือนก่อน +1

      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 11 หลายเดือนก่อน +1

      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  11 หลายเดือนก่อน +1

      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. 👍

  • @KiffinGish
    @KiffinGish 11 หลายเดือนก่อน +2

    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  11 หลายเดือนก่อน +7

      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".😉

  • @paulh6933
    @paulh6933 11 หลายเดือนก่อน +1

    I like the short vids.

    • @AngularUniversity
      @AngularUniversity  11 หลายเดือนก่อน

      Thank you, stay tuned I will be publishing more shorts this week, starting tomorrow. 😊

  • @mayasingh1910
    @mayasingh1910 4 หลายเดือนก่อน

    Single Data Observability Pattern

  • @nomoredarts8918
    @nomoredarts8918 10 หลายเดือนก่อน +1

    PageData should be type (alias), not interface

    • @AngularUniversity
      @AngularUniversity  10 หลายเดือนก่อน +1

      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. 👍