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

shA.t
  • 16,580
  • 5
  • 54
  • 111
Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467

1 Answers1

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.
Radim Köhler
  • 122,561
  • 47
  • 239
  • 335
  • the-model and theModel were intentional because of wanting to pass ngModel to the directive with two-way data-binding. – Shaun Luttin Feb 16 '14 at 06:12
  • 6
    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 required – Radim Köhler Feb 16 '14 at 06:15
  • I don't understand that last comment. Does `require: 'ngModel'` mean that we are passing an ngModel controller? – Shaun Luttin Feb 16 '14 at 06:18
  • 2
    Would you say, that now it all makes more sense?, Please, if you can, please, do read this **http://docs.angularjs.org/guide/directive** the angular guide to directives. This is explained there, search for: `^myTabs`. Enjoy Angular, it is amazing tool ;) – Radim Köhler Feb 16 '14 at 06:25
  • 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'`? – Shaun Luttin Feb 16 '14 at 06:28
  • 1
    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 ;) – Radim Köhler Feb 16 '14 at 06:31
  • 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! – Shaun Luttin Feb 16 '14 at 06:34
  • 1
    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 designed – Radim Köhler Feb 16 '14 at 06:35
  • 4
    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! – Shaun Luttin Feb 16 '14 at 06:37
  • 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). – Alejandro García Iglesias Aug 05 '14 at 19:30