Refactoring my ugliest code with signal view queries

Поделиться
HTML-код
  • Опубликовано: 19 янв 2025

Комментарии • 44

  • @JoshuaMorony
    @JoshuaMorony  10 месяцев назад

    Join the newsletter: mobirony.ck.page/4a331b9076

  • @MichaelScharf
    @MichaelScharf 10 месяцев назад +2

    It's so funny to see that signals are "the new kid on the block" although Meteor(js) invented them in 2012, and it was called Tracker and later MobX uses them under the hood (called atoms). My prediction is that in a few years a library similar to MobX will be built on top of Signals, and it will become popular. I have been using Meteor and later MobX for almost 10 years now, and I am watching how these things get reinvented over and over again.

  • @dylanjhalltech8313
    @dylanjhalltech8313 10 месяцев назад +1

    Thanks Josh. A very simple and to the point explanation.

  • @deatho0ne587
    @deatho0ne587 10 месяцев назад

    That looks much cleaner than most reactive code I have seen. My age of starting coding (basic, c, & assembly) and the dirtiness that you showed at the start are the two main reasons I have sort of avoided it in Prod code. Not calling reactive code bad at all.
    3:57 what is this.togglePlay$?

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад

      togglePlay$ emits when the user clicks the video - ideally this shouldn't be required for videoLoadStart$ but for some reason using fromEvent to create a stream from loadeddata actually triggers the video load without the user actually clicking the video, so I had to set up togglePlay$ like this to delay the subscription to the stream from fromEvent

  • @bjarnekinkel4122
    @bjarnekinkel4122 10 месяцев назад

    Could you create a video about working with forms reactively? Topics could include having the form in a view-only mode and editing mode, asynchronously filling it with data, being able to leave the edit mode with or without saving the changed data, and the ability to load a template while inside the edit mode to suggest some inputs. It would also be interesting to cover more advanced form concepts like a FormArray within the actual form.The use case I envision is an editing page for a blog post, where I can select a post. It would then be displayed in view-only mode. Upon clicking edit, a form filled with the corresponding data would open. I could then edit it and either save or cancel. Additionally, there should be a button that automatically fills certain inputs with data from the backend without directly saving the changes - just a template. The post could have optional tags, necessitating the use of a FormArray.When I tried to implement all of this, I found it challenging to keep everything relatively simple. But perhaps you know some tips and tricks that could help.

  • @BiswajitPaul01
    @BiswajitPaul01 10 месяцев назад

    I would love if you could share the sample code url.

  • @jeffnikelson5824
    @jeffnikelson5824 10 месяцев назад

    Hey Josh, thanks for highlighting this new feature!
    One feedback for your angularstart course:
    do you think you could add a small example to the signal-based state management approach that shows how to properly connect CRUD operations to the state ? I think for a lot of people, including me, this is the most common use-case and would be great to see how a professional does this.
    For me its sometimes unclear how to connect the subjects to api calls and how to handle responses and errors properly.

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад

      The Quicklists app covers a more full CRUD type scenario but it's local, but the Chat app covers working with a remote backend/live data (but it's not full CRUD since we aren't doing updates or deletes there) - do these miss scenarios that you want to see covered or do you mean more that you would like to see examples earlier in the theory sections?

    • @jeffnikelson5824
      @jeffnikelson5824 10 месяцев назад

      @JoshuaMorony Yes exactly, that's what I mean. Both of them kind of get close to a real crud scenario, but not quite. for me personally, it would be already enough to connect the quicklist actions to a real api instead of saving local in an effect.

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +1

      @@jeffnikelson5824 that could make an interesting/natural extension for that app, I'll think about including that thanks

  • @TheoLeChnow
    @TheoLeChnow 10 месяцев назад +3

    FINALLY, byebye setters

    • @LarsRyeJeppesen
      @LarsRyeJeppesen 10 месяцев назад

      And bye bye NgOnChanges.... bye forever, so long, not gonna miss ya

  • @CraigGoldstone
    @CraigGoldstone 10 месяцев назад +2

    So awesome to be experiencing the continual evolution of Angular and frontend development. Long may it continue. Thanks for documenting your journey with these new features and helping us all understand the benefits practically.
    I still see observables in the mix here - and I don't meant to villainise observables at all - but for this specific component did you consider using a purely signal based approach? And more generally, do you agree with the statement: "For presentation components we should prioritise signals over observables"?

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +4

      For a purely signals approach here the code would have to be imperative: e.g. having a callback function for the loadstart event that sets a signal. Not really a big deal, and in this isolated situation there isn't much tangible benefit to the declarative version. But, I think things get a bit iffy if you are switching paradigms on a case-by-case/ad-hoc basis and so generally I will prefer to just make everything declarative, and especially now that it is much easier with signal view queries.
      To your last question, that's not really the way I think about it, but I wouldn't really say I either agree or disagree with it. Generally it makes sense since I would view observables/rxjs being at the "top" of the data flow in the application where necessary, and then components in the application would generally be consuming state via signals. I would say generally my preference is for declarative code, I will reach for signals first, but if I can't make things declarative with just signals (e.g. I am dealing with async situations) then I will reach for observables.

  • @marapetsani7527
    @marapetsani7527 10 месяцев назад

    Is the videoElement$ needed? can't the value of the videoElement signal be used?

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +1

      I assume you mean something like videoLoadComplete$ = fromEvent(this.videoElement().nativeElement, 'loadeddata')? That should be fine, and I think is probably preferable

    • @marapetsani7527
      @marapetsani7527 10 месяцев назад

      yup. in videoLoadStart$ as well (basically the first switchMap is redundant). Cool video btw

  •  10 месяцев назад

    Awesome! Thanks Josh!

  • @christianhalaby832
    @christianhalaby832 10 месяцев назад

    Hey Josh, I was checking your course and it seems more focused on beginner friendly, where as i am looking for a course focused on optimization (and new things that dropped in angular v17 as it seems there is a lot changing an i am not keeping track) etc... a fellow angular dev with 3 years of experience.

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +1

      The course is designed to be approachable for people new to Angular but the content still gets quite advanced, I wouldn't say it has a focus on optimisation so much but rather on architecture/patterns/declarative code etc. The course was written with v17, but there has also been some more features added after v17 was released (like view queries and model) and the course will be updated with these soon.

  • @jonatabiondiJsLover
    @jonatabiondiJsLover 10 месяцев назад

    You will update the course with a dedicate section? Also for new feature like model...ecc
    Im finishing your course. I think Is great course

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +1

      The plan is to "refresh" the course once signal based components arrive/we have all the features for signal based components - I'll go through and rewrite for the post-v17 features that were added (plus maybe some extra sections for stuff like model)

  • @EJIqpEP
    @EJIqpEP 10 месяцев назад

    Amazing video! I wanted to ask how do you solve the problem with signals and typescript.
    someSig = signal(null)
    @if (someSig() {
    someSig().name
    }
    Typescript will throw the error that signal can be null and if doesn't help because it's a call. as keyword helps but only if we have just a single signal to check.
    Do you know any better solution?
    Thanks in advance. This is the only thing that stops me from going full signals.

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад

      The Angular team are working on type narrowing for this situation, but for now you can work around it by using an alias e.g: @if(someSig(); as someSig){}

    • @EJIqpEP
      @EJIqpEP 10 месяцев назад

      @@JoshuaMorony Thank you for your answer. It helps with a single signal but not if I have multiple of them.

  • @CodeZakk
    @CodeZakk 10 месяцев назад

    I don't understand the final code why do you use switch map and also what does filter(Boolean) do. Thanks in advance!🎉

    • @figloalds
      @figloalds 10 месяцев назад

      switchMap makes so that you switch from using one observable into using the other one. You take the value emitted from one observable and turn that into a new observable
      He's saying videoLoadStart$ will emit when togglePlay$ emits true, then it will subscribe to videoElement$ and relay it's emissions and then take these relayed emissions and actually start a whole new observable that is based on the events of the videoElement that was emitted by the previous step
      Now, I don't understand why videoLoadStart and videoLoadEnd are declared differently since they were supposed to be essentially very similar.
      From looking at a glance I don't know if videoElement$ is supposed to replay events onSubscribe, and I don't know if any of this will cause a memory leak or not, but it all certainly looks to me like a gun with a heat seeking system to find feet. And that's why I hate RXJS. Unnecessarily hard to comprehend what it's doing, even harder to debug

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +1

      The videoLoadComplete$ example is a more typical example - when the "videoElement$" observable emits the video element, I want to take that and switch to a stream created from its "loadeddata" event. The "videoLoadStart$" is a bit more complicated due to what I think is a bug - I only want to react to the user triggering the load, but I found that if I use fromEvent on 'loadeddata' it actually triggers the load automatically without the user ever clicking the video - so I added a workaround of reacting to the "togglePlay" action which only happens when the user actually clicks the video.

    • @e-jarod4110
      @e-jarod4110 10 месяцев назад

      ​@@figloalds without rxjs, how would you implement a reactive "stream" of data in react or vue or angular ?
      Well there is signals now, but without it ?

    • @figloalds
      @figloalds 10 месяцев назад

      @@e-jarod4110 I wouldn't, that's the point
      This whole "reactive stream of data" just serves to turn a simple problem into an unnecessarily complicated one
      The "bug" that Joshua mentioned above is part of what I mentioned, the "magic" not magic'ing predictably, having to come up with an abstract completely different model for the program, only to get an unpredictable, undebuggable mess feels bad

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад

      @@figloalds the bug in this case is extremely atypical, this is probably the only case I've ever run into where there is actually some weird/mysterious behaviour happening that is (probably) some underlying platform/rxjs issue

  • @tranquility_lane
    @tranquility_lane 10 месяцев назад

    Love it! ❤

  • @Daniel-dj7vc
    @Daniel-dj7vc 10 месяцев назад

    Cant this be achieved through a store? I stull have some trouble understanding usecases for viewchild

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +1

      viewChild just lets you get a reference to some element in the template, basically the Angular way to do something like document.querySelector. You can certainly do this imperatively without viewChild, you would do something like set up a callback function for a (loadstart) event binding and then set a "videoLoadStart" signal from that callback function. The benefit of doing it declaratively with viewChild is that when you look at "videoLoadStart$" you can see exactly what it is/how it behaves, and its behaviour is not defined/controlled from anywhere else. With an imperative approach if you look at the declaration for "videoLoadStart" all you see is "videoLoadStart = signal(false)" the behaviour of this thing is controlled outside of its declaration which could be in one or more external places.

    • @Daniel-dj7vc
      @Daniel-dj7vc 10 месяцев назад

      @@JoshuaMorony i get the declarative part but ViewChild just feels like it creates tight coupling between the components. The loading state could easly be managed in a signal store and all of this would not be needed.

  • @figloalds
    @figloalds 10 месяцев назад +3

    RxJS makes for such messy and hard to debug code, no wonder you were having trouble doing something this simple.

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +3

      My experience has been that wherever I've used RxJS to enable declarative async code it's led to more organised and easier to debug code (and there have generally been less bugs)

    • @figloalds
      @figloalds 10 месяцев назад

      @@JoshuaMorony When you bind videoLoadStart$ in the template (I'm assuming that you're using this as a boolean?) and togglePlay$ emits false, does your template continue to see videoLoadStart$ as its truthy last emited value or does it emit undefined? I'm assuming this is a bool because subscribing is "return to monke", like going back to imperative and defeats the purpose.
      I'm really curious to see what this actually do and how resilient it is to "end-user creativity", and also what happens when things change (they always do) and you have to re-think everything to conform to the new specs. Also speaking about specs, how do you test this?

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +2

      ​@@figloalds I use the approach described in this video: ruclips.net/video/R4Ff2bPiWh4/видео.html which does intentionally have an imperative bridge between RxJS and signals either via a normal manual subscribe, or generally I prefer using "connect" from ngxtension but still it's technically a subscribe under the hood anyway. Having this one imperative step doesn't defeat the purpose of using a declarative approach though, and imo simplifies things a great deal (generally means you don't have to make more complex streams using scan to keep track of state). Essentially the imperative bridge between RxJS/Signals becomes essentially a redux style reducer step.
      In this particular case, the loading/playing state are tracked separately - videoLoadStart$ emitting will update the "status" state in the signal to "loading", videoLoadComplete$ emitting updates it to "loaded", but the actual playing/pausing of the video is tracked by the "playing" state in the signal, which is toggled by the "togglePlay$" source emitting.
      As for tests, this code hasn't been updated yet but you can see the general approach to testing for this here: github.com/joshuamorony/angularstart-giflist/blob/main/src/app/home/ui/gif-player.component.spec.ts

    • @figloalds
      @figloalds 10 месяцев назад

      @@JoshuaMorony thank you for answering, I get a better picture now. I said that it would defeat the purpose because you could "listen to events and set state" using the angular's native way of binding to events (someEvent)="someAction(. . .)". I was under the impression that you were using observables as a representation of a reactive state and binding it to the template using the async pipe or some directive. You mentioned that you needed some centralized "thing that you can post new events to so that the template updates", and that thing in pure angular is just a function, if you call "someAction(. . .)" outside of the event context, you get the same thing. This is why I am under the impression that using subscribe-like semi-imperative approach with rxjs kinda defeats the purpose
      I reckon, this application is not the best example of rxjs being useful, I will binge watch more of your videos to have a better idea on your approach to rxjs in general and cases where it truly excels

    • @JoshuaMorony
      @JoshuaMorony  10 месяцев назад +1

      ​@@figloalds Of course it's not up to me to decide what people should like, but I do find it worth it. It keeps things mostly declarative and where possible things are derived purely from their source in a "proper" declarative way which keeps the dependencies of things clear. But, in Angular there is always going to be some level of imperative code required, and the most realistic way to handle this in Angular imo is to have an event binding that nexts some subject or sets some signal where required - this should generally be avoided if possible, but sometimes that's not possible or isn't practical. The benefit of this is that it keeps the imperative portion as small as possible - rather than having a callback function that does some stuff, the imperative part is limited to just calling "next" with some payload. This acts as a new source, and then whatever needs to be derived from that can be done so declaratively.
      That stuff is imperative by necessity, and then I also intentionally choose to have a second imperative part where those sources are subscribed to in a reducer step to set the state. That isn't strictly necessary, I could just have everything derived from the source streams and never bother with this imperative step or signals, but I think it makes the RxJS required considerably more complex for not much gain.
      To be clear for this particular video, I think it's neither here nor there. Deriving the loadstart directly from its source here is nice and is a more declarative style, but it wouldn't be that big of a deal to do the same sort of event binding -> next/set imperative step.