In the last post I explained that developers have a lot to gain by making sure their custom controls work properly with ng-model
.
That post shows the starting point – making trivial things like the required
validation work and having the form’s $valid
property take into account custom controls.
But that’s just the tip of the iceberg, and ng-model
allows for quite a bit more customization and integration in order to allow writing controls that work as smoothly as builtin ones.
In this post I’ll how to start integrating your control with the NgModelController
and make your controls more capable and robust.
Our Starting Position
Let’s keep going with the example from the previous post, which was this very simple component:
1 2 3 4 5 6 |
|
A good first step would be to note that we can use this control with the name
attribute, since ngModel
looks for it:
1 2 3 4 |
|
By supplying name="foo"
we can now access it from the form to make sure it’s valid, e.g. $ctrl.form.foo.$valid
.
Changing Values Properly
In order to make our component work seamlessly with ng-change
we will need to make sure that whenever the control’s value is changed as a result of a user interaction (not programmatically), we let NgModelController
know.
First, we will need to make sure to require
the NgModelController
, and then, when the user clicks a button, invoke $setViewValue
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
The important bits here are the require
definition and the handling of the user’s click in userToggledOn
, which calls NgModelController
’s $setViewValue
.
Note that second parameter which lets it know what kind of DOM event triggered the change.
Defining emptiness
In my previous post I showed how required
can simply be dropped in and used once ng-model
is in place.
That’s only the case, though, if your definition of “emptiness” matches the default logic as described in the documentation.
But in case your control’s logic doesn’t match this, e.g. your model is an array and emptiness means the array is empty, you should override this behavior to let NgModelController
know what you expect.
Inside your controller, after requiring ngModel
as shown above, do this:
1 2 3 4 5 6 7 8 |
|
As you can see, we’re overriding the $isEmpty
method, which is intended exactly for this purpose.
Also, note I’m making sure to access ngModelCtrl
on $onInit
, since it will not be defined earlier.
Handling Programatic Changes
An important part of the integration is to make sure the view is changed whenever the model value gets changed programmatically.
For example, if the control’s ng-model
attribute is a binding from its parent, and the parent changes that value, it usually means that the control should update the UI in order to show this state (e.g. because an update was received from the server).
In those scenarios, NgModelController
expects us to override the $render
method.
NgModelController
places a $watch
on its value, and calls $render
when it needs to change (though, note this watch is a shallow watch. If you’re mutating an object as your model, you will need to trigger it manually).
This would look roughly like so:
1 2 3 4 5 6 7 8 9 |
|
That’s it for now.
There’s much more to ng-model
, e.g. parsers and formatters that are handy when you want specific validations on inputs, etc.
To be updated when I write about it, subscribe in the form below!
“Maintaining AngularJS feels like Cobol 🤷…”
You want to do AngularJS the right way.
Yet every blog post you see makes it look like your codebase is obsolete.
Components? Lifecycle hooks? Controllers are dead?
It would be great to work on a modern codebase again, but who has weeks for a rewrite?
Well, you can get your app back in shape, without pushing back all your deadlines!
Imagine, upgrading smoothly along your regular tasks, no longer deep in legacy.
Subscribe and get my free email course with steps for upgrading your AngularJS app to the latest 1.6 safely and without a rewrite.