My go to way to manage state in Angular applications

แชร์
ฝัง
  • เผยแพร่เมื่อ 7 มิ.ย. 2024
  • My modern Angular course: angularstart.com/
    In this video we take a look at how to use the "Service with a Subject" approach to managing state with RxJS in an Angular application.
    Get weekly content and tips exclusive to my newsletter: mobirony.ck.page/4a331b9076
    Learn Angular with Ionic: ionicstart.com
    RxJS/Observable Basics: • OBSERVABLES ARE CONVEY...
    Caching/Sharing Observables: • How to share your RxJS...
    Operators: • MAP, FILTER, AND MERGE...
    Using the Async Pipe: • How to use the ASYNC P...
    0:00 Introduction
    0:36 Setting up the Subject
    1:47 Accessing the stream
    3:11 Emitting data
    4:47 Sharing streams
    5:22 Modifying streams
    6:15 Accessing state
    7:51 Conclusion
    #angular #rxjs #ionic
    - More tutorials: eliteionic.com
    - Follow me on Twitter: / joshuamorony

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

  • @bamboo6044
    @bamboo6044 ปีที่แล้ว +8

    If you haven't already, it might be a good idea to make an updated version of this, perhaps create a service for CRUD that uses generics and can be extended and used for various API endpoints

  • @NvmThemHereIAm
    @NvmThemHereIAm ปีที่แล้ว

    I instinctively created a similar pattern (as in not making the subject private and accessing it straight away). Thank you for this explanation!

  • @jaredwilliams8621
    @jaredwilliams8621 ปีที่แล้ว +20

    I have implemented the "service with a subject" pattern in several applications, and it works great for sharing the data from the service to the components. However, I feel that there isn't a lot of examples out there demonstrating how the component is supposed to share new/modified data back with the service in a reactive way.

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

      I came here as I want to know more about that too

    • @go41000
      @go41000 8 หลายเดือนก่อน +1

      @@SuperQuwertz any luck? is the data availalbe on page refresh?

    • @SuperQuwertz
      @SuperQuwertz 8 หลายเดือนก่อน

      I use Svelte so I ended up with the value of the component getting set by the stream and nexting the stream if the component emits changes@@go41000

    • @DD-vc7fq
      @DD-vc7fq 7 หลายเดือนก่อน

      My main advice here would be to implement all of the logic inside services, and handle only display stuff in the components.
      But, if there is something you want to do inside component that will change the data and also notify the service that the data has been changed, that can be done easily.
      Imagine if we have a Component and a ComponentService.
      Inside ComponentService you have:
      someSubject = new BehaviourSubject(0);
      ----
      Inside Component you have:
      someSubject = this.componentService.someSubject;
      constructor(private componentService: ComponentService) {
      }
      onClick() {
      this.someSubject.next(1);
      }
      What will happen here is the value of someSubject changes to 1 inside component, but it also changes to 1 inside service as well, because we defined that someSubject from Component is equal to someSubject from ComponentService (if anyone wonders why the changes on one subject makes the same changes on another subject, read about shallow copies).
      My general advice is: make a service for each component you make. Handle all of the logic inside services (if possible). Obviously, there will be times when you simply need to handle the logic inside components (prime example is if you want to change the data inside AfterViewInit() lifecycle hook, which is a hook that services don't have since they don't have html templates).

    • @trevormontgomery6795
      @trevormontgomery6795 7 หลายเดือนก่อน

      Can you pass a parameter to the getter in the service? You could call next in the getter

  • @stephenjames2951
    @stephenjames2951 ปีที่แล้ว

    Clear and concise description of the method used

  • @niravparsana
    @niravparsana 2 ปีที่แล้ว +20

    Very helpful topic. Please do video on NgRx as it is more flexible for managing state in complex application. It would be good if you start with basic topics as NgRx is wide subject.

    • @dalu_
      @dalu_ ปีที่แล้ว +4

      ngrx has so much boiler plate.
      idk if it's more flexible, but ok, this comment is a year old now.

  • @luismpcunha
    @luismpcunha 2 ปีที่แล้ว

    Good info, will definitely be using the approach of separating getting all of the data from getting only the new elements going forward.

  • @EverydayJavaScript
    @EverydayJavaScript 2 ปีที่แล้ว

    Nice video. This helped me fix one of the issues in my project.

  • @jacobshomali5434
    @jacobshomali5434 2 ปีที่แล้ว +3

    I tried using services and observables like this in angular but couldn't quite get it to work. The solution I came to eventually was to use redux instead but I'm glad I can finally figure out how to do it this way.

  • @ilclaudio123
    @ilclaudio123 2 ปีที่แล้ว

    Thank you for this intertesting video :-)

  • @jayasaichandmaheshmunagala2135
    @jayasaichandmaheshmunagala2135 2 ปีที่แล้ว

    Love to see ngrx content.please make it

  • @user-is5bs8qb4y
    @user-is5bs8qb4y ปีที่แล้ว

    Thank you very much for the insight :)

  • @ksivasuriyaprakash9976
    @ksivasuriyaprakash9976 2 ปีที่แล้ว +3

    Please post a video about NgRx.

  • @TravisSomaroo
    @TravisSomaroo ปีที่แล้ว

    Hey Josh, very helpful topic. I wanted to ask would this approach update the UI based the database has been updated. So for example if I create a new article would the list of articles be updated without me having to refresh the page? Thanks in advance. :)

  • @trustingod0
    @trustingod0 2 ปีที่แล้ว

    I learned a lot from your video. I was wondering would it be a best practice to update my list of employees only when i add a new employee and not make an http call to the server except for in the case when the user wants to look at the list of employees when the application first begins and to give an initial stream of data to the observable. But then i worry about memory and holding a lot of data in the observable, is this feasible? Is holding the data in memory more efficient than making an API call to the server? Thanks !!!

  • @satheshkumar5906
    @satheshkumar5906 ปีที่แล้ว

    would be useful if you could prepare a tutorial on best practices using ngrx

  • @tobihbestpaul1444
    @tobihbestpaul1444 2 ปีที่แล้ว

    please a video for the NgRx store, some applications need "order" and less freedom.

  • @go41000
    @go41000 8 หลายเดือนก่อน

    Will the data be available on page refresh?

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

    where you start the init function ?inside the service? in the app component? how should we initialize the article$?

  • @Mystearica
    @Mystearica ปีที่แล้ว

    Very nice video as always.
    How do you handle errors this way?
    In the app we are working, we have to show the error when a 200 ok response with an specific object, insted of the one we are expecting, so we need to add ifs always inside the subscribe to check it.

    • @JoshuaMorony
      @JoshuaMorony  ปีที่แล้ว +2

      My go to approaches are either using a separate error stream (one that will use ignoreElements to ignore normal emissions, and a catchError that will return a stream of the error value), or using Component Store to handle the error states. A few of my recent videos talk about these approaches.

  • @AdrienSAULNIER
    @AdrienSAULNIER ปีที่แล้ว +1

    Nice video, ty ! Some questions :
    - How to set/handle UI state in your example (articles loading) into the service ? Do you create a new observable such as loading$ (or callState$ which represents the actual HTTP call status) or a state object containing the array of articles AND the ui state in the same object ?
    - How to handle error ?
    - How to handle substate (a child object by example, which depends of articles feature) which has is own ui state too ? Do you create new service or do you manage this substate into the ArticlesService ?
    I try and read so many many (many...) things and I never found the best (and universal) approach for those cases. It breaks my brain and raises doubts...
    Ty !

    • @JoshuaMorony
      @JoshuaMorony  ปีที่แล้ว +1

      Personally if I am dealing with loading/success/failure sorts of states I like to use NgRx Component Store - but for a more simple approach you could do something like have an ngIf tied to the observable stream, and then provide an "else" template for loading that is displayed if the ngIf fails (which it will if the observable stream has not emitted yet). I've got some other videos on this.
      Different ways you can go about handling errors - again, Component Store provides some nice patterns for this and is generally what I would prefer, but there are also nice patterns like having a separate stream specifically for errors - Again, I have some more videos on both of these topics
      It depends on the situation, but generally if I had some child component I would have the parent component pulling in the stream from the service, and then passing that child component values from the stream as an input (using the async pipe)

    • @AdrienSAULNIER
      @AdrienSAULNIER ปีที่แล้ว

      ​@@JoshuaMorony Thank you for your answers 🙏
      About the ngIf, I don't know If I do wrong, but I'm always use a callState$ string obs which reflects the http call status, then display a template depends from its value (instead of dealing with properties such as "loading", "loaded", ...). What's your opinion about it ?
      The ngIf seems good in case you have a simple data to load just one time, but when you're working with paginated data by example, you need to show "loading" template after each page changes...
      Is extends ComponentStore into a Service provided in root a good practice, in some cases ? By example, if you want to share some states from multiple components which they're not in the same tree ?
      And the last question: Let's imagine we are a three level component tree: Would you be shocked if thethe 1rst include your ArticlesService and the 3rd too? Knowing that the 3rd has complex actions and uses other observables than the 1rst.
      Ty for your time :)

  • @will-ever
    @will-ever 8 หลายเดือนก่อน

    Would public *readonly* articles$ be better in the service than a private one with a method? For the cases when data is requested only in init, of course...

  • @kflo411
    @kflo411 2 ปีที่แล้ว +3

    You should add a note about how you go against the grain in your naming of the BehaviorSubject variable: the dollar sign suffix is by-and-large used just for Observables.
    The BehaviorSubject of course is an observable by nature, but even you made an Observable/Subject distinction in your getter method. This is another distinction worth at least noting.

    • @JoshuaMorony
      @JoshuaMorony  2 ปีที่แล้ว +2

      Interesting, I've never considered this to be honest - I've been quite intentionally using the dollar sign suffix for BehaviorSubjects and was under the impression this is what most other people were doing too (it's also what seems to make the most sense to me too), but I could certainly be wrong about that.

  • @KeganVanSickle
    @KeganVanSickle 2 ปีที่แล้ว

    Observables and updating their state is how I manage large datasets.

  • @pawekoaczynski4505
    @pawekoaczynski4505 ปีที่แล้ว +4

    2:15 I'm pretty sure you are still able to call the `next` method.
    Heah, you will get TypeScript error, but you can make it go away with @ts-ignore. I believe that adding the type annotation doesn't really do to the underlying object's structure - it still has the same methods, TypeScript doesn't make the fields private.
    Combined with explicitly adding return type makes me don't really like this solution. Though I can see other people seeing my comment as a nitpick :p

    • @nektworks
      @nektworks ปีที่แล้ว +3

      This is correct.
      The getArticles function returns a BehaviorSubject. The reason TypeScript isn't erroring this is because technically, the BehaviorSubject class extends Subject class, which extends Observable class, which means that a BehaviorSubject can act as an Observable.
      Instead of creating a getArticles function, you can also use the asObservable function for your BehaviorSubject, like this:
      private articlesSubject = new BehaviorSubject;
      public articles$ = articlesSubject.asObservable();

    • @bamboo6044
      @bamboo6044 ปีที่แล้ว +1

      @@nektworks I just wanted to comment the same, also, no need for public keyword as props are public by default.

  • @kunalvirk
    @kunalvirk ปีที่แล้ว

    I tried this but struggling to make it work. So I have about four services `UserInfo`, `TeamService`, `ProjectService` & `FilterService`. They depend on each other in the mentioned order i.e. `FilterService` depends on `ProjectService` which depends on `TeamService` which depends on `UserInfo`. `UserInfo` does the authentication via Auth0. So in the application when I try to get userInfo, I get undefined (ofcourse it is all asynchronous) but how to make it work. I don't want to introduce my application to NgRx if it can be handled by angular. Can you please help on this?

  • @quickmaths4762
    @quickmaths4762 ปีที่แล้ว

    Why not start behavioursubject with null? It's less ambiguous imo. Articles could be empty when arrived. But null only before arrived @Joshua

  • @123vimalan
    @123vimalan 2 ปีที่แล้ว

    Please do a video in Ngrx.

    • @JoshuaMorony
      @JoshuaMorony  2 ปีที่แล้ว

      A couple of my recent videos are on NgRx :)

  • @FrancescoDeGiorgio
    @FrancescoDeGiorgio 2 ปีที่แล้ว

    One question:
    I have the following flow:
    - a service which reads "news" items from a database,
    - the "news" interface with title, text, image, etc.
    - the "homepage" view (ts & html)
    if I use a local json file and I update one of its values, it automatically update the view with the new values but, if I read the news-items from a database the only way to get the updates values is to call an "update" function every X seconds or entering the view again.
    There is any chance that it takes automatically the update values from the database or to automate the refresh-call ? (something similar happens with firebase if i'm not wrong)

    • @JoshuaMorony
      @JoshuaMorony  2 ปีที่แล้ว +2

      You can do polling with RxJS quite nicely if you want, the basic idea would be to use a 'timer' creation operator from RxJS which is basically just like a setInterval. Let's say the timer emits every 2 seconds, you could then use a switchMap operator to switch that timer observable stream to the observable stream from your HTTP request, which would return you the results from the server.
      This is certainly doable, but you might also want to investigate other ways to achieve this without needing to poll a server, but that would lie outside of RxJS (e.g. maybe you use a web socket and then integrate that with your stream, or maybe you have something like Firestore which can also provide a stream of updates).

  • @JZubero
    @JZubero 2 ปีที่แล้ว +1

    Do you see any strong benefit in using the BehaviorSubject opposed to a regular Observable with the shareReplay and startWith operators in your specific scenario? Of course, if you'd be pushing new data from different "places" inside of your service, the Subject-based approach would be the way to go. And: +1 for some NgRx vids! Thanks ✌

    • @JoshuaMorony
      @JoshuaMorony  2 ปีที่แล้ว +4

      The key benefit to BehaviorSubject here is that it gives you that initial value and will always supply the last value, so that's probably easier than manually using additional operators to achieve the same thing, but of course you can do whatever suits your circumstance best or just what you like doing - the main idea is that you can get an observable stream of data from your service, you could create that stream however you like.

    • @hansschenker
      @hansschenker 2 ปีที่แล้ว

      you write less code - less code is less possibiities for errors!

    • @JZubero
      @JZubero 2 ปีที่แล้ว

      ​@@hansschenker Transforming the stream of the HTTP request with shareReplay and startWith - instead of handling the BehaviorSubject - could actually reduce the lines of code needed. But as Josh said, it really depends on the specific use case. Still agreeing on your statement about less code, less bug potential 🙃

  • @FinanzMinimalist
    @FinanzMinimalist ปีที่แล้ว +1

    I don't think this is (not yet) a state management system.
    1. You miss a function to set the state from the outside.
    2. You miss the getValue method of BehaviorSubject to get the last emitted value directly (important for usage in components).
    It is more like a better way of getting data from an api and present it as an BehaviorSubject + add a function for filtering.
    Btw: I really like your videos :)

  • @user-jtwe1xrf2n
    @user-jtwe1xrf2n ปีที่แล้ว

    What about the subscription inside the ArticlesService? Are you not unsubscribing from that?

    • @JoshuaMorony
      @JoshuaMorony  ปีที่แล้ว

      Technically it would be safer to do this and I would generally advise it, but an observable from the HTTP client will complete after one emission (although I think still piping take(1) to be explicit is worthwhile). In a more robust example, I would generally want to run initialisation logic like this inside of something like a NgRx Component Store effect.

  • @mfpears
    @mfpears ปีที่แล้ว +1

    8:30 It won't scale well without selectors, no matter what rules you follow.

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

    I don't get the purpose of declaring articles$ private then exposing it unmodified with the getArticles,..yes typescript may fail to compile but the returned value is technically a BehaviorSubject and you'd still be able to call next() and emit a new value from outside. A safer option is to return articles$.asObservable()

  • @ValerioComo
    @ValerioComo ปีที่แล้ว

    I think init method is an anti-pattern

  • @Don_Giovanni
    @Don_Giovanni 2 ปีที่แล้ว

    What's your reasoning for subscribing to an observable and then creating a new one in a service? IMO the beauty of rxjs lies in the pipe methods that let you manipulate the data without the need to break the chain along the way. And if you need to get a value on subscription you can just use one of the replay methods, afaik.

    • @JoshuaMorony
      @JoshuaMorony  2 ปีที่แล้ว

      The point in the video was to demonstrate that we can get data however we want - sync or async - and then just emit that on the BehaviorSubject when it's ready. I do agree with what you're saying though, the fact that I used an observable to get the data to emit on the BehaviorSubject could be confusing. If I wasn't just trying to show how BehaviorSubject works ideally I would
      1) Have a articles$ member variable to store the HttpClient observable
      2) Call getArticles which will either set articles$ to the stream returned from HttpClient if it isn't defined yet, or if it has just return articles$ directly
      3) When creating the HttpClient stream, pipe the shareReplay operator so that future requests for articles$ also return the value from the server
      But I don't think explaining all of that would've been a good idea for this video since it is more beginner oriented - coming up with simple self contained examples with no flaws is hard :)

  • @ps-pu5dd
    @ps-pu5dd ปีที่แล้ว

    ng rx no? bra

  • @davidNelson8875
    @davidNelson8875 5 หลายเดือนก่อน

    It is not recommended to call next of another subject inside subscribe

  • @EduardLepner
    @EduardLepner 2 ปีที่แล้ว +4

    Not only this video useless for an experienced developer but also harmful for the beginners. This approach completely ignores the huge power of rxjs with caching with shareReplay and startWith which is stated in the comments below but also introduces code smell in init() method which shifts this responsibility to UI component thus making such service way worse than a 'classical' one.

    • @JoshuaMorony
      @JoshuaMorony  2 ปีที่แล้ว +2

      Unless I'm mistaken, BehaviorSubject already provides a hot observable so the responses in this case would be cached/shared by default (my understanding is a BehaviorSubject is basically just a shortcut for Subject + shareReplay + startWith). I do have a more advanced article on caching with observables here: eliteionic.com/tutorials/caching-and-sharing-firestore-data-with-rxjs-sharereplay/ but it's not really the point of this video.
      Maybe I'm wrong to teach it this way, but my opinion is that it's fine to provide a simplistic example to teach a concept and that making everything adhere to best practices can get in the way of the learning for beginners - I want to introduce a couple of concepts at a time not everything at once. I agree using an init method here is not ideal, but I think it makes the example easier. If I want to do videos on more advanced things I will generally make a video that focuses on just that, like the concepts you are talking about: th-cam.com/video/H542ZSyubrE/w-d-xo.html
      Anyway I've got a comment similar to yours a couple of times so I'll pin this so people can see this extra detail if they want.

    • @EduardLepner
      @EduardLepner 2 ปีที่แล้ว +1

      @@JoshuaMorony Yes, indeed, BehaviourSubject provides last emitted value to the observers but if it was your intent init() method should have been declared as private and used in the constructor of that service (which leads the data to be preloaded, ofc). Otherwise, the first consumer of that service (HomePage in your example) must somehow guess that it's its responsibility to call this method. And if the application is made of chains services calling each other it might result in very hard to troubleshoot problem when everything works fine but results don't show up.
      I agree that beginner tutorials should be made of simple examples but this particular example just kills the whole point of rxjs in Angular and leads to incorrect overall architecture understanding. Sorry, if I seemed rude but I should have stated that :)

    • @JoshuaMorony
      @JoshuaMorony  2 ปีที่แล้ว +2

      ​@@EduardLepner All good! I think conversations like this can be great to help add colour to a simple video if it's kept constructive. If I were to go with the approach of an "init" method in a real application (which I generally wouldn't, but I don't think it is necessarily wrong) I would have it be called from the root component so that it is initialised when the application is started. I think this is preferable to using the constructor as it is generally best to avoid doing work in constructors wherever possible - primarily because it can make testing difficult.
      What I would actually do in a real application is have the initialisation happen whenever the getArticles method is first called - although this might depend on what the method is supposed to do exactly, if it needs to load data from a server then I might want to trigger it earlier, in which case I might actually rely on an init method.

    • @gildas_dev
      @gildas_dev 2 ปีที่แล้ว +1

      @@JoshuaMorony Personally I use to put the init in the constructor. The first time the constructor is called is the first time the service is used because of DI. This way it is safe to manage lazy loading errors I think.

    • @JoshuaMorony
      @JoshuaMorony  2 ปีที่แล้ว +2

      @@gildas_dev I think it's fine to do it that way if that's what you want - the only real downside (at least the only one I can think of) is that in a test environment it is going to mean that creating the service is going to trigger anything you have in the constructor. If it's not in the constructor then it can make things easier to test.