Angular 13 Fundamentals Custom Input - Control Value Accessor
Transcript from the "Custom Input - Control Value Accessor" Lesson
>> In this example we are going to talk about implementing a control value accessor control inside of a form. So, one of the things that I love about front end Masters is that, I get to have a lot of really interesting interactions with students even in a live workshop format.
[00:00:22] That individuals are able to ask questions and a lot of times I'm able to respond to them in real time, which is very, very cool. And this one in particular was requested by one of my friends, he asked me on Twitter could you show a control value accessor example, and so I am happy to comply.
[00:00:46] So what we have here is a custom input that we want to create, so the idea behind a control value accessor is that, you're taking a component, that ideally you can interact with in some way, and you're making it compliant or you're essentially fitting it in such a way that it can fit inside of a standard angular form.
[00:01:19] And so a good example of this might be if you had a jQuery component that you were wrapping that you wanted to just be able to drop in, and bind to it using ngmodel into a data binding. Well you can absolutely do that. There's just a little bit of a layer that you need to implement in order for that to work.
[00:01:44] And so what I have here is a custom input and what I'm doing is inside of this, I'm generating a random key on an interval. So I thought about like, what would be an example that might be plausible to anchor this into something that would be believable, make sense in that you might actually use it.
[00:02:09] And so one of the things that I deal with frequently is that, I'm trying to access something and have two factor authentication setup. And I have to open up an app, and I get a random sequence of characters that I need to then input into the field. So I'm just imagining a scenario where you are entering in a form.
[00:02:34] And within that you're randomly generating a key. So, what I wanna do is take this particular component, and I want to make it compliant with how a form control would work. And so here you can see that the key itself is, I believe every five seconds it's shifting but outside of this, that nothing is happening.
[00:03:01] So by the time we finish this and we wire this up, that when this changes here via ngmodel into a binding, it will update the outside world. So if we look real quick here, we have our custom control. The name is the key, so this is how we're tracing this out down here, and we're passing in the current key, and what we need is, when this changes through the ng model kind of channel, we want to update it down here.
[00:03:33] So within our custom control component, we have a pretty basic component. I have the key which is what we are binding to, I have a source stream and all this is doing is, it's firing after the first second and then five seconds after that, and I've also defined a sub property, the reason being is on inch on a knit.
[00:04:04] I am going to subscribe to the sources stream, aka timer, aka ticker, and I'm just gonna call generate key. When you call subscribe it returns, see if they'll tell me here, I won't know exactly what to say. So it's a subscription object, which from here, then on ngOnDestroy, being called this.sub.unsubscribe.
[00:04:34] So this is just us being good stewards over our code, and so that's why I have the sub property here so that when I subscribe, I can store a reference to that subscription and then unsubscribe when this is removed off the page. Now this generate method is, document driven development or Stack Overflow Driven Development sod, in that I basically said, how do I generate a random character of strings, and here we are.
[00:05:06] So Math.random().toString(36).substr(2, 8). And so it's good enough, I don't really care what's in here, and then I have a method generate key, which then sets the key property to whatever is returned from this .generate. So, a pretty cool component but it is certainly not form control compliant. And so what we're going to do is, we are going to implement one more interface, and this interface is going to be the value control accessor, I mean just double check my notes to make sure I do not go off the rails here, and get busted for fake news it's actually control value accessor, there we go, aright?
[00:06:01] And notice here, this is very upset because we are missing some properties. So, right value, register on change, register on touch. And so we'll go down at the bottom and we are going to start to implement this interface. So the first one we're gonna do is write value.
[00:06:29] And this is going to take, in this case I'm going to update this to take a string, and we're going to say, this.key = key, all right? And so when this loads, and this initial load is that it will write the value to the internal key property, so that it is updated.
[00:06:56] And from here, we need to register onChange. And all we're gonna do here is we're just gonna say, this.onChange = fn. And so this is just a convenient method here, but we will fix this in just a moment. So registerOnTouched, and this.OnTouch = function And so now it's saying well, where is this?
[00:07:36] And so what we're gonna do here, is we're going to define onChange, Any, and we are basically decide this to be a no op, so we're just going with the absolute simplest form of this that we can so, no op, here we go. And we should be compliant now, we have implemented the control value accessor interface.
[00:08:15] But what we also need to do is, we need to update the metadata to define this as a control value accessor. So we are going to go providers, and within this, we're going to define a new object which is going to go provide, and so Engie value accessor and we're going to go use existing, and forward ref, and then from here, we're just going to pass in or reference the custom control component.
[00:09:00] And then we're going to say that we are going to allow multi: true, all right? Let's save this, and let's just take a peek at our code and see what this looks like. So if I refresh this, yes if you saw real quick just dial in current key boom, so just for a second we were able to write that value, and then the key cycle took over.
[00:09:38] So we need to wire this up a little better. So back into our code. We can go into our generate key. And what we're going to do is we are going to update this to call this onChange. We're just gonna pass in this key. There we go. Now, if we come back you'll notice here that when it changes, it's also changing down here.
[00:10:17] Very cool, and if I click, I can kind of subvert this and we can do it faster than the cycle if you wanted to manually generate a new key. We could do that. Now, one thing that I want to just call out here is if we look over this element.
[00:10:41] You'll notice here that even though I've touched it, here like It's still registering as untouched. And so if you want to hook into the validation portions of the form control, then we need to add in some additional code. So let's go back into our example, and from here we are going to go into the template itself, and we're going to do a couple of things.
[00:11:23] So, on the input itself, I'm going to add in, I'm going to block, bind, to blur. And from here, I'm just going to call onTouch. And what I'm also going to do is on the button, I'm going to bind to the scope mouse out. And we're also going to call onTouch, okay.
[00:11:55] And now, if we come back to our example, I'm gonna pull this back up and let me inspect this. So it's untouched here. And when I come over here, and I mouse over, you saw that the minute that I did that, that the untouched property immediately came off so, we'll do this one more time so you'll see here nguntouched and then I'll go here and it's gone.
[00:12:37] So now it's ngtouch and so this is how we hook in to, not only communicating the value but the validity and the state of the control itself and so just to reiterate or review the code. Very quickly. What we've done is the first step, I believe is to implement your control value accessor, and then make it compliant.
[00:13:06] And so you have really three things you need to do. Write value, so that allows you to write the initial value. This is pretty simple. And then from here, register on changes, register on touched and I basically just put a no op in there and that is good enough.
[00:13:25] And then from here, when you wanna update, the value itself you just call this onChange, and if you want to set it to touch, or that has been touched, you can then just call onTouch. And so what we've done at this point is we have created a brand new form control.
[00:13:51] But one more thing, so I just totally pulled the Steve Jobs here. Notice that when I type, nothing is happening, it's not reflecting. So thinking cap on, how would we fix this? Well, we would need to take whatever we type and push that into an on changes event.
[00:14:15] So let's go back to our code real quick and let's tighten this up. And underneath of this, we're just going to go ngModelChange. And we're going to call onChange, we're just going to pass in the event, so let me just double check my spelling here, ng model change.
[00:14:43] And we're just calling on change yet again, back here. And now when I type this in, what I would expect is that it updates down here as well. So you just need to ask yourself, where do I want to surface, a meaningful change to the model and then call on change.
[00:15:02] So in this case we are doing it in a couple of places. So, when you type it directly in, you need to capture that on change. You can also do on touch to set it to touched, when we click the button we're generating the key but we're also updating it by calling this on change and so, there you go my friend this is how you do a control value accessor in Angular