Important bit of context for the viability of signals based sources/actions here (thanks to Younes Jaaidi for pointing this out). One reason I gave for not using signals was that (by default) the signals value needs to change in order for effects to run. But I mentioned we *could* work around that if we wanted to. However, a more important problem with the signal approach is how effects are scheduled. Even if we do make sure we only set unique values, or supply a custom equality function to the signal so that the signal always sees values as being different, we can still run into problems with missing events. If we were to trigger "add" twice in succession for example, even with different values, the effect will only run once for the second value, meaning the first "add" event will just be ignored. This works fine in my case in this example app, but this could be pretty surprising behaviour that could trip you up, and makes a stronger case for using RxJS for events.
I was just about to mention that. Effects don't run on every signal change. They are scheduled to run, when the signal graph is in a consistent state (glitch free state, keyword: diamond problem). Signals are the shiny new thing in Angular, but they are only one side of the coin. The other side is events, and events are best handled with observables. Or in other words: signals hold the state, and events transitions between states. And we don't want to miss events (like I don't want to miss the event when you publish a new video...). Thanks for your content!
I think of signals/effects as a "distinctUntilChanged, debounceToEffect, shareReplay" kind of thing. And depending on the events you want to process, that are most likely not the operators you want to use.
I tried your pattern once but did not like that some of the event 'sources' were subscribed to on construct..... If I had any unexpected error on one of my sources.. the source was unsubscribed... thus closing the subscription... breaking the app... so I found it too error prone.. and just went back to using imperative click handlers to call/subscribe.
There was a similar question somewhere else in the comments, I'll paste my reply from there: In short: to enable actions/facilitate more declarative code. With the approach where we have some sort of event notifier we limit the imperative code to just triggering that action, and wherever our reducers are. When an action is triggered, anything that is interested in that action can react to it. If we have add/remove methods, we have more imperative code in those methods. To give a concrete example where the action approach is nice from this app, when a checklist is removed two separate services need to react to that happening. With the action based approach, they just share the same event source of the remove being triggered and react however they need to. With a remove method, the remove method will contain imperative code that also needs to make sure it calls more imperative code from the other service to make sure the remove happens there as well.
Curious, why not just ditch the observables and have add and remove methods? Seems like less of a clash of styles than mixing observables and signals as sources.
In short: to enable actions/facilitate more declarative code. With the approach where we have some sort of event notifier we limit the imperative code to just triggering that action, and wherever our reducers are. When an action is triggered, anything that is interested in that action can react to it. If we have add/remove methods, we have more imperative code in those methods. To give a concrete example where the action approach is nice from this app, when a checklist is removed two separate services need to react to that happening. With the action based approach, they just share the same event source of the remove being triggered and react however they need to. With a remove method, the remove method will contain imperative code that also needs to make sure it calls more imperative code from the other service to make sure the remove happens there as well. Ultimately it's a style choice
@@JoshuaMorony Thanks for the explanation. I was about to ask the same question. The sources approach makes it much easier for other services to act on the event streams. I might have gone a little offroad with signal effects.
And of course, it depends... on your context. In really simple scenarios you can get away with the imperative approach. But some day you will encounter situations where the event based solution will fit better. You decide, which and how many patterns your app should use.
I've thought about using an abstraction that uses this, e.g. wrap a signal with a custom equality function into something like "sourceSignal()". Still not sure it's the right idea though, but maybe.
I’m a bit confused. Angular is gradually moving away from RxJS because it’s considered 'too complex,' but in the process, they’re introducing a number of new mechanisms that aren’t necessarily easier to understand or learn. So, in the end, we’re just replacing one form of complexity with another.
I don’t get why people think that a new feature should replace an older one. You know, you can have two ways of doing something, right? You can do it with signals only, with RxJS only, with both combined, or with neither of them.
I don't think we can say RxJS complexity is the reason Angular is adopting signals. They were evaluating how they wanted to approach reactivity in Angular moving forward (including what mechanism they would use to allow for zoneless change detection), potentially they could have chose to lean more into RxJS, but ultimately decided that signals were a better fit for what they are trying to achieve.
@@o_glethorpe Because having 2 ways to do one thing is something bad that you try to avoid as a framework developer whenever possible. So they must have thought that signals offer a greater advantage over RxJS. I don't know whether it's simplicity or anything else.
Tbh before this stuff is not properly figured out and set into an industry standard I'm not moving away from a rxjs only approach. Once you get used to rxjs it really repeats itself and is easy to use. I don't get why we are trying to replace something that is working perfectly fine.
In the scope of Angular, what we should aim for is using signals in the template to get rid off zonejs one day. But outside of the template rxjs/observables are just fine.
@@larshanisch Can you clarify this please with an example? I am using the ViewModel Design Pattern mostly, combining observables for my template. Does this mean, that I would simply have to replace my ViewModel Observable with a Signal? Or is there more to it?
@@dreivierabi6314 I don't know how your ViewModel Design Pattern is implemented. I use (for now) mainly a redux like pattern, where I have one AsyncPipe in the template where I push the state and use properties on that state in the template (OnPush of course) and let the "input not changed if reference not changed" behaviour do it's thing. On new components I don't use the AsyncPipe anymore but replace all data needed in the template with signals. That can be "computed" (most of the time), but any readable signal will do. Then I have some event sources like button clicks etc., transform them with rxjs and use "toSignal" at the right place. I will simplify this once the "Resource API" in Angular gets more stable. For now I had something similar implemented by myself. A "subscribe" with the right "takeUntil(Destroyed)" to set some signals would also work.
If I'm using a library I generally prefer to use signalSlice (I don't have anything against SignalStore), but I like to have a sense/method of how to approach state without a library as well, and doubly so for scenarios where I'm teaching (I would prefer to teach using just what comes out of the box).
Important bit of context for the viability of signals based sources/actions here (thanks to Younes Jaaidi for pointing this out). One reason I gave for not using signals was that (by default) the signals value needs to change in order for effects to run. But I mentioned we *could* work around that if we wanted to. However, a more important problem with the signal approach is how effects are scheduled. Even if we do make sure we only set unique values, or supply a custom equality function to the signal so that the signal always sees values as being different, we can still run into problems with missing events. If we were to trigger "add" twice in succession for example, even with different values, the effect will only run once for the second value, meaning the first "add" event will just be ignored. This works fine in my case in this example app, but this could be pretty surprising behaviour that could trip you up, and makes a stronger case for using RxJS for events.
I was just about to mention that. Effects don't run on every signal change. They are scheduled to run, when the signal graph is in a consistent state (glitch free state, keyword: diamond problem). Signals are the shiny new thing in Angular, but they are only one side of the coin. The other side is events, and events are best handled with observables. Or in other words: signals hold the state, and events transitions between states. And we don't want to miss events (like I don't want to miss the event when you publish a new video...).
Thanks for your content!
I think of signals/effects as a "distinctUntilChanged, debounceToEffect, shareReplay" kind of thing. And depending on the events you want to process, that are most likely not the operators you want to use.
Great use of animations and graphics! 👍
You will update the angular course?
Yes, I'm working on that now
I tried your pattern once but did not like that some of the event 'sources' were subscribed to on construct..... If I had any unexpected error on one of my sources.. the source was unsubscribed... thus closing the subscription... breaking the app... so I found it too error prone.. and just went back to using imperative click handlers to call/subscribe.
Instead of the “actions” as Subjects that you .next(), why not methods that then interact with signals?
There was a similar question somewhere else in the comments, I'll paste my reply from there:
In short: to enable actions/facilitate more declarative code. With the approach where we have some sort of event notifier we limit the imperative code to just triggering that action, and wherever our reducers are. When an action is triggered, anything that is interested in that action can react to it. If we have add/remove methods, we have more imperative code in those methods. To give a concrete example where the action approach is nice from this app, when a checklist is removed two separate services need to react to that happening. With the action based approach, they just share the same event source of the remove being triggered and react however they need to. With a remove method, the remove method will contain imperative code that also needs to make sure it calls more imperative code from the other service to make sure the remove happens there as well.
im with you in this one. some cases is better to let the rxjs to its job and signals handle states
Curious, why not just ditch the observables and have add and remove methods? Seems like less of a clash of styles than mixing observables and signals as sources.
In short: to enable actions/facilitate more declarative code. With the approach where we have some sort of event notifier we limit the imperative code to just triggering that action, and wherever our reducers are. When an action is triggered, anything that is interested in that action can react to it. If we have add/remove methods, we have more imperative code in those methods. To give a concrete example where the action approach is nice from this app, when a checklist is removed two separate services need to react to that happening. With the action based approach, they just share the same event source of the remove being triggered and react however they need to. With a remove method, the remove method will contain imperative code that also needs to make sure it calls more imperative code from the other service to make sure the remove happens there as well.
Ultimately it's a style choice
@@JoshuaMorony Thanks for the explanation. I was about to ask the same question. The sources approach makes it much easier for other services to act on the event streams. I might have gone a little offroad with signal effects.
@@JoshuaMorony thanks, I like it. Basically pub sub right?
And of course, it depends... on your context. In really simple scenarios you can get away with the imperative approach. But some day you will encounter situations where the event based solution will fit better. You decide, which and how many patterns your app should use.
for signal to emit on each change pass it this options object: { equal: () => false }
I've thought about using an abstraction that uses this, e.g. wrap a signal with a custom equality function into something like "sourceSignal()". Still not sure it's the right idea though, but maybe.
I’m a bit confused. Angular is gradually moving away from RxJS because it’s considered 'too complex,' but in the process, they’re introducing a number of new mechanisms that aren’t necessarily easier to understand or learn. So, in the end, we’re just replacing one form of complexity with another.
I don’t get why people think that a new feature should replace an older one. You know, you can have two ways of doing something, right? You can do it with signals only, with RxJS only, with both combined, or with neither of them.
I don't think we can say RxJS complexity is the reason Angular is adopting signals. They were evaluating how they wanted to approach reactivity in Angular moving forward (including what mechanism they would use to allow for zoneless change detection), potentially they could have chose to lean more into RxJS, but ultimately decided that signals were a better fit for what they are trying to achieve.
@@o_glethorpe Because having 2 ways to do one thing is something bad that you try to avoid as a framework developer whenever possible. So they must have thought that signals offer a greater advantage over RxJS. I don't know whether it's simplicity or anything else.
A large part of development, especially front end, is fashion.
@@o_glethorpe Because the learning curve is staggering for new devs. It also constrains future evolution of a language/framework
Tbh before this stuff is not properly figured out and set into an industry standard I'm not moving away from a rxjs only approach. Once you get used to rxjs it really repeats itself and is easy to use. I don't get why we are trying to replace something that is working perfectly fine.
Well, whos saying this is a new way of doing and you should replace rxjs? Its another way of doing it and you can choose what you prefer.
@@o_glethorpefor now, but we know that at some point someone will want to stop maintaining two things and will ditch the support for the old one
In the scope of Angular, what we should aim for is using signals in the template to get rid off zonejs one day. But outside of the template rxjs/observables are just fine.
@@larshanisch Can you clarify this please with an example? I am using the ViewModel Design Pattern mostly, combining observables for my template. Does this mean, that I would simply have to replace my ViewModel Observable with a Signal? Or is there more to it?
@@dreivierabi6314 I don't know how your ViewModel Design Pattern is implemented. I use (for now) mainly a redux like pattern, where I have one AsyncPipe in the template where I push the state and use properties on that state in the template (OnPush of course) and let the "input not changed if reference not changed" behaviour do it's thing. On new components I don't use the AsyncPipe anymore but replace all data needed in the template with signals. That can be "computed" (most of the time), but any readable signal will do. Then I have some event sources like button clicks etc., transform them with rxjs and use "toSignal" at the right place. I will simplify this once the "Resource API" in Angular gets more stable. For now I had something similar implemented by myself. A "subscribe" with the right "takeUntil(Destroyed)" to set some signals would also work.
Why not just use the signal store library? Honest question.
If I'm using a library I generally prefer to use signalSlice (I don't have anything against SignalStore), but I like to have a sense/method of how to approach state without a library as well, and doubly so for scenarios where I'm teaching (I would prefer to teach using just what comes out of the box).
@JoshuaMorony good answer
:3