Controller 'ngModel', required by directive '…', can't be found
Asked Answered
P

1

15

What's going on here?

Here are my directives:

// template <input ng-model="theModel"  />
app.directive('bseInput', function () {
    return {
        templateUrl: "/Scripts/bse/bse-inputs.html",
        scope:
        {
            theModel: '=',
        },
        compile: function compile(tElement, tAttrs, transclude) {

            // do stuff

        }
    };
});


app.directive('submitRequired', function (objSvc) {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {

          // do something
        }
    };
});

Here is an example of the directive in use:

<input bse-input submit-required="true" the-model="someModel"></input>

Here is the actual error text:

Error: [$compile:ctreq] Controller 'ngModel', required by directive 'submitRequired', can't be found! http://errors.angularjs.org/1.2.2/$compile/ctreq?p0=ngModel&p1=submitRequired at http://www.domain.ca/Scripts/angular/angular.js:78:12 at getControllers (http://www.domain.ca/Scripts/angular/angular.js:5972:19) at nodeLinkFn (http://www.domain.ca/Scripts/angular/angular.js:6139:35) at compositeLinkFn (http://www.domain.ca/Scripts/angular/angular.js:5550:15) at nodeLinkFn (http://www.domain.ca/Scripts/angular/angular.js:6132:24) at compositeLinkFn (http://www.domain.ca/Scripts/angular/angular.js:5550:15) at publicLinkFn (http://www.domain.ca/Scripts/angular/angular.js:5458:30) at http://www.domain.ca/Scripts/angular/angular.js:1299:27 at Scope.$get.Scope.$eval (http://www.domain.ca/Scripts/angular/angular.js:11634:28) at Scope.$get.Scope.$apply (http://www.domain.ca/Scripts/angular/angular.js:11734:23) angular.js:9159 (anonymous function) angular.js:9159 $get angular.js:6751 nodeLinkFn angular.js:6141 compositeLinkFn angular.js:5550 nodeLinkFn angular.js:6132 compositeLinkFn angular.js:5550 publicLinkFn angular.js:5458 (anonymous function) angular.js:1299 $get.Scope.$eval angular.js:11634 $get.Scope.$apply angular.js:11734 (anonymous function) angular.js:1297 invoke angular.js:3633 doBootstrap angular.js:1295 bootstrap angular.js:1309 angularInit angular.js:1258 (anonymous function) angular.js:20210 trigger angular.js:2315 (anonymous function) angular.js:2579 forEach angular.js:300 eventHandler angular.js:2578ar.js:7874

Pisistratus answered 16/2, 2014 at 6:4 Comment(0)
R
22

Just in case, that the above <input> snippet does not contain a typo, this is the issue:

the-model

we need ng-model

<input bse-input submit-required="true" ng-model="someModel.Property"></input>

angular is using normalized/denormalized naming conventions, which at the end means: ng-model is the html way how to express the ngModel. HTML is case insensitive... and this solves this issue

Suggestion. If we are working with multiple directives applied to one element:

  • bse-input
  • submit-required

We should let both of them to work with a standard INPUT settings. So, both should could require ng-model, as a way how to access the model passed to input.

if the-model should be representing different setting, which is absolutely ok, we just do not have to skip passing the ng-model as well

About require:

When you have nested directives that need to communicate with each other, the way to do this is through a controller.

Other directives can have this controller passed to them with the require property syntax. The full form of require looks like:

require: '^?directiveName'

Explanations of the require string:

  • directiveName: This camel-cased name specifies which directive the controller should come from. So if our directive needs to find a controller on its parent , we’d write it as myMenu.
  • ^ By default, Angular gets the controller from the named directive on the same element. Adding this optional ^ symbol says to also walk up the DOM tree to find the directive. For the example, we’d need to add this symbol; the final string would be \^myMenu.
  • ? If the required controller is not found, Angular will throw an exception to tell you about the problem. Adding a ? symbol to the string says that this controller is optional and that an exception shouldn’t be thrown if not found. Though it sounds unlikely, if we wanted to let s be used without a container, we could add this for a final require string of ?\^myMenu.
Retired answered 16/2, 2014 at 6:8 Comment(10)
the-model and theModel were intentional because of wanting to pass ngModel to the directive with two-way data-binding.Pisistratus
if you use the require it means: Other directives can have this controller passed to them. so, the attribute require is related to the other directive not to the model. I mean, if you will have setting require: 'ngModel' then the HTML attribute must contanin ng-model. The link function, will then in the ctrl variable recieve that controller, just requiredMauldin
I don't understand that last comment. Does require: 'ngModel' mean that we are passing an ngModel controller?Pisistratus
Would you say, that now it all makes more sense?, Please, if you can, please, do read this docs.angularjs.org/guide/directive the angular guide to directives. This is explained there, search for: ^myTabs. Enjoy Angular, it is amazing tool ;)Mauldin
You mention that the link function, will then in the ctrl variable, receive the controller. What is that controller? How is it related the require: 'ngModel'?Pisistratus
The biggest trick there, is that the directive and controller in this particular case are equal. Look, what we can get in another directive - is in fact a controller of the other. We do not have access to the others directive "definition" ... we do have access to the controller. And this is the way how we can communicate among them. please read the directive guide - it is there ;)Mauldin
Yes. Things do make more sense now! Now, directive and controller are equal (in this scenario); directives, though, also have link, compile, and controller properties. Hmm. I would like to read the directive guide; time constraints prevent. Soon!Pisistratus
I mean, they are equal in this scenario. When we do require ngModel, we are taliking about ngModel directive, but what we will get in runtime is the ngModel controller... this is a way how angular was designedMauldin
Got it. require: 'ngModel' means that the ng-model attribute must be present in the markup. require also means that, at run-time, the directive's link function will have access to the controller for the ngModel directive. Wow! Thank you!Pisistratus
All the confusion comes from being unaware that directives can have controllers which look pretty much like any controller, but should be mentioned separately as "directive controllers". Those controllers are essentially a way for directives to expose an API for other directives to interact with. So when you do for example require: 'ngModel' you're asking for the controller of the ngModel directive, which is ngModelController. You can define a controller in your directive by setting the controller property of the directive definition object to a valid controller (func or name string).Katzman

© 2022 - 2024 — McMap. All rights reserved.