I have an element directive and an attribute directive:
<my-element my-attribute="value"></my-element>
my-attribute is a directive which require ngModel:
app.directive('myAttribute', [
function() {
var definition = {
restrict: 'A',
require: 'ngModel',
link: function ($scope, $element, $attrs, ctrl) {...}
}
return definition;
my-element can be used without ng-model but the template of my-element contains input which always has ng-model. The my-attribute should be removed from my-element and added to input inside my-element. In my compile function in my-element directive I have:
var value = element.attr('my-attribute');
element.remove('my-attribute');
element.find('input').attr('my-attribute', value);
It works ok when attribute directive doesn't have require: 'ngModel'. But if I have ngModel required I get:
Error: [$compile:ctreq] Controller 'ngModel', required by directive 'myAttribute', can't be found!
Why? How can I fix this?
When you have require: 'ngModel' you'll receive an error if you don't have an ng-model attribute on that element the directive is on - that's what this directive property means. It says "give me the controller of ngModel's controller to be available as the 4th parameter in the link function.
To avoid this, if you don't always need to use it, you can add a ? sign before the ngModel and it means it's optional:
....
require: '?ngModel',
....
If you want to remove an attribute you should use
element.removeAttr('my-attribute');
I'm not sure about your intention in the link function, but you could make the link function in myAttribute run only if there is a ng-model attribute on the element:
require: 'ngModel',
compile: function(el, attr) {
if (attr.ngModel) {
return function(scope, elm, attr, ngModel) {
// link function
}
}
}
This is possible because angular doesn't try to find the required directives until it's going to execute the link function for that directive.
Related
I'm getting the ngModel name, but I also want to get the name of the form to which the element with the "validacion" directive belongs.
I strictly need to get the name of the form to which the HTML element belongs. I can have several forms so I need a dynamic solution. thank you very much
.directive('validacion', function ($timeout,$rootScope,validacionCampos,$compile) {
return {
restrict: 'AE',
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
if (!ngModel){
console.log("no hay modal")
return;
}
The following code should fetch you the value of name attribute of the form to which the validacion element belongs:
link: function (scope, element, attrs, ngModel) {
console.log(element.closest('form').attr('name'));
}
EDIT If jQuery is not there in the project, use the parent() function instead, provided that the directive element is the direct child of the <form> tag:
link: function (scope, element, attrs, ngModel) {
console.log(element.parent().attr('name'));
}
You should require the formCtrl:
require: '^form',
and use this in the link function of your directive:
link: function(scope, element, attrs, formCtrl) {
console.log(formCtrl);
}
For full details on passing forms into directives, see similar problem being solved at Stackoverflow here:
Pass form to directive
EDIT: With AngularJS 1.5.x you have different syntax (being able to skip the link function), just an citation of https://stackoverflow.com/users/1021943/priidu-neemre great answer, (see more with above mentioned link):
return {
restrict : 'EA',
require : {
form : '^'
},
I'm in need of doing transclusion together with manual compilation in link function and I'm wondering if there is a better way than what I'm pasting below as "Dynamic". It seems to be working but I'm not sure what are the consequences of removing ng-transclude attribute and I'm removing it due to Angular complaining to Error: [ngTransclude:orphan] Illegal use of ngTransclude directive in the template! No parent directive that requires a transclusion found. Element: <div ng-transclude="">.
Bottom-line is that I need to provide different templates for different instances of this directive with fallback to generic ones.
I can't use compile part. Here I have just simple example but I have also other cases with big link function that I would rather don't want to convert as this should be just "golden bullet" type of adjustment.
This little guy can be found here https://github.com/institut-de-genomique/Ultimate-DataTable/blob/master/src/directives/udt-form.js in original form.
Original
angular.module('ultimateDataTableServices')
.directive('udtForm', function () {
return {
restrict: 'A',
replace:true,
transclude:true,
templateUrl:'udt-form.html',
link: function(scope, element, attr) {
}
};
});
Dynamic
angular.module('ultimateDataTableServices')
.directive('udtForm', ['$compile', '$templateCache', function ($compile, $templateCache) {
return {
restrict: 'A',
transclude:true,
link: function(scope, element, attr, ctrls, transclude) {
var tplName = 'udt-form.html',
tplPath = scope.udtTable.configMaster.templates[tplName] || scope.udtTable.configMaster.templatesMaster[tplName] || tplName,
elem = angular.element($templateCache.get(tplPath));
elem.find('[ng-transclude]').removeAttr('ng-transclude').append(transclude());
element.replaceWith($compile($templateCache.get(tplPath))(scope));
}
};
}]);
TIA!
I've written a directive with an isolate scope.
app.directive('myDirective', function() {
return {
restrict: 'E',
scope {
attr1: '#',
attr2: '#',
noValueAttr: // what to put here?
},
link: function(scope, elem, attrs) {
// how to check here if noValueAttr is present in mark-up?
}
};
});
the html could be
<my-directive attr1='...' attr='...' ... no-value-attr>
or
<my-directive attr1='...' attr='...' >
I am wondering how to use (and have the directive detect if it's there or not) an optional attribute which has no assigned value. Thanks.
Just use attrs.hasOwnProperty('noValueAttr') in the link function to test whether the attribute is present or not.
Don't forget the attribute in markup would be no-value-attr, not noValueAttr like you showed.
link: function(scope, elem, attrs) {
if (attrs.hasOwnProperty('noValueAttr'))
// attribute is present
else
// attribute is not present
}
I know it is an old question, but there is that way to:
link: function(scope, elem, attrs) {
scope.noValueAttr = scope.$eval(attrs.noValueAttr) || 'default value';
}
In my directive, I'm setting ngModel as a dependency with 'require: '?ngModel'. But whenever the link function is called, ngModel is undefined.
Here's my full code:
myModule.directive('myDirective', function() {
return {
restrict: 'E',
require: '?ngModel',
link: function($scope, $element, $attr, ngModel) {
if (!ngModel) return;
console.log("This code never runs!");
}
}
})
You need to set the ng-model attribute for this directive in your HTML file.
Example:
<my-directive ng-model="foo"></my-directive>
I built two custom directives, datepicker and date-validation, and I'm having trouble with getting datepicker to include the date-validation directive. At first I had the datepicker and date validation directives as...
controls.directive('dateValidation', ['$filter', function ($filter) {
return {
require: 'ngModel',
restrict: 'A',
link: function (scope, element, attrs, ngModel) {
// validation methods here...
}
};
}]);
controls.directive('myDatepicker', [function () {
return {
restrict: 'A',
require: 'ngModel',
replace: true,
template: '<input type="text" date-validation />',
link: function (scope, element, attrs, ngModel) {
// inject date picker here...
}}
};
}]);
and this worked perfectly until I tried it in IE8. In IE8 it has a problem with the way the directive replaces the current element with the template and throws an error.
I then tried making replace equal false and took away the template. Then adding the "date-validation" attribute with jQuery and re-compiling the element using the $compile method like...
controls.directive('myDatepicker', ['$compile', function ($compile) {
return {
restrict: 'A',
require: 'ngModel',
replace: false,
link: function (scope, element, attrs, ngModel) {
element.attr({ 'date-validation': '' });
$compile(element)(scope);
// inject date picker here...
}}
};
}]);
But this didn't work either.
Also, the HTML that uses the directive looks like this...
<input type="text" ng-model="startDate" my-datepicker />
If anyone can advise me on how to include another directive inside a custom directive I would greatly appreciate it.