The ‘controller ngModel
required by directive’ error can be confusing when you first come across it, particularly if you get it when running your unit tests. But it’s a simple fix, and obvious when you understand what Angular’s trying to do.
Tl;dr
This error is caused by a directive with require: '^ngModel'
in its definition object, and no ngModel
attribute in the HTML used to compile the directive (either in your app or your tests).
Fixing the Problem
The AngularJS error:
Controller 'ngModel' required by directive
is thrown because your directive has the following property on its directive definition object (DDO):
require: '^ngModel'
…and you haven’t added an ng-model="somePropertyName"
attribute onto the directive’s declaration in its HTML.
To fix it, simply add ng-model
as an attribute to the HTML used to compile the directive, like so:
<myDirective ngModel="someProperty"></myDirective>
Eh?!
In Plain(er) English
Suppose you have the following directive:
angular .module('myApp') .directive('myDirective', myDirective); function myDirective(){ var directiveDefinitionObject = { templateUrl: '/partial.html', controller: 'myDirectiveCtrl', restrict: 'E', require:'ngModel', scope: {} } }
Note the line require ngModel
. This tells Angular that myDirective
must have the controller used by ngModel
.
When Angular sees this statement, it will do the following:
- look for the
ngModel
directive (which is a core directive) - access
ngModel
‘s controller (calledngModelController
) - pass
ngModel
‘s controller tomyDirective
‘s link function
Once this is done, ngModel
‘s controller (i.e. ngModelController
is available to use in the link function via the function’s controller argument:
link: function(scope, elem, attrs, controller){ // Angular has found ngModelController, and has passed it into the // link function as the controller argument. }
You’re happy, as you can use ngModelController
‘s properties and functions, and Angular’s happy, as it’s found what you told it to find.
But where does Angular find ngModel
? You need to tell it where it is by defining it explicitly as an attribute in myDirective
‘s HTML:
<myDirective ngModel="someProperty"></myDirective>
You also need to ensure it’s in the HTML fragment you use to compile the directive in its unit tests:
var compiledDirective = $compile(angular.element(
<myDirective ngModel="someProperty"></myDirective>
))(scope);