5.9 Observing Form State Changes
In this lesson you'll see how to use observables to listen for changes in either control value or form status. You'll also learn a little bit about what observables are and how they can be used.
1.Introduction6 lessons, 42:00
2.Get Started With Angular7 lessons, 42:38
3.Core Concepts7 lessons, 55:20
4.Template Deep Dive11 lessons, 1:10:56
5.Forms10 lessons, 1:45:41
6.Routing9 lessons, 1:15:10
7.Using the HTTP Client5 lessons, 56:24
8.Testing10 lessons, 1:23:27
9.Building for Production1 lesson, 03:40
10.Conclusion1 lesson, 01:32
5.9 Observing Form State Changes
Hi folks. In this lesson, we're going to look at how we can observe changes in the state of a form or individual form controls from within a component. There are multiple different ways that we can do this, ranging from simple event handling to more complex state change observables. If we want to observe the changes of a single element, like a text input. One way to do that is to use an event binding directly on the element. So, for example, if we wanted to be notified when someone typed into the telephone field in our feedback form. We could do something like this. And then we would need to add this handle input method to the component class. And let's go back to the browser now. As we start to enter characters into the input we start to see our logs in the console. So this approach works and it could be fine for simple situations, but there are problems with doing it this way. It's a reactive form, not a template driven form. So, it's not fantastic to go against that philosophy and start adding things in the template. Sure, it's only one event binding in this case, but what if we wanted to watch all of the inputs for changes? Now there are more bindings and the template is even more complex. I mean it's pretty complex as it is anyway. Partly that's because of my screen resolutions. But it's still pretty complex. So the last thing that we wanna do is make that more complex. This technique also relies on passing the event object. There are ways around this, sure. We could use a template reference variable to avoid passing the event object. But then there's even more stuff going into the template. But also, this approach has other limitations. What if we don't want our handler to be invoked on every single keypress? We'd need to add our own debouncing logic into the component and that would make the component more complex, as well as the template. So event binding works but it's basic and it has limitations. A better way would be to observe changes in the control from within the component and then we don't have to change the template at all. So let's get rid of the binding in the template. And we can get rid of the handler back in the class. Angular creates a number of things that we can use to subscribe to changes in our forms. One of these is called value changes, and we can subscribe to this in order top be notified whenever the controls value changes. Let's add a subscription in the ng on Init method for changes in the telephone inputs. We can get the control that we are interested in using the useful get method, and we parse it the name of the control that we want to get, just like if we were using that in a template which we have done previously. All controls have these value changes property which contains an observable that we can subscribe to using the subscribe method. This method takes a callback, which we used an arrow function for in this example. This handler has passed the value that changed. So let's save that, and let's go back to the browser again. And it's pretty much the same as before. We can see the console log statement every time we press a key. So, personally, I think this is already a big improvement. First of all, all of the changes are in one place. They're not split between the component and the template. Also, we don't have to work with the complex event object. We receive only the value which changed in the callback. These are already big benefits. But also, the whole observable concept gives us access to much more powerful tools. Let's say that we want to delay the console log so that we don't get the value change on every single keystroke. With observables this is easy. We just need to import the helpful debounce time operator from RXJS. Now we can pipe this function to our observable. The debounds function delays the firing of the observable by the specified number of milliseconds. So let's go back to the browser now. So this time we can type five or six characters easily before we see the first console log. Okay so now you've seen some of the benefits let's take a moment to clarify what observables actually are. Observables are similar conceptually to promises. And they're useful for asynchronous code, just like promises. We're interested in handling some event at some future point but we don't know when. So we can add a handler that will be in vote at some future point whenever that event occurs. The difference between observables and promises is that promises are intended for a single result at some future point. Whereas observables are intended for a stream of possibly many results at many future points. In our example here, a promise would not be suitable for watching value changes, because there could be many value changes. Observables also have a much richer API than promises, and can be retried, canceled, and much, much more. This brings me to the next point about observables. Unsubscribing. We've subscribed to these value changes inside the ngOnInit method, and this method will be fired every time the component is initialized. Which for us is every time the feedback form is opened. Look, if we add a console log to the top of ngOnInit. And now go back to the browser. We can see that we see that console log every single time the feedback form is opened. The point here is that we could end up with memory leaks. Angular takes care that we aren't adding multiple subscriptions for the same event, so we shouldn't any duplicate logs for the value changes, but in more complex forms I've experienced these memory leaks firsthand. And so it can happen and it can cripple an application if not handled properly. What we should do is cancel the subscription when we're done with the component. Thankfully this is incredibly easy to do. So first of all, let's bring some extra type information into our component. This also comes from the RxJS library, which is a separate third party library but comes included with Angular. We also need to bring in another of Angular's lifecycle methods. We already have the Oninit interface but we'll need the ondestroy interface as well. And let's add that to the list of interfaces that the class implements. And now we need to add an ng on destroy method. We'll populate this method in just a moment. Okay, so when we subscribe to something, the subscribe method returns an observable. And this is what we'll need to unsubscribe. So we'll need to store this as a property of the component. We don't need to expose this proxy to the template or outside of the component at all, so we can keep it private, and it will be of the type subscription. So now when we bind to value changes, we just need to capture the return and store it in this property. And now we can fill out the ng on destroy method. All we need to do to dispose of the subscription, is call the unsubscribe method of the subscription objects. This simple line of code will help us to avoid any impacts on performance due to memory leaks. It's recommended to always unsubscribe from any subscriptions of observables that we create. Okay, so although interesting we don't need to watch our telephone input for value changes. Let's make a slight change to see that we can also subscribe to status changes of the form itself. Okay, so this time we are binding to status changes on the entire form. Although you should note that you can do this for individual form controls as well. This time the callback function receives the status of the form as an argument. And if we go back to the browser now. We should start to see locked messages of the form status which is currently invalid. And now we can see that the last few log messages say valid because once we filled out the form correctly, the form should become valid. So, this can be extremely useful to monitor when the form becomes valid. Again, this example is purely to show you these concepts. We don't need this in this particular form, so let's go ahead and remove everything that we've added so far in this lesson. Or, if you prefer, you can just comment now and play around with it later. So, I just want to remind you that observables aren't a part of Angular. They are a part of this external third-party library called RxJS, and we used one of the operators from this framework, the. As well as this, there are many, many others. So this webpage shows all of the RxJS version 5 operators that can be used. You can see there are many, many of them. For all kinds of different situations. So I'd recommend that you come back here at some point and just have a look through some of these. And also if you want to learn a bit more about RxJS itself, and observables in general, this site also contains a wealth of information all about RxJS. So in this lesson we saw how to watch either a form or individual form controls for either value changes or status changes using the concept of observables. We've seen how to use the subscribe methods where they handler for events that we want to observe. We've seen how to use operators like Debounce time, and we've learned we should always unsubscribe from observables when we no longer need them. Thanks for watching.