first off I think it's great to have deep knowledge about Angular stuffs (change detection, rxjs) & messing with them. But after refactoring, you ended up with 2 nested pipes and a HOO (Higher Order Observable) inside another HOO, which definitely harder to debug & understand. Async pipe might be great but if it make your code harder to read & trace, just go with the normal way, subscribe & assign value to a variable, bind that value to template & let Angular take its course.
I would create BehaviorSubject and push new values within the Input setter and a subscription to subject with delay to project the data to template. This would be easier to test and the data is split from reactive mapping without introducing flattening operators.
I'm back into working w/ angular and searched a topic and now your all videos are being recommended to me. I am not disappointed; your videos are excellent!
Nice video! I switched over to using onPush for every component for the reasons you mentioned. Another reason I see which is not explicitely mentioned: Once you understand when the changeDetection is triggered when set onPush, it becomes very clear and concise (and easy to test) changes in the component because there is only a very limited number of events if your component is as small as it should be. There is also no way for someone else to mess changeDetection up - I've seen horrible inplace mutation of arrays and objects inside that array which only worked because of the default Angular ChangeDetection setting. I honestly believe there should only be OnPush or it should be the default at least. Even for small apps. Performance gain is nice 2 have and makes sense, but as you said, it just makes you write much clearer code which is way easier to understand if you just take the time to watch your explanation ;)
Hi Joshua, thanks for this great video 👍 It gave me clear understanding on the benefits of creating components reactively (also functional via pipe()). I'm the one who asked ealier regarding the benefits of why use RxJS over standard way. I think this video answered that. THANKS!
Correction- the async pipe does not trigger change detection, it calls markForCheck that tells angular to check the component in the next change detection cycle
@Andy Brewer Angular uses zone.js to trigger change detection. So the following things can trigger change detection in angular: 1. all things zone.js patches (Web apis, browser events, ajax requests etc...) 2. manually calling detectChanges() which tells Angular to run change detection on the component and it's children 3. calling ApplicationRef.tick() which tells Angular to run change detection for the whole application. By default angular will check every single component in the component tree for changes unless that component's change detection strategy is set to OnPush, in that case angular decides whether to skip that component or not. The way it determines whether to skip it is by checking the following things: 1. If an Input reference has changed 2. An event originated from the component or one of its children. 3. The component was marked for check (like what the async pipe does)
I am aware of this so hopefully the video isn't misleading, I mentioned that the async pipe also uses the CDR to trigger change detection - which it does by using markForCheck() and then a tick() is triggered, which is going to cause the component to be updated just like detectChanges() would do if we did that manually ourselves. Maybe the video could have been clearer here, but it's also a deeper point that I didn't really want to get bogged down on. Thanks for the comment!
then why the example works with all the photoes? With markForCheck changeDetection only works on the next cycle, but here we got all the photoes displayed instead of skipping the last one
Some comments are taking this video as evidence that rxjs is bad (at least for this situation). But there's always multiple ways to do something, and maybe people might find something like this more intuitive: this.currentPhoto$ = zip(interval(500), from(value.reverse())).pipe( map(([intervalIndex, photo]) => photo) } Learning zip, interval, and from can be tricky, but the hardest one here is zip... and playing with the rxjs marbles diagram for it gives an intuitive understanding super quickly.
I must say the onPush: I’m totally pro. Makes you think about your code. As a lecturer, developer and dev mgr, I often see functional code which is very readable at writing time and while explaining. However for other developers reading your code and even for yourself 2 years later, it’s hard see what it does exactly. The number of lines doesn’t matter, readability to other people is much more important.
Thanks for the great videos :) Regarding the photos example, I see many people saying that you shouldn't pass an observable to an input. Do you disagree?
I generally agree, most of the time I will use the async pipe to subscribe to the stream and pass synchronous values to the component as an input. Important not to be too dogmatic about these things though, sometimes breaking the "rules" can be beneficial or useful. One argument for passing in an observable as an input is that the input reference won't change so there is going to be less change detection triggered with OnPush - so potential significant performance gains there under the right circumstances. Maybe sometimes it just makes the code easier/cleaner and that's fine too.
This example shows why exactly you shouldn’t use onPush. And if there are any performance gains, it should be Angular who come up with performance updates for their product not developers
Thanks a lot for this video, this is actually something I've started to do recently, so I'm glad to see the long-term benefits it can bring. Also, I must confess being a little disappointed by your newsletter, that enforce the use of a .subscribe(), instead of your beloved async pipe 🤔😏
I responded to a similar question in another comment: "I generally agree, most of the time I will use the async pipe to subscribe to the stream and pass synchronous values to the component as an input. Important not to be too dogmatic about these things though, sometimes breaking the "rules" can be beneficial or useful. One argument for passing in an observable as an input is that the input reference won't change so there is going to be less change detection triggered with OnPush - so potential significant performance gains there under the right circumstances. Maybe sometimes it just makes the code easier/cleaner and that's fine too."
What GLaw said, also you can supply flags when generating components if you want, and I would not advise against it - I think you should do it for every component :) having some OnPush and some not would probably just make things confusing.
In the last version of the code no observables are passed between components. Instead, the setter converts the value to an observable (and the currentPhoto$ observable is "converted" back to a normal value via the `async`-pipe).
The delay operator will add a delay before the stream starts emitting, but it won't add a delay for *each* emission. That's why the concatMap is required here, because I want to wait 500ms between each emission, so I have to create an individual stream for each individual photo so that the delay will work how I want.
Join my mailing list for more exclusive content and access to the archive of my private tips of the week: mobirony.ck.page/4a331b9076
first off I think it's great to have deep knowledge about Angular stuffs (change detection, rxjs) & messing with them. But after refactoring, you ended up with 2 nested pipes and a HOO (Higher Order Observable) inside another HOO, which definitely harder to debug & understand. Async pipe might be great but if it make your code harder to read & trace, just go with the normal way, subscribe & assign value to a variable, bind that value to template & let Angular take its course.
I would create BehaviorSubject and push new values within the Input setter and a subscription to subject with delay to project the data to template. This would be easier to test and the data is split from reactive mapping without introducing flattening operators.
I'm back into working w/ angular and searched a topic and now your all videos are being recommended to me. I am not disappointed; your videos are excellent!
Nice video! I switched over to using onPush for every component for the reasons you mentioned. Another reason I see which is not explicitely mentioned: Once you understand when the changeDetection is triggered when set onPush, it becomes very clear and concise (and easy to test) changes in the component because there is only a very limited number of events if your component is as small as it should be. There is also no way for someone else to mess changeDetection up - I've seen horrible inplace mutation of arrays and objects inside that array which only worked because of the default Angular ChangeDetection setting. I honestly believe there should only be OnPush or it should be the default at least. Even for small apps. Performance gain is nice 2 have and makes sense, but as you said, it just makes you write much clearer code which is way easier to understand if you just take the time to watch your explanation ;)
Thank you for your efforts! I've finally found a better angular channel to learn from!
Thank you so much! All of your videos are amazing! You are the best source for advanced Angular topics by far.
Hi Joshua, thanks for this great video 👍 It gave me clear understanding on the benefits of creating components reactively (also functional via pipe()). I'm the one who asked ealier regarding the benefits of why use RxJS over standard way. I think this video answered that. THANKS!
Correction- the async pipe does not trigger change detection, it calls markForCheck that tells angular to check the component in the next change detection cycle
That sounds like a subtle, but important, point. What's the difference? When would the next change detection cycle be?
@Andy Brewer
Angular uses zone.js to trigger change detection.
So the following things can trigger change detection in angular:
1. all things zone.js patches (Web apis, browser events, ajax requests etc...)
2. manually calling detectChanges() which tells Angular to run change detection on the component and it's children
3. calling ApplicationRef.tick() which tells Angular to run change detection for the whole application.
By default angular will check every single component in the component tree for changes unless that component's change detection strategy is set to OnPush, in that case angular decides whether to skip that component or not. The way it determines whether to skip it is by checking the following things:
1. If an Input reference has changed
2. An event originated from the component or one of its children.
3. The component was marked for check (like what the async pipe does)
@Andy Brewer
hope that clears it up
I am aware of this so hopefully the video isn't misleading, I mentioned that the async pipe also uses the CDR to trigger change detection - which it does by using markForCheck() and then a tick() is triggered, which is going to cause the component to be updated just like detectChanges() would do if we did that manually ourselves. Maybe the video could have been clearer here, but it's also a deeper point that I didn't really want to get bogged down on. Thanks for the comment!
then why the example works with all the photoes? With markForCheck changeDetection only works on the next cycle, but here we got all the photoes displayed instead of skipping the last one
What a clean explanation. Great content as always.
Some comments are taking this video as evidence that rxjs is bad (at least for this situation). But there's always multiple ways to do something, and maybe people might find something like this more intuitive:
this.currentPhoto$ = zip(interval(500), from(value.reverse())).pipe(
map(([intervalIndex, photo]) => photo)
}
Learning zip, interval, and from can be tricky, but the hardest one here is zip... and playing with the rxjs marbles diagram for it gives an intuitive understanding super quickly.
I must say the onPush: I’m totally pro. Makes you think about your code.
As a lecturer, developer and dev mgr, I often see functional code which is very readable at writing time and while explaining. However for other developers reading your code and even for yourself 2 years later, it’s hard see what it does exactly.
The number of lines doesn’t matter, readability to other people is much more important.
Second this. Readable code is in most cases way more important than the most technically beautiful code.
I use change detection in my website and this is very interesting!
Really nice example, thank you very much!
Thanks for the video! At 6:50, onPush change detection strategy won't enable change detection because `currentPhoto` isn't marked with @Input() ?
Nice, however I shiver at the thought of unittesting stuff like this.
Thank your for this video
Thanks for the great videos :) Regarding the photos example, I see many people saying that you shouldn't pass an observable to an input. Do you disagree?
I generally agree, most of the time I will use the async pipe to subscribe to the stream and pass synchronous values to the component as an input. Important not to be too dogmatic about these things though, sometimes breaking the "rules" can be beneficial or useful. One argument for passing in an observable as an input is that the input reference won't change so there is going to be less change detection triggered with OnPush - so potential significant performance gains there under the right circumstances. Maybe sometimes it just makes the code easier/cleaner and that's fine too.
Would have been better if you had provided stackblitz example.
Thanks for the video 👍
thank you for the video! it's gonna be useful for a particular feature I'm working on in an app of mine! Btw why did you reverse your photo array?
Just because I wanted the photos to display in reverse order (last should be shown first) - nothing special going on there!
Great video 👍
This example shows why exactly you shouldn’t use onPush. And if there are any performance gains, it should be Angular who come up with performance updates for their product not developers
Thanks a lot for this video, this is actually something I've started to do recently, so I'm glad to see the long-term benefits it can bring. Also, I must confess being a little disappointed by your newsletter, that enforce the use of a .subscribe(), instead of your beloved async pipe 🤔😏
What’s the approach to detect change in dynamic created components? You would need to use ChangeDetectorRef right?
ty
Isn't pushing Observables through Input an antipattern?
I responded to a similar question in another comment: "I generally agree, most of the time I will use the async pipe to subscribe to the stream and pass synchronous values to the component as an input. Important not to be too dogmatic about these things though, sometimes breaking the "rules" can be beneficial or useful. One argument for passing in an observable as an input is that the input reference won't change so there is going to be less change detection triggered with OnPush - so potential significant performance gains there under the right circumstances. Maybe sometimes it just makes the code easier/cleaner and that's fine too."
And how to stop/interrupt this slideshow?
Thanks for the video! Is there a way to set OnPush globally, instead for each component, or would you advise against doing it for every component?
You can set it in angular.json, but it will only affect the components that you will create in the future (with the CLI)
What GLaw said, also you can supply flags when generating components if you want, and I would not advise against it - I think you should do it for every component :) having some OnPush and some not would probably just make things confusing.
@@GLawSomnia Thank you for the answer!
@@JoshuaMorony Thanks for the answer, I think I'll do that from now on 😁
I like your accent man
11:25 ain't this violate the "reactive" coding rule? You must have subscribed somewhere to get the list of photos as an array.
get a helping answer thanks.....
But it is not a good practice to give a stream to a component.
In the last version of the code no observables are passed between components. Instead, the setter converts the value to an observable (and the currentPhoto$ observable is "converted" back to a normal value via the `async`-pipe).
Sae
😂 Pᵣₒmₒˢᵐ
Couldn't you have totally skipped concatMap here and just go with this:
from(value.reverse()).pipe(delay(500)) ?
The delay operator will add a delay before the stream starts emitting, but it won't add a delay for *each* emission. That's why the concatMap is required here, because I want to wait 500ms between each emission, so I have to create an individual stream for each individual photo so that the delay will work how I want.
@@JoshuaMorony Yes, now it makes sense. thanks a lot for the detailed explanation!