Thanks so much Rainer for your videos. This one was incredibly helpful. I just follow your steps to create a signal Store extension and was so great... I really appreciate your effort doing those videos. Thanks!
Still wrapping my head around using the signal store. However, the presentation is good. Thank you for sharing the code to allow for a slow digestion of information.
@@Petre66CA The EventEmitter extends from a Subject and you should only use it for dispatching Events to be consumed by the parent component. You might want to compare a Signal to BehaviorSubject or Observable in general. I highly recommend you to read github.com/angular/angular/discussions/49684 where they explain why Signals at all and how they are different from RxJs.
I really like the general approach with having the state out of the component and quite minimal with signalstore compared to that huge boilerplate code you have to write for standard state management with ngrx.
@@RainerHahnekampI am also very happy to find your channel. I appreciate the way you explain advanced topics and I'm looking forward to more content on building systems like esbuild, and the usage of design patterns in Angular (Adapter, Composite, Facade, etc).
@@nnaluekenneth8420 Thanks, there is a video covering the Facade in NgRx. I would also be interested to cover build systems. Not just esbuild, but how it works together with Vite and what other parts are relevant in this area.
If I use an exhaustMap and switch quickly between page 2, 3 and 4, it could happen that requests for 3 and 4 are not processed. exhaustMap waits for the inner observable to complete and ignores all incoming values from the outer observable. So for pagination, you usually go with switchMap.
I don't see any benefit in using Selenium anymore. I've been using Selenium (Selenide) for so many years, and we had to deal with so many flaky tests that we gave up one time. When Cypress came out, we gave E2E testing with Cypress another chance and were more than happy. Since then, I've never heard that somebody really enjoyed working with any webdriver-based derivate. Since I would not use it, I don't want to make a video about it. I know that there is now WebDriver Bidi. It has been hailed to bring the same quality as CDP (Cypress, Playwright) but download statistics show a completely different picture. Did I miss anything, respectively is there a particular reason why you are asking?
@@RainerHahnekamp thank you. I asked because a lot of testers QA teams use Selenium from what I see in jobs posts and I wonder why it was not used with Angular in any of your videos, and you are the best person to ask for it when talking about testing 🙂 .Have a great day
@@etexalbania7301 Yes, you also have remember that Selenium was the standard framework for more than a decade. In huge environments you don't change that quickly. So I don't think that there will be demand for Selenium developers in the future as well. For new projects, though, Selenium doesn't seem to be the first choice anymore (diplomatically said).
Thanks, I'm quite satisfied with the current progress of my channel. I am getting more and more subscribers each day. Would have never thought that I reach 1,000 but I'm almost at 3 now. I think it is just a question of time and consistency.
Always appreciate your videos! I adopted the not-loaded, loading and loaded paradigm from watching your videos as I was learning about state management in Angular. I had a few thoughts that I wanted to share: - The auto-suggestion feature in the IDE you are using makes it difficult to follow along. As a viewer, we are watching and the code suggestions appear and then disappear and it forces some viewers to have to scratch their head, pause the video, backtrack and watch a section over and over. Disabling it in the IDE when making videos should make it much easier for all viewers, not just experienced developers and/or those who are familiar with the IDE you are using, to follow along. - I have only watched about the first half of this video, but I know that I would really love to see an example from scratch that covers managing state client-side and how to handle effects. Do we first update the state client-side and then make an external call (to an api)? Is it possible (and is it a good architecture) to use the NOT_LOADED, LOADING and LOADED paradigm that was used previously in the rxjs variant of NgRx? Thanks again, much appreciated, you offer really good thoughts about architecture and design! - raj
Hi Raj, thanks a lot for your comments. I'm not sure I understand your point about the IDE auto-suggestions. Could you maybe give me an example from the video? If it helps I can of course disable whatever feature I'm using there. You can change the state first and then do the backen call. That's what we call optimistic locking. It brings better user experience but makes it harder for you. You always have to provide a function which reverts the state change if something goes wrong. It only makes sense for simple changes. If a single action triggers changes in different entities, it is way more difficult to provide a revert logic, so we usually want to send the request there first and wait for the response before updating the state. I would definitely recommend to use the LoadStatus pattern which I presented in one of my videos for the global store. Definitely do that! All the best, Rainer
Very nice and clear, to the point. Thanks a lot. So is this more or less the end of ngrx/store and will the community move to SignalStore for global state?
Thanks I can't predict 100% the future, but yes. Also the NgRx team says (talk at ng-conf) that SignalStore is the way to go. Only, if you absolutely must use the Redux pattern, then the global State is an option.
A really nice demonstration. I have a question, If I have 2 signalStore for Customer and User for 2 different pages. I have a global loading bar, how can I apply isLoading on the global component
Hi, please take a look at my example. I have an http interceptor which sets the loading status in a service which is then read by the global component. If that doesn't answer your question, let me know and I can provide alternatives.
@@RainerHahnekamp I see that interceptor but sometimes we have calls that we don't want to show a loading bar. I have just come up with the idea of making a LoaderStore with isLoading property. Inject this store into withMethods of each Entity store where I want to show or hide the loading bar. This LoaderStore will be injected in LoaderComponent and bind isLoading into our HTML element. I am not sure if this is also the correct way to do it.
@@SonPham-zy2zpIf it is just about being able to exclude certain requests from not showing the loader spinner, please take a look at the HttpContext. Whenever you execute an HTTP request you have the option to add contextual data to the request which could be read by the interceptors. For example I use the context for a specific error message. You can find it at github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/shared/http/error-message.context.ts. If you need more option, then going with a service as you did is definitely something I would also then. If it is just about a simple property, I think I would skip the Signal Store for that and just use a simple Signal.
@@RainerHahnekamp It's nice to see a small channel with such a great quality and you have potential to grow. I've invested on youtube a few years ago, you can check out my channel containing angular and other nodejs content, but it's all in Portuguese. youtube.com/@fmaransatto
I'm always thinking about the glitch-free-effect. What if I call a method for example twice with different inputs? Will the first one be gone? What if I want to use the signalstore for events . I fill a buffer with events from a websocket. Will I lose data? I really like the signal store, but I can't get my head around this. :o
So if you call a method twice synchronously, the first one will be dropped. WebSocket messages usually arrive asynchronously, so they won't be dropped by the glitch-free effect. You can use the SignalStore for events with rxMethod, but you have to think what type you use to represent that event. I would avoid putting an event into as Signal, usually the events comes from an Observable or an event listener (also works without rxMethod). Observables and event listeners are both fine. If you end up having dependencies between signal stores, then you will very quickly have an event in the form of a Signal. You might now be worried about the glitch-free effect, but from my experience, it is very rarely a real issue.
Hi Rainer! Thanks a lot for sharing this knowledge! Just a question... How can the signalStores communicate with each other ? I often use effects and actions between several stores to manage the global state of applications... Is this still possible?
Sure, the bookingStore for example has a dependency to customersStore: github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts. You just inject whatever you require, and that should do it.
Great video. Thank you for the amazing content. I am wondering how to update the store based on a signal input of the component. Let's assume having an input "id". Whenever the "id" changes I would like to update the store. How would this be done?
Hi Lukas, that’s where you use rxMethod. If you have an rxMethod of type number for example, it doesn’t just accept a number as parameter but also a signal of oberservable of type number. You can an example here: github.com/rainerhahnekamp/eternal/blob/talk/ng-india-2024-solution/src/app/holidays/feature/quiz/quiz.component.ts
Thank you for the fast reply. I really appreciate it. Makes sense. That’s how I did it in the end anyway. I was just not sure if it is the best way to do it as I initially thought rxMethod is only necessary working with rxjs. In this case the only operator needed is tap to patch the state. But it works fine. 👍🏻 Originally I used an effect within the component but this resolved in some errors. I just wanted to mention it if someone else also experiences this “problem”. Anyway. Thanks for the response and the great content. Keep it going. ✌🏻☺️
Hi Rainer, thanks a lot for sharing! I often use actions and effects to manage global UI state for example. Is this still possible with NgRx signalStores? Can signalStores communicate with each other?
Hi, thanks. So the way how SignalStore can communicate with each other is via the dependency injection. You can find in example at: github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts The bookingStore has a dependency to the customersStore. Reducer & Actions are part of the Redux pattern and not supported in the Signal Store. You could use our Redux extension: github.com/angular-architects/ngrx-toolkit, stick to the global store, or try to do it without Redux. Does that answer your question?
Great example. What if my load method takes 2 more parameters they are signals? Do i need separate state for that? would my load method will call when those values changes? Currently i am using angular effects with signals and effects for loading data gets called when any of those values changes.
Thanks. If the parameteruses is a Signal, rxMethod applies an effect to it. If it is an Observable, it subscribes to it. Every time an Observable emits or Signal emits (careful with glitch-free), the inner Observable is executed. To put it short for Signals: rxMethod is just a wrapper around the effect function with an untracked in it. Did I answer your questions?
@@RainerHahnekamp sorry for the typo, I meant "data". I considered for example such as restaurant rating. It is quite simple to define a state (rest. name, photo, rating, etc.), load it at the beginning and show the state in template. But what should happen when user is rating restaurant by clicking 4th star? Should store be somehow involved in passing data (vie REST) to server? Or should I update data in store and in database separately?
@@walterluszczyk Yes, the store is responsible for calling the backend. Instead of patchState, the store would have a method called 'updateRating'. In that method, the store calls the API and then updates the store via patchState.
Hi Rainer, when patching state in the store if you have nested objects once you go to the second level object you must supply all parameters not just the attributes that have changed, is this a limitation of the patch state concept?
Yes, exaclty. So the patchState refers only to the first level. A possible alternative might be patchState(store.nestedProperty, {foo: 1}) but that does not exist.
Hi, thank you for the content I would like to ask if we can use store methods inside rxMehtod? For example, if I need to implement a search and in case of an empty query, just call the loadAll() method, how can I access it from inside rxMethod?
Hi, can you give me a more concrete code example? You have direct access to your store in an rxMethod. So it shouldn't be an obstacle, but I think you asking for something more specific.
@@RainerHahnekamp *TH-cam is deleting my comment for some reason I'll split it into two parts Yes, you are right, I have access to store inside rxMethod, but it is access to its properties that are declared in initialState. I don't have access to the methods themselves. Here is a simple code sample where you can see what the problem is: const initialState: IUsersState = { users: [], isLoading: false, error: null, } export const usersStore = signalStore( { providedIn: 'root' }, withState(initialState), withMethods((store) => ({ generateUsers() { return [ { name: 'John' }, { name: 'Marry' } ]; }, loadAll() { const users = this.generateUsers(); patchState(store, { users }); }, search: rxMethod( pipe( debounceTime(500), distinctUntilChanged(), switchMap((query) => { if (!query) { this.loadAll(); // TS2532: Object 'this' is possibly undefined //OR store.loadAll(); // TS2339: Property loadAll does not exist on type // { // users: Signal; // isLoading: Signal; // error: Signal; // [ STATE_SIGNAL ] // : // WritableSignal; // } } // ...rest of code }), ), ), })), );
As you can see, I have no problem accessing generateUsers() inside loadUsers() via 'this'. But in rxMethod the context is lost, so this.loadAll() displays an error because 'this' === undefined. Also when using store.loadAll() inside rxMethod I get an error that such a method does not exist on my store type (but still I have an access to 'users', 'isLoaing' and 'error'). Now I've found a solution that looks something like this: export const usersStore = signalStore( { providedIn: 'root' }, withState(initialState), withMethods((store) => { function generateUsers() { return [ { name: 'John' }, { name: 'Marry' } ]; } function loadAll() { const users = generateUsers(); patchState(store, { users }); } const search = rxMethod( pipe( debounceTime(500), distinctUntilChanged(), switchMap((query) => { if (!query) { loadAll(); } // ...rest of code }), ), ); return { loadAll, search }; })); I hope my explanation is clear😅
Hmmm, with anglar 18, adding @ngrx/signals fails with resolve dependency errors (both with ng add and npm install). Are we expecting a bump in the ngrx signals soon, or is there a workaround? does npm i --force work properly in this case?
Hi Rainer, another challenge I have encountered is how do you initiate a method call on a component signalStore via a signal state change in a global signalStore? My scenario has a CompanyId on the global signalStore which can be changed by the user at any given point, there are then a collection of chart components which needed to be refreshed but I can't work out how I would do that other than using an effect which firstly causes a problem because by default you can't change signal states within them and I don't want to override that behaviour it suggests I'm doing something wrong!
Hi, you do that via rxMethod. rxMethod would trigger a method of a local signalStore whenever the the selector/signal of the companyId changes. Do you have some code snippt, so that I get a better impression on what you do?
I have my rxMethod that loads the chart data and I call that on the ngOnInit of the chart component and the data loads however, how do I call that method again when the CompanyId changes externally?@@RainerHahnekamp
@@RainerHahnekamp not sure what happened I responded to this an hour ago and now it's disappeared! Anyhow, I've got a solution working by converting my authStore.companId signal to an observable and subscribing to that in my component constructor e.g. this.companIdChangedSubscription = toObservable( this.authStore.companyId, ) .pipe(tap(() => this.loadChartData())) .subscribe(); However, I thought I'd seen the back of subscribing and unsubscribing, or is there another way?
signal() & computed(): yes (since Angular 17) effect(): no (developer preview) NgRx Signal Store: no (developer preview) I'd like to add that developer preview does not mean that it is untested and you to expect bugs, like in a beta version. Developer Preview means it is quite stable but they can introduce breaking changes within a major version.
@@MahmoudTarek-pz1rl You're welcome. I wouldn't expect that the SignalStore will get so many new features. Maybe some extensions like withEntities but not in its core.
@RainerHahnekamp I totally agree with you. But I was sure that something related to deal with entities will have a place with signal store, whatever how. But, for me the surprise and it was the first time I see it I even didn't came across it was signalStoreFeature it is really powerful feature to have, extensibility in this way and having a generic store features just so much powerful
great intro to SignalStore. It really looks more intuitive and yet equally powerful (if not more) than the original global store/ComponentStore. Do you know if the effect() API will be introduced to SignalStore once effect() is fully stable, for example, withEffects()? The current approach, withMethods + rxMethod seem not fully "signalized" to me, but I do think it is sufficient for almost all use cases
Thanks. I read up the documentation on SignalStore on NgRx and realized that the function generated by RxMethod can actually receive a static value (effect triggers only once), and a signal/observable (effect triggers on signal/observable update), and it auto-cleanup since it got access to DestroyRef in InjectionContext. Way more powerful than I initially thought. I look forwards to future videos on this very interesting SignalStore, perhaps on a higher architectural level, such as using it as global store, connecting and initializing component-level signalStore to global signalStore
@@vutruong4164 Yes, there is an upcoming video where I focus more on the role of the Signal Store to enhance Angular's Signals. I'll explain the rxEffect there more in detail. When it comes to global and local store, the repo of this video contains already a working example. github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts. You just inject like any other store.
@@RainerHahnekamp sorry for troubling you again, but is there still the concept of eagerly vs lazily initialing states with SignalStore? My understanding is withState will always eagerly initialize the store, so I probably have to initialize states with "undefined" if I want to "lazily" initialize states later by using a load method in withMethods
@@vutruong4164No worries at all. So as soon as you inject the Store for the first time, it will be initialized and it has to have state from the very beginning. You could use undefined, but I would provide default values and a property to my state that says if the state is ready or not. If you use undefined for all your properties, you always have those union types which are net very comfortable to work with. All components, all services have to deal with those.
Thanks for the video, how can you update multiple states let's say you have a component scoped state but you also want to update part of the global application state, with actions and reducers you can hook onto any action you wish and thus reduce multiple states, but I can't see how you would do that with Signal Store?
@RainerHahnekamp Yes but how do I provide access to both states, how do I provide that access, is it via the withMethods parameter list? if so how do I get the store instance to pass through?
@@RainerHahnekamp I think I've solved it, by exporting the type from my store e.g. "export type AuthStore = InstanceType;" I can then pass it into the withMethods function and use it with patchState! Not tested it yet though 🙂
@@andyhb1970That sounds a little bit unnecessary. If you want in your withMethod access to another store, then just use the inject function. You can an example here: github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts
@@RainerHahnekamp I've just removed the exported type and it's working fine! I must have had a funny five minutes or VS Code intellisense did!!! Yes you're right I can just inject any dependencies into the withMethods function.
In component store can create selector something like storeBSomeValue$ = this.select(this.#storeA.someValue$, (someValue) => someValue). Is it possible to provide state from other store? So I can get access to value from storeA using storeB. Thank you for nice video.
Sure, I've pushed a version which contains a bookingStore that requires data from the customersStore. Check it out here: github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts
@@RainerHahnekamp Hi. Its really bad, but I always payed a lot of attention to this so maybe other people wont be bothered so much. I treated video as nice introduction, but skipped some parts and filled gaps with articles due to this.
@@RainerHahnekamp Yes, the sound quality is not particularly good, but I don't mind. I appreciate much more the hard work put into the video and the valuable information, so the sound quality doesn't really matter. Thanks for the great video Rainer! I hope you had a wonderful Christmas. Wishing you all the best for the New Year and hope you continue to give us such good content :)
@@hille-hausonfire4338 Thanks both. I will manually create subtitles and will upload them. The automatic ones aren't that good. To me, it is important that you can understand what I'm saying. As soon as that's not the case anymore, I have to re-record it. And thanks for Christmas wishes. Hope you enjoyed it as well!
Hello Alex, sorry for the poor quality. It was due to a damaged wire of the microphone and I had to aggressively apply audio filters to remove the majority of the background noise. It won't happen again. Switched now to a wireless microphone. You can also watch the second Part which takes on a different perspective but with much better audio quality.
there is a solution to easily handle side effects with it ?? I have a case that one slice of a store, when updated, trigger a side effect in another slice to handle... it's like a domain event.
Yes there is. The function you generate with rxMethod can also accept an Observable or event a Signal. Example: const Store = signalStore(withMethods(store ({incrementer: rxMethod()}))); const store = new Store(); // default usage store.incrementer(1); // usage with Observable store.incrementer(of(1,2,3)); //usage with Signal const number = signal(1); store.incrementer(number); --- So you just provide your event in the form of an Observable and you're done.
If you mean the redux pattern of the globa store, then yes. If you say Signals will replace State management libraries then I would definitely say no. Because you have Signals and a structure that is suited for state management, you need a library on top of it. This is something which has already been confirmed by Angular itself. They said, the default signal function will not be enough as soon as your Signal contains larger values.
Sorry, I am aware of that and all my newer videos are using a much better microphone. You can also checkout the second part which has overlapping content with that one.
Yeah, I know. I come from a small village in East Austria where half of the population have that surname. What I know from ancestry research is that one Hahnekamp appeared there in the 17th century. I guess Hahnekamp got a little bit germanized. The "kamp" is still as it was but I don't think that "Hahne" is Dutch.
Hi, I've considered it already after that recording ;) All my newer videos are now done with a wireless mic. If you checkout the second part, you should hear that it is much better. I still hope, you could understand what I said.
Well, the SignalStore is still in developer preview. If you can provide an easier solution with the same kind of extensibility, we will be more than thankful.
Wow, great introduction to these amazing new functionalities! I will be signing up for more!
Thanks a lot
I learnt a lot of information from your video. I'll just go and apply everything now. Thank you for this full of information video, Rainer!
Thanks, happy to hear!
Thanks so much Rainer for your videos. This one was incredibly helpful. I just follow your steps to create a signal Store extension and was so great... I really appreciate your effort doing those videos. Thanks!
Hey, great to hear. Thanks as well.
A really great introduction to NgRx/SignalStore. Thank you for your time to put it up. I am planning to use it in my ongoing project.
Great, happy to hear that Sakar!
Great video, really unique content. Thank you!
Thanks a lot!
Wow, great video and great intro to SignalStore & Signals. I'm jumping right into this now! Thanks!
You're welcome. Btw. a second one is going to be published very soon but that's a secret 🤫
Rainer really good stuff. Didnt realize the encapsulation bit that I will not have with the SignalStore. Really good overview
Thanks a lot Dhaval. I hope that there will soon be an encapsulation feature available.
In the meantime, a PR is available: github.com/ngrx/platform/pull/4210. You can upvote if you are in favor of that feature.
Absolutely love SignalStore.
yeah, me too (guess that's not really a surprise)
Amaizing content! As always. Brilliant knowledge sharing. Thanks a lot!
Thanks as well, Yanek!
As always super valuable content, thanks Rainer! 💪
Thanks Emil
Still wrapping my head around using the signal store. However, the presentation is good. Thank you for sharing the code to allow for a slow digestion of information.
You're welcome. Is there something specific you are struggling with?
@@RainerHahnekampI am struggling with the deluge of new terms. How is a signal different from an event emitter in earlier Angular versions?
@@Petre66CA The EventEmitter extends from a Subject and you should only use it for dispatching Events to be consumed by the parent component. You might want to compare a Signal to BehaviorSubject or Observable in general. I highly recommend you to read github.com/angular/angular/discussions/49684 where they explain why Signals at all and how they are different from RxJs.
I did not know such docs existed, what a game changer. Thanks for the clue@@RainerHahnekamp
I really like the general approach with having the state out of the component and quite minimal with signalstore compared to that huge boilerplate code you have to write for standard state management with ngrx.
Yes, me too. It makes things a lot of easier.
Glad have discovered your channel very educative. Waiting more knowledge sharing.
Thanks Denis, what kind of content would you expect?
@@RainerHahnekampI am also very happy to find your channel. I appreciate the way you explain advanced topics and I'm looking forward to more content on building systems like esbuild, and the usage of design patterns in Angular (Adapter, Composite, Facade, etc).
@@nnaluekenneth8420 Thanks, there is a video covering the Facade in NgRx. I would also be interested to cover build systems. Not just esbuild, but how it works together with Vite and what other parts are relevant in this area.
@20:16 I believe exhaustMap() should be used over switchMap(). switchMap() would restart the stream rather than ignoring subsequent ones
If I use an exhaustMap and switch quickly between page 2, 3 and 4, it could happen that requests for 3 and 4 are not processed. exhaustMap waits for the inner observable to complete and ignores all incoming values from the outer observable. So for pagination, you usually go with switchMap.
This is great, got a bit tricky at the end, but thanks a lot
Yeah, I know. Extensibility is the most powerful feature but also the hardest.
thank you for the video, a question about testing, why haven't you done a video for angular testing with Selenium?
I don't see any benefit in using Selenium anymore. I've been using Selenium (Selenide) for so many years, and we had to deal with so many flaky tests that we gave up one time. When Cypress came out, we gave E2E testing with Cypress another chance and were more than happy. Since then, I've never heard that somebody really enjoyed working with any webdriver-based derivate. Since I would not use it, I don't want to make a video about it.
I know that there is now WebDriver Bidi. It has been hailed to bring the same quality as CDP (Cypress, Playwright) but download statistics show a completely different picture.
Did I miss anything, respectively is there a particular reason why you are asking?
@@RainerHahnekamp thank you. I asked because a lot of testers QA teams use Selenium from what I see in jobs posts and I wonder why it was not used with Angular in any of your videos, and you are the best person to ask for it when talking about testing 🙂 .Have a great day
@@etexalbania7301 Yes, you also have remember that Selenium was the standard framework for more than a decade. In huge environments you don't change that quickly. So I don't think that there will be demand for Selenium developers in the future as well. For new projects, though, Selenium doesn't seem to be the first choice anymore (diplomatically said).
Thank you very much. ❤
You are very welcome!
Great Video - asking myself why you don't have more subscribers.
Thanks, I'm quite satisfied with the current progress of my channel. I am getting more and more subscribers each day. Would have never thought that I reach 1,000 but I'm almost at 3 now. I think it is just a question of time and consistency.
@@RainerHahnekamp with such good content, you will grow quickly. i will be visiting more often now.
@@koern82Thank you, if you want you can also help me by recommending this channel to your peers. 😀😅
Thank you for sharing the content. A question would be with ngrx effects?
@@rafaelcarvalho2918 yes, what question do you have about the ngrx effects? The SignalStore doesn’t have them.
@@RainerHahnekamp Does the way we implement change?
@@rafaelcarvalho2918 If you mean if you still have to differentiate between reducer and effect: No. The SignalStore does not follow the redux pattern.
YOUR CROWN HAS FALLEN, KING 👑👑
It falls slowly but it the crown stays within the dynasty
This is is amazing! Thanks for the video!
You are welcome. Btw, it has been released in 17.1 this week.
Always appreciate your videos! I adopted the not-loaded, loading and loaded paradigm from watching your videos as I was learning about state management in Angular.
I had a few thoughts that I wanted to share:
- The auto-suggestion feature in the IDE you are using makes it difficult to follow along. As a viewer, we are watching and the code suggestions appear and then disappear and it forces some viewers to have to scratch their head, pause the video, backtrack and watch a section over and over. Disabling it in the IDE when making videos should make it much easier for all viewers, not just experienced developers and/or those who are familiar with the IDE you are using, to follow along.
- I have only watched about the first half of this video, but I know that I would really love to see an example from scratch that covers managing state client-side and how to handle effects. Do we first update the state client-side and then make an external call (to an api)? Is it possible (and is it a good architecture) to use the NOT_LOADED, LOADING and LOADED paradigm that was used previously in the rxjs variant of NgRx?
Thanks again, much appreciated, you offer really good thoughts about architecture and design!
- raj
Hi Raj, thanks a lot for your comments.
I'm not sure I understand your point about the IDE auto-suggestions. Could you maybe give me an example from the video? If it helps I can of course disable whatever feature I'm using there.
You can change the state first and then do the backen call. That's what we call optimistic locking. It brings better user experience but makes it harder for you. You always have to provide a function which reverts the state change if something goes wrong.
It only makes sense for simple changes. If a single action triggers changes in different entities, it is way more difficult to provide a revert logic, so we usually want to send the request there first and wait for the response before updating the state.
I would definitely recommend to use the LoadStatus pattern which I presented in one of my videos for the global store. Definitely do that!
All the best,
Rainer
Very helpfull thanks a lot 😀
You're very welcome
Very nice and clear, to the point. Thanks a lot. So is this more or less the end of ngrx/store and will the community move to SignalStore for global state?
Thanks I can't predict 100% the future, but yes. Also the NgRx team says (talk at ng-conf) that SignalStore is the way to go. Only, if you absolutely must use the Redux pattern, then the global State is an option.
if possible , can you please shared repo link ?
Sure, there you go: github.com/rainerhahnekamp/ngrx-signal-store-demo
A really nice demonstration. I have a question, If I have 2 signalStore for Customer and User for 2 different pages. I have a global loading bar, how can I apply isLoading on the global component
Hi, please take a look at my example. I have an http interceptor which sets the loading status in a service which is then read by the global component. If that doesn't answer your question, let me know and I can provide alternatives.
@@RainerHahnekamp I see that interceptor but sometimes we have calls that we don't want to show a loading bar. I have just come up with the idea of making a LoaderStore with isLoading property. Inject this store into withMethods of each Entity store where I want to show or hide the loading bar. This LoaderStore will be injected in LoaderComponent and bind isLoading into our HTML element. I am not sure if this is also the correct way to do it.
@@SonPham-zy2zpIf it is just about being able to exclude certain requests from not showing the loader spinner, please take a look at the HttpContext. Whenever you execute an HTTP request you have the option to add contextual data to the request which could be read by the interceptors. For example I use the context for a specific error message. You can find it at github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/shared/http/error-message.context.ts.
If you need more option, then going with a service as you did is definitely something I would also then. If it is just about a simple property, I think I would skip the Signal Store for that and just use a simple Signal.
Awesome video, my only suggestion is to adjust your microphone equalizer and make it more acute, remove the bass, it's a bit difficult to understand.
Thanks a lot. After that recording I bought a wireless microphone. Sorry for the bad audio quality. Will not happen again :)
@@RainerHahnekamp It's nice to see a small channel with such a great quality and you have potential to grow. I've invested on youtube a few years ago, you can check out my channel containing angular and other nodejs content, but it's all in Portuguese. youtube.com/@fmaransatto
@@RainerHahnekamp let me consider reproducing a similar content but in Portuguese
I'm always thinking about the glitch-free-effect. What if I call a method for example twice with different inputs? Will the first one be gone?
What if I want to use the signalstore for events . I fill a buffer with events from a websocket. Will I lose data?
I really like the signal store, but I can't get my head around this. :o
So if you call a method twice synchronously, the first one will be dropped. WebSocket messages usually arrive asynchronously, so they won't be dropped by the glitch-free effect.
You can use the SignalStore for events with rxMethod, but you have to think what type you use to represent that event. I would avoid putting an event into as Signal, usually the events comes from an Observable or an event listener (also works without rxMethod). Observables and event listeners are both fine.
If you end up having dependencies between signal stores, then you will very quickly have an event in the form of a Signal. You might now be worried about the glitch-free effect, but from my experience, it is very rarely a real issue.
Hi Rainer! Thanks a lot for sharing this knowledge! Just a question... How can the signalStores communicate with each other ? I often use effects and actions between several stores to manage the global state of applications... Is this still possible?
Hey Arnold, I have already answered your question below. I think you asked twice :)
@@RainerHahnekamp Sorry Rainer! My first comment didn't show up when I submitted it, so I wrote another one. 😅
@@arnoldm5358I see, all good!
Can I combine stores? For example, to retrieve all products of the logged in user?
Sure, the bookingStore for example has a dependency to customersStore: github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts.
You just inject whatever you require, and that should do it.
Which font and theme are you using in this ?
The font is cascadia code pl but the rest is standard IntelliJ
Great video. Thank you for the amazing content. I am wondering how to update the store based on a signal input of the component. Let's assume having an input "id". Whenever the "id" changes I would like to update the store. How would this be done?
Hi Lukas, that’s where you use rxMethod. If you have an rxMethod of type number for example, it doesn’t just accept a number as parameter but also a signal of oberservable of type number. You can an example here: github.com/rainerhahnekamp/eternal/blob/talk/ng-india-2024-solution/src/app/holidays/feature/quiz/quiz.component.ts
Thank you for the fast reply. I really appreciate it.
Makes sense. That’s how I did it in the end anyway. I was just not sure if it is the best way to do it as I initially thought rxMethod is only necessary working with rxjs. In this case the only operator needed is tap to patch the state. But it works fine. 👍🏻
Originally I used an effect within the component but this resolved in some errors. I just wanted to mention it if someone else also experiences this “problem”.
Anyway. Thanks for the response and the great content. Keep it going. ✌🏻☺️
@@LukasMachetanz Thanks, I will 💪👍
Great content.
Thank you so much.
Thanks and you are very welcome!
Hi Rainer, thanks a lot for sharing! I often use actions and effects to manage global UI state for example. Is this still possible with NgRx signalStores? Can signalStores communicate with each other?
Hi, thanks. So the way how SignalStore can communicate with each other is via the dependency injection. You can find in example at: github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts The bookingStore has a dependency to the customersStore.
Reducer & Actions are part of the Redux pattern and not supported in the Signal Store. You could use our Redux extension: github.com/angular-architects/ngrx-toolkit, stick to the global store, or try to do it without Redux.
Does that answer your question?
Great example. What if my load method takes 2 more parameters they are signals? Do i need separate state for that? would my load method will call when those values changes? Currently i am using angular effects with signals and effects for loading data gets called when any of those values changes.
Thanks. If the parameteruses is a Signal, rxMethod applies an effect to it. If it is an Observable, it subscribes to it. Every time an Observable emits or Signal emits (careful with glitch-free), the inner Observable is executed.
To put it short for Signals: rxMethod is just a wrapper around the effect function with an untracked in it.
Did I answer your questions?
Hi, thanks for grat tutorial. Furthermore. do you have any material which shows how to manage with saving data to DB?
Hi Walter, you are welcome. Do you mean Date or data?
@@RainerHahnekamp sorry for the typo, I meant "data". I considered for example such as restaurant rating. It is quite simple to define a state (rest. name, photo, rating, etc.), load it at the beginning and show the state in template. But what should happen when user is rating restaurant by clicking 4th star? Should store be somehow involved in passing data (vie REST) to server? Or should I update data in store and in database separately?
@@walterluszczyk Yes, the store is responsible for calling the backend. Instead of patchState, the store would have a method called 'updateRating'. In that method, the store calls the API and then updates the store via patchState.
Hi Rainer, when patching state in the store if you have nested objects once you go to the second level object you must supply all parameters not just the attributes that have changed, is this a limitation of the patch state concept?
Yes, exaclty. So the patchState refers only to the first level. A possible alternative might be patchState(store.nestedProperty, {foo: 1}) but that does not exist.
@@RainerHahnekamp interesting I see there's a similar issue raised in the component store on gh. Thanks for the feedback
Hi, thank you for the content
I would like to ask if we can use store methods inside rxMehtod?
For example, if I need to implement a search and in case of an empty query, just call the loadAll() method, how can I access it from inside rxMethod?
Hi, can you give me a more concrete code example? You have direct access to your store in an rxMethod. So it shouldn't be an obstacle, but I think you asking for something more specific.
@@RainerHahnekamp
*TH-cam is deleting my comment for some reason
I'll split it into two parts
Yes, you are right, I have access to store inside rxMethod, but it is access to its properties that are declared in initialState. I don't have access to the methods themselves. Here is a simple code sample where you can see what the problem is:
const initialState: IUsersState = {
users: [],
isLoading: false,
error: null,
}
export const usersStore = signalStore(
{ providedIn: 'root' },
withState(initialState),
withMethods((store) => ({
generateUsers() {
return [ { name: 'John' }, { name: 'Marry' } ];
},
loadAll() {
const users = this.generateUsers();
patchState(store, { users });
},
search: rxMethod(
pipe(
debounceTime(500),
distinctUntilChanged(),
switchMap((query) => {
if (!query) {
this.loadAll(); // TS2532: Object 'this' is possibly undefined
//OR
store.loadAll(); // TS2339: Property loadAll does not exist on type
// {
// users: Signal;
// isLoading: Signal;
// error: Signal;
// [ STATE_SIGNAL ]
// :
// WritableSignal;
// }
}
// ...rest of code
}),
),
),
})),
);
As you can see, I have no problem accessing generateUsers() inside loadUsers() via 'this'.
But in rxMethod the context is lost, so this.loadAll() displays an error because 'this' === undefined.
Also when using store.loadAll() inside rxMethod I get an error that such a method does not exist on my store type (but still I have an access to 'users', 'isLoaing' and 'error').
Now I've found a solution that looks something like this:
export const usersStore = signalStore(
{ providedIn: 'root' },
withState(initialState),
withMethods((store) => {
function generateUsers() {
return [ { name: 'John' }, { name: 'Marry' } ];
}
function loadAll() {
const users = generateUsers();
patchState(store, { users });
}
const search = rxMethod(
pipe(
debounceTime(500),
distinctUntilChanged(),
switchMap((query) => {
if (!query) {
loadAll();
}
// ...rest of code
}),
),
);
return { loadAll, search };
}));
I hope my explanation is clear😅
Hmmm, with anglar 18, adding @ngrx/signals fails with resolve dependency errors (both with ng add and npm install). Are we expecting a bump in the ngrx signals soon, or is there a workaround?
does npm i --force work properly in this case?
--legacy-peer-deps is the recommended option. See here: github.com/ngrx/platform/issues/4352
@@RainerHahnekamp Perfect, thanks for the reply. I have been greatly enjoying your videos on signals and the signal store.
@@davesharman8302 Thanks Dave. Happy to hear that 😄
Hi Rainer, another challenge I have encountered is how do you initiate a method call on a component signalStore via a signal state change in a global signalStore? My scenario has a CompanyId on the global signalStore which can be changed by the user at any given point, there are then a collection of chart components which needed to be refreshed but I can't work out how I would do that other than using an effect which firstly causes a problem because by default you can't change signal states within them and I don't want to override that behaviour it suggests I'm doing something wrong!
Hi, you do that via rxMethod. rxMethod would trigger a method of a local signalStore whenever the the selector/signal of the companyId changes.
Do you have some code snippt, so that I get a better impression on what you do?
I have my rxMethod that loads the chart data and I call that on the ngOnInit of the chart component and the data loads however, how do I call that method again when the CompanyId changes externally?@@RainerHahnekamp
@@RainerHahnekamp not sure what happened I responded to this an hour ago and now it's disappeared! Anyhow, I've got a solution working by converting my authStore.companId signal to an observable and subscribing to that in my component constructor e.g.
this.companIdChangedSubscription = toObservable(
this.authStore.companyId,
)
.pipe(tap(() => this.loadChartData()))
.subscribe();
However, I thought I'd seen the back of subscribing and unsubscribing, or is there another way?
@@RainerHahnekamp I keep replying and my reply just disappears :-(
@@andyhb1970 then write to me directly on LinkedIn or Twitter please
First question, are Signals ready for Production? 😅
signal() & computed(): yes (since Angular 17)
effect(): no (developer preview)
NgRx Signal Store: no (developer preview)
I'd like to add that developer preview does not mean that it is untested and you to expect bugs, like in a beta version. Developer Preview means it is quite stable but they can introduce breaking changes within a major version.
@@RainerHahnekamp awesome, still waiting for a stable version of that NgRx Signal Store, thanks for sharing!
Can I use it now in my upcoming projects or it is still not stable?
You can use it. It is very stable.
@RainerHahnekamp Thanks a lot, and thank you so much for the great content.
I wish to see and learn more about new approaches
@@MahmoudTarek-pz1rl You're welcome. I wouldn't expect that the SignalStore will get so many new features. Maybe some extensions like withEntities but not in its core.
@RainerHahnekamp I totally agree with you.
But I was sure that something related to deal with entities will have a place with signal store, whatever how.
But, for me the surprise and it was the first time I see it I even didn't came across it was signalStoreFeature it is really powerful feature to have, extensibility in this way and having a generic store features just so much powerful
great intro to SignalStore. It really looks more intuitive and yet equally powerful (if not more) than the original global store/ComponentStore.
Do you know if the effect() API will be introduced to SignalStore once effect() is fully stable, for example, withEffects()? The current approach, withMethods + rxMethod seem not fully "signalized" to me, but I do think it is sufficient for almost all use cases
I don't think so. The rxMethod itself also expects a signal and is executed whenever that Signal emits. In that sense, rxMethod is already an effect.
Thanks. I read up the documentation on SignalStore on NgRx and realized that the function generated by RxMethod can actually receive a static value (effect triggers only once), and a signal/observable (effect triggers on signal/observable update), and it auto-cleanup since it got access to DestroyRef in InjectionContext. Way more powerful than I initially thought.
I look forwards to future videos on this very interesting SignalStore, perhaps on a higher architectural level, such as using it as global store, connecting and initializing component-level signalStore to global signalStore
@@vutruong4164 Yes, there is an upcoming video where I focus more on the role of the Signal Store to enhance Angular's Signals. I'll explain the rxEffect there more in detail.
When it comes to global and local store, the repo of this video contains already a working example. github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts. You just inject like any other store.
@@RainerHahnekamp sorry for troubling you again, but is there still the concept of eagerly vs lazily initialing states with SignalStore?
My understanding is withState will always eagerly initialize the store, so I probably have to initialize states with "undefined" if I want to "lazily" initialize states later by using a load method in withMethods
@@vutruong4164No worries at all. So as soon as you inject the Store for the first time, it will be initialized and it has to have state from the very beginning. You could use undefined, but I would provide default values and a property to my state that says if the state is ready or not. If you use undefined for all your properties, you always have those union types which are net very comfortable to work with. All components, all services have to deal with those.
Thanks for the video, how can you update multiple states let's say you have a component scoped state but you also want to update part of the global application state, with actions and reducers you can hook onto any action you wish and thus reduce multiple states, but I can't see how you would do that with Signal Store?
You would require to have access to the component-provided and to the global state. patchState will work for both. Did you mean that?
@RainerHahnekamp Yes but how do I provide access to both states, how do I provide that access, is it via the withMethods parameter list? if so how do I get the store instance to pass through?
@@RainerHahnekamp I think I've solved it, by exporting the type from my store e.g. "export type AuthStore = InstanceType;" I can then pass it into the withMethods function and use it with patchState! Not tested it yet though 🙂
@@andyhb1970That sounds a little bit unnecessary. If you want in your withMethod access to another store, then just use the inject function. You can an example here: github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts
@@RainerHahnekamp I've just removed the exported type and it's working fine! I must have had a funny five minutes or VS Code intellisense did!!! Yes you're right I can just inject any dependencies into the withMethods function.
If i need to do some operations based on signal data, how to do that in component..
Not able to do that
Hi Prasoon, generally speaking, that's what an effect is for. Can you maybe give me a concrete example?
@@RainerHahnekamp Yes I used effect and it works..
Can we use multiple effect of same component or not??
@@prasoon2510 Yes, you can have as many effects as you want.
@@RainerHahnekamp Thank you so much for the quick response 😊❤️
@@prasoon2510 You are very welcome. If something is still unclear, let me know
In component store can create selector something like storeBSomeValue$ = this.select(this.#storeA.someValue$, (someValue) => someValue). Is it possible to provide state from other store? So I can get access to value from storeA using storeB. Thank you for nice video.
Sure, I've pushed a version which contains a bookingStore that requires data from the customersStore. Check it out here: github.com/rainerhahnekamp/ngrx-signal-store-demo/blob/main/src/app/bookings.store.ts
awesome content, but terrible audio :/
Hi, how bad is it? Do you think it is better, if I do a retake?
@@RainerHahnekamp Hi. Its really bad, but I always payed a lot of attention to this so maybe other people wont be bothered so much. I treated video as nice introduction, but skipped some parts and filled gaps with articles due to this.
@@RainerHahnekamp Yes, the sound quality is not particularly good, but I don't mind. I appreciate much more the hard work put into the video and the valuable information, so the sound quality doesn't really matter. Thanks for the great video Rainer! I hope you had a wonderful Christmas. Wishing you all the best for the New Year and hope you continue to give us such good content :)
@@hille-hausonfire4338 Thanks both. I will manually create subtitles and will upload them. The automatic ones aren't that good. To me, it is important that you can understand what I'm saying. As soon as that's not the case anymore, I have to re-record it. And thanks for Christmas wishes. Hope you enjoyed it as well!
I enjoyed the video very much, but I wish the audio could be better. I turned my volume to 100% and it was still difficult to hear.
Hello Alex, sorry for the poor quality. It was due to a damaged wire of the microphone and I had to aggressively apply audio filters to remove the majority of the background noise.
It won't happen again. Switched now to a wireless microphone.
You can also watch the second Part which takes on a different perspective but with much better audio quality.
there is a solution to easily handle side effects with it ??
I have a case that one slice of a store, when updated, trigger a side effect in another slice to handle... it's like a domain event.
Yes there is. The function you generate with rxMethod can also accept an Observable or event a Signal.
Example:
const Store = signalStore(withMethods(store ({incrementer: rxMethod()})));
const store = new Store();
// default usage
store.incrementer(1);
// usage with Observable
store.incrementer(of(1,2,3));
//usage with Signal
const number = signal(1);
store.incrementer(number);
---
So you just provide your event in the form of an Observable and you're done.
@@RainerHahnekamp thank you... it helped a lot!
@@TheHelianos You are welcome. That rxMethod is extremely powerful. Also outside of the Signal Store. RxJs v8 will have something similar built-in
huh signals replace ngrx tho?
If you mean the redux pattern of the globa store, then yes. If you say Signals will replace State management libraries then I would definitely say no. Because you have Signals and a structure that is suited for state management, you need a library on top of it.
This is something which has already been confirmed by Angular itself. They said, the default signal function will not be enough as soon as your Signal contains larger values.
Your microphone settings should be adjusted for better performance.
Yes, I bought a new mic after that video. Btw. this video covers also the latest features introduced in v18: th-cam.com/video/jrIXg0Avv38/w-d-xo.html
very good stuff. But where is github? ;-)
Thanks! Here it is: github.com/rainerhahnekamp/ngrx-signal-store-demo
I'm having trouble understanding you because of the audio quality
Sorry, I am aware of that and all my newer videos are using a much better microphone. You can also checkout the second part which has overlapping content with that one.
@@RainerHahnekamp Nice! Thanks for that. Good luck and keep up the good vids
@@floriss9Thanks, will do!
Do you have Dutch ancestors? Hahnekamp/Hanenkamp, it is a Dutch name.
Yeah, I know. I come from a small village in East Austria where half of the population have that surname. What I know from ancestry research is that one Hahnekamp appeared there in the 17th century. I guess Hahnekamp got a little bit germanized. The "kamp" is still as it was but I don't think that "Hahne" is Dutch.
Ngrx all ur angular state are belong to us
It definitely became easer now!
Please, consider better mic or equalization in post, your audio is too bassy, it is not good for voice clarity.
Hi, I've considered it already after that recording ;) All my newer videos are now done with a wireless mic. If you checkout the second part, you should hear that it is much better.
I still hope, you could understand what I said.
That's great :) @@RainerHahnekamp
voice sound quality is really bad.. please use a proper mic.
Yes sir, threw it away after that one and working with a wireless. You can see the difference in the second part.
Still hilariously overcomplicated way to achieve something a simple service can do....
Well, the SignalStore is still in developer preview. If you can provide an easier solution with the same kind of extensibility, we will be more than thankful.