In the last post I explained that developers have a lot to gain by making sure their custom controls work properly with
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
name="foo" we can now access it from the form to make sure it’s valid, e.g.
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
First, we will need to make sure to
NgModelController, and then, when the user clicks a button, invoke
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
Note that second parameter which lets it know what kind of DOM event triggered the change.
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
$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
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.