Required directive controller is not present on the current DOM element - javascript

So I was wondering what actually "required directive controller is not present on the current DOM element" means
here is the link of error:https://docs.angularjs.org/error/$compile/ctreq?p0=ngModel&p1=contenteditable

You are missing the ngModel directive for your custom directive.
Try something like this:
app.directive('contenteditable', function () {
return {
restrict: 'E',
require: 'ngModel',
link: function (scope) {
// do something
}
};
});
and in your HTML file add ng-model
<contenteditable ng-model="name"></contenteditable>

From the angular.js documentation on directives
When a directive uses require, $compile will throw an error unless the specified controller is found. The ^ prefix means that this directive searches for the controller on its parents (without the ^ prefix, the directive would look for the controller on just its own element).

Related

How to set list of required attributes in AngularJS Directive?

I have created AngularJS directive that requires HTML element to:
1) be a child of the form;
2) have 'conditional-value' attribute;
To accomplish this I created directive like this:
.directive('onFocusoutDoSomething', function () {
return {
require : ['^?form', '^conditionalValue'],
restrict : 'A',
link : function(scope, element, attrs, ctrl) {
// ... do something
}
}
})
But for some reason I get this Exception:
Controller 'conditionalValue', required by directive 'onFocusoutDoSomething', can't be found!
Camel case directive name and attributes are replaces with dash cases by AngularJS, right?
If you have to create 'A' attribute type directive, it should be placed in scope{conditionalValue:'='} of directive or bindToController:{conditionalValue:'='}, not in require:{}, require is used for directive to directive communication.
If you have require in directive definition object, that directive is inheriting form parent directive mentioned in require field. for example if you have,
app.directive('tabSet',function(){
//DDO
});
tabs directive can inherit the controller properties of tabSet directive.
app.directive('tabs',function(){
return{
require:'^tabSet'
}
});

What is the meaning of "require: ^"?

What does the ^ mean under require in this angular directive?
I found this snippet and trying to figure out what it's saying.
.directive('accordionGroupHeading', function() {
return {
restrict: 'EA',
transclude: true,
template: '',
replace: true,
require: '^accordingGroup',
link: function(scope, element, attr, accessibleAccordionGroupCtrl, transclude) {
accessibleAccordionGroupCtrl.setHeading(transclude(scope, function() {}));
}
};
})
The require in this is not about require js it is actually saying that this directive requires a parent of type accordingGroup
take a look at the angular documentation for more info
https://docs.angularjs.org/guide/directive#creating-directives-that-communicate
From https://docs.angularjs.org/guide/directive
The ^^ prefix means that this directive searches for the controller on its parents. (A ^ prefix would make the directive look for the controller on its own element or its parents; without any prefix, the directive would look on its own element only.)
So basically you will have an error if your directive won't find the controller. Using require, you will be able to get access to the controller from the link method of directive.
link: function(scope, element, attrs, controller) {}

Add custom Directive to existing Input that already has angular directives [ng-model/ng-required]

I would like to use a standard input control that is decorated with ng-model and ng-required and then add my own custom attribute directive that provides uib-typeahead functionality to the control.
I used this link to get my directive partly working.
Add directives from directive in AngularJS
PLUNKR - The Version 2 of the directive does not work correctly with ng-model
My Directive does add typeahead functionality and that works quite well, but it is not binding the model on to the control after item is selected.
I have two version of my directive.
Version 1: is an element style directive and I have been using it successfully for a while, but it fell short when I wan't to have a bit more control over the input element, especially when I wanted to use ng-required='true' and other ng-message directives.
Version 2: is an attribute style directive, I went with this because I felt it was better to just add the typeahead functionality that I wanted to any standard HTML that can optionally use ng-required='true', ng-model etc...
While this directive is mostly working, it does not interact correctly with ng-model and I'm not sure how to get it working
angular.module(APP)
.directive('wkLocationSuggest', ['$compile', function ($compile) {
return {
restrict: 'A',
require: 'ngModel',
replace: false,
//terminal: true,
//priority: 0,
scope: {
wkApiModel: '=' // Provide access to the internal data that is returned via the API lookup
},
controller: 'LocationSuggestController',
link: function (scope, element, attrs, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
element.attr('typeahead', 'location as row.location for row in typeAhead($viewValue)');
element.attr('typeahead-wait-ms', '750');
element.attr('typeahead-on-select', 'onSelectInternal($item, $model, $label)');
element.attr('typeahead-min-length', '2');
element.attr('typeahead-focus-first', 'true');
element.removeAttr("wk-location-suggest"); //remove the location-suggest to avoid indefinite loop
element.removeAttr("data-wk-location-suggest"); //also remove the same attribute with data- prefix if it exists
// None of this is working
//// invoked when model changes from the outside
//ngModelCtrl.$render = function () {
// //scope.innerModel = ngModelCtrl.$modelValue;
//};
////// invoked when model changes from the inside
//scope.onChange = function (value) {
// ngModelCtrl.$setViewValue(scope.innerModel);
//};
scope.onSelectInternal = function ($item, $model, $label) {
// This fires, but it effects the ng-model on the first input,
// but not the input that this directive is attached too
ngModelCtrl.$setViewValue($item.location);
};
$compile(element)(scope);
}
};
}]);
These two images demonstrate part of the problem, may be better to test for yourself using PLUNKR above
I initially tried to dynamically add validators to your wk-location-suggest-new directive by implementing blur on the input element in combination with ngModel's $setValidity method; but don't know what exactly was preventing the event from firing.
Therefore, I turned to the other directive wk-location-suggest-old and tweaked it a bit to fit in both desired behaviors.
There, I noticed that you were missing a couple of things:
First of all, in order for a form element to glue with the form itself (wkProfileCompany in your case), and to work with ng-model, the element (in the directive template) needs a name.
Secondly, ng-required (or required) would work with the form only if it is added as an attribute to the element in the directive template, not the directive which compiles to the template containing the element.
Directive Definition
As you may notice, I've passed two properties from the outer scope to the directive's inner scope, namely:
the name of the input element,
and an isRequired flag as to specify whether the input is required or not.
.
.directive('wkLocationSuggestOld', [function () {
return {
restrict: 'E',
require: '?ngModel',
scope: {
name: '#', // <==
isRequired: '=' // <==
},
template: '<input name="{{name}}" type="text" class="{{innerClass}}" ng-model="innerModel"'
+ ' ng-change="onChange()" uib-typeahead="location as row.location for row in typeAhead($viewValue)" '
+ ' typeahead-wait-ms="750" typeahead-on-select="onSelectInternal($item, $model, $label)" '
+ ' typeahead-min-length="2" typeahead-focus-first="true" '
+ ' ng-required="isRequired">', // <== added ng-required here
controller: 'LocationSuggestController',
link: function (scope, element, attrs, ngModel) {
if (!ngModel) {
return;
}
...
}])
HTML
Finally, you can use the tweaked directive in your HTML as such:
<wk-location-suggest-old class="form-control" type="text" name="location2" ng-model="location2" is-required="true"></wk-location-suggest-old>
Plunker
Update
One of the possible reasons for ng-model not correctly binding in the wk-location-suggest-new directive to a provided value (i.e. location3) is that you are replacing the whole DOM element with a new custom DOM element which is compiled with the isolated scope of the directive itself.
Since the directive wk-location-suggest-new has an isolate scope, the scope is totally unaware of location3, because location3 (and all the other location values) are defined in the scope of MainCtrl and NOT the scope of the directive itself; therefore, you'll end up binding the input's value to an undefined property.
link: function (scope, element, attrs, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
...
$compile(element)(scope); // <== here
You need to update your model in setTimout() like below as you have an isolated scope in the directive.
setTimeout(function () {
scope.$apply(function () {
scope.location3 = 'Your selected value'
});
}, 2000);
Alternatively you can also utilize $timeout service to achieve the same result.

AngularJS directive parameter "ctrls"

I see a link function in a directive in Angular like so:
link: function (scope, element, attrs, ctrls) {
var ngModelCtrl = ctrls[0],
invalidInputController = ctrls[1];
// ...
}
Can someone help me understand where the controllers in the ctrl parameter are coming from?
Edit: the directive has an angular require property specifying two other directives by name like so:
require: ['ngModel', '?numberFormatterPreventInvalidInput'],
I suspect they come from there.
Typically they come from the require part of a directive, which is either a string or an array of controllers.
['^something', '^another']
Within link they are accessed by ctrl[0] and ctrl[1].
Also from the documentation:
The basic difference (between controller and link) is that controller can expose an API, and link
functions can interact with controllers using require.
Best Practice: use controller when you want to expose an API to other
directives. Otherwise use link.
if in your directive is written
require:["ngModel","^directiveTwo"]
than ngModel , directiveTwo are names of directives and directiveTwo has to be a parent directive
link: function (scope, element, attrs, ctrls) {
var ngModelCtrl = ctrls[0],
controllerOfDirectiveTwo = ctrls[1];
// ...
}
ctrls the last parameter isthe array of the controller of the directives defined by require so for ngModel this :https://docs.angularjs.org/api/ng/type/ngModel.NgModelController for your directives defined by the controller: in your directive

Angularjs Multiple directives [gridsection, gridsection] asking for template on: <div gridsection="">

I'm receiving the error: Multiple directives [gridsection, gridsection] asking for templateon : <div gridsection=""> with this code.
I don't see how i'm using nested directives or what is causing this.
html page
<div gridsection ></div>
directive
angular.module('web').directive('gridsection', function() {
return {
restrict: 'A',
replace: false,
scope: {
patient: "=patient"
},
templateUrl: 'directive/section.html',
link: function(scope, element, attrs, fn) {
}
};
});
directive/section.html
<div>
here?
</div>
It seems like you are declaring the gridsection multiple times in your angular code.
I have seen this before when I have a copy of a directive script file in a folder.
i.e. my file structure was
* myDirective.js
* myDirective - copy.js
So essentially I had two directives with the same name.
Doh!
Note originally posted this as a comment but created as an answer in response to comment from #jayjayjay
For posterity, I was getting this exception because I was trying to create a directive named pager and that was colliding with Bootstrap's pager.
Make sure you didn't include the script tag twice.
I had this issue but only declared the directive once in the markup, turns out it was because I included the script twice.
Note: I saw this in one of the comments for another answer and posted it as an answer for easier access/to prevent it getting lost.
I was getting this error for a reason not specified in other answers.
I was using declaration for xyz directive as <xyz xyz="xyz"></xyz>
my definition was:
angular.module('app')
.directive('xyz', function () {
return {
templateUrl: '..../xyz.html',
restrict: 'EA',
scope: {
xyz: '='
},
link: function (scope, element, attrs) {
}
};
});
The problem here is that I allowed directive to be used as element and attribute. so <xyz xyz="xyz"></xyz> contained both the declaration which was causing the issue.
Solution is to either restrict the directive to be used as Element only restrict: 'E' OR change the name of the directive to something like xyzView and use it like <xyz-view xyz="xyz"></xyz-view>.

Categories

Resources