There was a flaw in my Angular mental model

  Рет қаралды 11,767

Joshua Morony

Joshua Morony

Күн бұрын

Пікірлер: 57
@JoshuaMorony
@JoshuaMorony Жыл бұрын
Pinning this because it has come up a few times in the comments and I didn't do a good job of communicating this in the video. The service where ActivatedRoute is injected is provided directly to the component (not in root), and it is only ever used for this one component - it is not shared/reused by anything else. The lifecycle of the service/its state/streams etc are tied to the lifecycle of the component.
@properwaffles
@properwaffles Жыл бұрын
I still need to go back a rewatch this a few times, but seeing this sort of issue/problem solving ah-ha moment in action is invaluable to me.
@ryanngalea
@ryanngalea Жыл бұрын
In my Supabase app, I use realtime to listen to changes to the Postgres table in the data service, when data comes back updated, removed, inserted I simply update the state on the fly. This works great because state is reflected in any open browsers listening to the table. And I never manually update the state as the realtime listener will automatically take care of it.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
I filmed a video a couple of days ago that is almost exactly this (just not Supabase), it'll be out next week! It's basically the same approach I've already mentioned except there is just one source that sets the state signal - the stream of data from the db - so everything just updates automatically. Very nice.
@maxenn9936
@maxenn9936 Жыл бұрын
This is a great example of how Software/Developing really is about problem solving 👌
@sorinvestemean9686
@sorinvestemean9686 Жыл бұрын
I was a lazy developer, but with every video that you post and also on the premium subscription, drives me to enjoy the programming even more and to make me not sayin' This works, let's move on
@JoshuaMorony
@JoshuaMorony Жыл бұрын
Next newsletter is going out tomorrow, join the mailing list here if you like: mobirony.ck.page/4a331b9076
@jovanmilosevic1129
@jovanmilosevic1129 Жыл бұрын
Isn't injecting ActivatedRoute in the service actually injecting root ActivatedRoute and not the one we need (meaning we might not get /:id value correctly)? Or does that not apply when we have standalone components + no modules?
@doenerpoener
@doenerpoener Жыл бұрын
The same problem came into my mind. Afaik ActivatedRoute works as long as you don't have lazy loaded child routes. The solution for that (could be): Provide the Service directly in the Components Providers, so the Injection Context remains the same.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
@@doenerpoener Yes - I haven't played around with all possible scenarios, but I did specifically come across the fact that I needed to provide the service directly to the component in order for this to work (which I was doing anyway and want to do, so it's not really a problem - but maybe other scenarios might become a problem with this approach)
@lucasgiunta8874
@lucasgiunta8874 Жыл бұрын
Input from the standalone api will avoid you to use the activated, you can use à transforme function to return à signal (or an observable) and keep the same flow. In case later from an onclick or whatever you need to re trigger the flow, just call .next/.set from the source. I don't use the activated route to fetch the param sins standalone api. The problem is you must have a standalone app in the latest version of angular to have this benefit.
@zero14111990
@zero14111990 Жыл бұрын
using the logic with signals(native angular signals) you need to have minimum angular 16 and with angular 14 you can use the standalone logic
@lucasgiunta8874
@lucasgiunta8874 Жыл бұрын
@@zero14111990 time to upgrade the version and your app to standalone 😅
@sebuzz17
@sebuzz17 Жыл бұрын
Good point, the new option for input tied to route params is really cool for that.
@leakyabstraction
@leakyabstraction Жыл бұрын
I wonder what's the lifetime of the service that currently processes the route. Whether it could accidentally pick up an "id" param from other, unintended routes/pages too 🙈
@JoshuaMorony
@JoshuaMorony Жыл бұрын
The service needs to be provided to the component directly, so the lifetime of the service and its observables are tied to the component
@pedrofernandes2005
@pedrofernandes2005 Жыл бұрын
Kinda failed to understand how eventually your code would change to incorporate such changes. Too much drawing, too little code explaining :P
@JoshuaMorony
@JoshuaMorony Жыл бұрын
The only change is literally injecting ActivatedRoute directly into the service (where the sources/state management is), rather than in the smart component and trying to pass it to the service.
@nomoredarts8918
@nomoredarts8918 7 ай бұрын
ActivatedRoute will not work inside service as it's associated with a component that is loaded in an outlet
@adrian_cg
@adrian_cg Жыл бұрын
What if the route changes after the service is instantiated? (assuming {providedIn: root}), I guess if you provide the service at the component level then it's good.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
Yes - this doesn't work (well, at least I haven't tried to make it work yet) unless the service is provided to the component directly
@Brumry
@Brumry Жыл бұрын
Could you do a video on HostBinding and all of its use cases?
@sebuzz17
@sebuzz17 Жыл бұрын
Hello Josh, are you sure that you can safely use the ActivatedRoute in your service ? I mean, if you only have one route file for one only module it should be fine, but I've had issues trying to get route params when using the routes in a service rather than in a component and it became a real mess to sort which params i was able to get depending on the module route that was activated.
@nvahalik
@nvahalik Жыл бұрын
This was my thought as well. We have a hundred routes all with different IDs. Seemingly, you'd have to make the service aware of the routes you want... which now adds a new dependency on the service that, IMO, shouldn't be there. This seems like it's just pushing the problem away. In an Ionic app, we expect that our page components are going to have a lot of this kind of stuff in them. It's just a given. They are the "smart" components and that's ok. If you're wanting to be able to re-use those services in multiple areas... you may have problems with this approach.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
I didn't do a good job of communicating this in the video, but the service is provided directly to only this one component and is never shared with any other component - effectively this isn't doing anything more than providing a way to split out the state management code from the component itself into a separate file.
@sebuzz17
@sebuzz17 Жыл бұрын
@@JoshuaMorony Aaaah, ok sorry i get it, I didn't noticed that.
@daniivanov4554
@daniivanov4554 10 ай бұрын
@@JoshuaMorony in this case we need 2nd service to export Subjects or sources if we want trigger changes from other components
@fftcg.online5189
@fftcg.online5189 Жыл бұрын
Wouldn't you run into issues where the same service needs to respond in multiple routes with different param names as well? Additionally, with modules, you would only get the activated route that your module has instantiated....I think the component sending the param is the correct pattern even if more imperative since it keeps the param selection isolated to the component that requires it. Really enjoying this series. Keep them up!!!!!!!
@RyanWaite28
@RyanWaite28 Жыл бұрын
I don't think services need to know about UI state like activated route params etc; just data state. Instead, just create an observable when the route changes, like this: constructor( private activatedRoute: ActivatedRoute, private apiService: ApiService, ) {} this.postsByUser$: Observable = this.activatedRoute.params.pipe( mergeMap((params) => this.apiService.getPostsByUserId(params['user_id'])) );
@fftcg.online5189
@fftcg.online5189 Жыл бұрын
@@RyanWaite28 right..my thought was that user_id may not be the same depending on context so it was hard to specific as a hard code in the service.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
The problem with that in this context is that its pulling state out of the state management approach - so I'd have a situation where most of the state is within the state signal and accessed with selectors, except this state particular bit of state which lives outside of it - on top of that I might also want there to be loading/error states associated with this, and then maybe those are or are not alongside the rest of the state in the state management approach, so I think it gets messy. @fftcg.online5189 I didn't do a good job of communicating this in the video, but the service is provided directly to this one component, the lifecycle of the state/observables is tied to the one component, and is not shared with any other components.
@RyanWaite28
@RyanWaite28 Жыл бұрын
@@JoshuaMorony I agree with that approach. My initial concern was the idea of responsibility: what is responsible for knowing the state of the application. I’m guessing that the service in this example is a state management services that manages the current state and provides data sources based on it, so in the end, i think your solution works best.
@agon19
@agon19 Жыл бұрын
Please help a beginner out - in the code, I don't get how the loading state transitions into success state. For example, when status is error, the retry button is rendered. Once you click the retry button, a retry event is sent to the retry$ source and the state becomes "loading" - how does it transition from here to "success"? What happens? And btw, awesome videos! Really quality stuff, keep it up!
@JoshuaMorony
@JoshuaMorony Жыл бұрын
The retry$ subject is also used in the retry operator inside of the articlesForPage$ source - when articlesForPage$ emits it will cause the success state to be set, in the case of a retry it will trigger the articlesForPage$ source to try again and if it succeeds it will set the success state
@agon19
@agon19 Жыл бұрын
@@JoshuaMorony Thanks so much! It's a lot clearer now - both what happens and that I need to get a stronger grip on RxJS
@racemadnss
@racemadnss Жыл бұрын
Why couldn’t you just inject the service into the detail page component and then, during onNgInit, pass the url param to the service and populate your state? Appreciate you exploring lots of options to help us code better!
@JoshuaMorony
@JoshuaMorony Жыл бұрын
I could have, and this is what I thought I was going to have to do, but ideally I didn't want to have to add an imperative subscribe in the component to pass the data to the service, it's nicer to just have the data source where I need it from the start
@justyk2555
@justyk2555 Жыл бұрын
Hi Joshua, I'm still figuring out if the pure declarative approach is really suitable for more complex scenarios. For example, if you want to pre-populate a form using data from the backend, you will quickly run into problems regarding the form status, since you have changes to the form, but want to display them as untouched + pristine after first prefill from backend. Furthermore, it is difficult to update the data reactively, but to distinguish between a change and a reset (e.g. via button) and to set the field dirty or pristine accordingly. Do you have any thoughts on this?
@JoshuaMorony
@JoshuaMorony Жыл бұрын
Not sure if you've seen my "fully declarative" video - but I don't think a "full"/"pure" declarative approach is possible/practical for an Angular application - so there are already concessions being made somewhere and it's just a matter or where we want to draw the line. This signals/rxjs approach for me strikes a nice balance, and I think should be able to handle any situation that could come up - ultimately anything/anywhere in the application can trigger one of the sources, which can modify the state in any way necessary, and if necessary you can also use "effect" to trigger side effects as the result of some portion of the state signal changing (e.g. your form values loading in for example). I will generally try to avoid side effects, but I think there is a point where they are just practical, just like I find next-ing my action sources practical even though it is not a "fully" declarative approach.
@adambickford8720
@adambickford8720 Жыл бұрын
Not sure i love having the URL in a 1:1 w/the service; feels like a coupling that shouldn't be there. Doesn't this effectively couple the url, the component and the service? Why break it up at all at that point?
@JoshuaMorony
@JoshuaMorony Жыл бұрын
Not breaking it up is an option, but I'd have a lot of state management boilerplate in the component then which I don't really like. It makes sense with my mental model - the service is handling the state, and passes state to the component. A stream of the current route params is needed by the service to compute the state, so it's injected where it is needed. Do you see a flaw in this thinking/approach?
@adambickford8720
@adambickford8720 Жыл бұрын
@@JoshuaMorony Just the 1:1 coupling of service and component. It makes sense for the component and template, but not service imo. What if I want to get 'user info' for different pages/urls/components. Do i now need to have that service listen to the union of all fragments that might need this data? Duplicative code for each that needs it? if it's just 1:1, then it feels like cargo culting. Perhaps its what we mean by 'service' and its scope? Maybe a service that's not 'global' and is scoped to the constituent component parts? I'd have to play w/a more meaningful codebase and iterate a bit to see where it falls down.
@karamuto1565
@karamuto1565 Жыл бұрын
​@@adambickford8720I would just use a route resolver to handle the routed components state, the component can then listen to the route data changing and that should be sufficient. You could also just next a subject in the service with the resolver of that is more your thing.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
@@adambickford8720 yes this service is specifically scoped to this one component and all of the state management associated is tied to the lifecycle of that component - the approach of injecting the route into the service doesn't work if it isn't provided directly to the component (or at least I haven't looked into how to make that work). Really this isn't anything more than splitting the component up into a state management part and the rest of the component, rather than having to have everything in the component.
@karamuto1565
@karamuto1565 Жыл бұрын
Wouldn't it also have been an option to use a route resolver? It's my way to go option to resolve data for a route target.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
For some reason I've just never used route resolvers/had any inclination to want to use them - maybe I should fill in that blind spot to see if I like them
@krasserTerror
@krasserTerror Жыл бұрын
I usually also have a Subject (void type) called "TriggerReload" that I connect with combineLatest to the activatedRoute. This way I can reload the data when it was updated.
@daguttt
@daguttt Жыл бұрын
That's too manual
@nathanalberg
@nathanalberg Жыл бұрын
I tried this pattern... but I had the same issue with @inputs. I needed some @input values referenced in my service... got a little frustrated and decided not to use it. Also, had an issue where one of my button click events (subscription) threw an error (which completed it)... and my button would stop working. So any possible errors in there need to be handled properly or your UI falls apart.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
I'm sure I'll run into some more situations I need to figure out, but at least with my current thinking I don't think I would generally ever have to utilise inputs in the state. I'm thinking I would only be using this state service for smart/routed components, so no inputs. But if that situation did arise I'd probably just next a source in the service with an input setter. If my dumb components did have some kind of internal state I'd probably just use signals. If any of the code is public feel free to share, it'd be interesting to take a look at.
@nathanalberg
@nathanalberg Жыл бұрын
@@JoshuaMorony Maybe I just build my apps wrong. I have a lot of smart components that are not tied to "route/pages". They are often customized by inputs. (I have overlay component/editors that can be opened from anywhere in the app, components that are used in multiple apps/places with @inputs to slightly tweak them etc). Thanks for the vids Josh.. i enjoy the videos even tho Im struggling with declarative.
@JoshuaMorony
@JoshuaMorony Жыл бұрын
​@@nathanalberg that's a fair point - I also have these sorts of smart components that aren't routed (e.g. specifically that kind of thing - modals/overlays that can be configured with inputs like isOpen). I also don't have any complex state in these sorts of smart components though so wouldn't be using the state management approach in the video - maybe I just haven't run into a scenario that is complex enough yet though. In any case, you always still have the option of just nexting a source in the service with an input setter if required.
Getting Started with ARCore: A Comprehensive Guide
4:20
Coding Tech Room
Рет қаралды 8
A visual guide to changing without reassigning in DECLARATIVE code
8:06
АЗАРТНИК 4 |СЕЗОН 2 Серия
31:45
Inter Production
Рет қаралды 786 М.
He bought this so I can drive too🥹😭 #tiktok #elsarca
00:22
Elsa Arca
Рет қаралды 44 МЛН
I only ever use *these* RxJS operators to code reactively
25:25
Joshua Morony
Рет қаралды 128 М.
The easier way to code Angular apps
9:54
Joshua Morony
Рет қаралды 66 М.
WTF is "Zone.js" and is it making your app slow?
13:21
Joshua Morony
Рет қаралды 54 М.
Why didn't the Angular team just use RxJS instead of Signals?
8:15
Joshua Morony
Рет қаралды 94 М.
React + RxJS = Reactive Global Goodness
26:46
Jack Herrington
Рет қаралды 42 М.
"I Hate Agile!" | Allen Holub On Why He Thinks Agile And Scrum Are Broken
8:33
Why use OnPush in Angular? Not for performance...
13:15
Joshua Morony
Рет қаралды 31 М.
Turns out REST APIs weren't the answer (and that's OK!)
10:38
Dylan Beattie
Рет қаралды 156 М.
WTF is "modern" Angular development?
10:46
Joshua Morony
Рет қаралды 70 М.
АЗАРТНИК 4 |СЕЗОН 2 Серия
31:45
Inter Production
Рет қаралды 786 М.