Require one attribute between choices in directive - angularjs

I am creating an angular directive and I want the user to specify a 'type' of the directive.
For example:
<my-directive type-a></my-directive>
or
<my-directive type-b></my-directive>
or
<my-directive type-c></my-directive>
I know I can do:
<my-directive type="a"></my-directive>
and then require the type attribute but then I'm doing string matching. Is there anyway to do this by requiring one of 'type-a', 'type-b', or 'type-c' to be present?

Without much background info, I came up with this solution.
JSFIDDLE
So basically myDirective has a controller which is shared by type directives (type-a, type-b.. and so on). The type directive sets the type on the scope of myDirective.
myApp.directive('myDirective', function() {
return {
restrict: 'E',
controller: function($scope) {
$scope.type = '';
this.setType = function(type){
if($scope.type === '') $scope.type = type;
else throw 'type can be only defined once. Current type is '+$scope.type
}
},
link: function(scope, elem, attrs) {
console.log(scope.type);
}
}
});
myApp.directive('typeA', function() {
return {
restrict: 'A',
require: '^myDirective',
link: function(scope, elem, attrs, ctrl) {
ctrl.setType('typeA');
}
}
});
myApp.directive('typeB', function() {
return {
restrict: 'A',
require: '^myDirective',
link: function(scope, elem, attrs, ctrl) {
ctrl.setType('typeB');
}
}
});

I think you can do <div data-my-directive="a"></div> which is a lot safer for cross-browser and w3c. Then the directive would be something like:
.directive('myDirective', function() {
return {
restrict: 'A',
scope: {
type: '='
},
link: function(scope,element,attrs){
}
};
});

Related

How to invoke a directive by using element name?

I have a angular directive:
app.directive('myDirective', function()
{
return{
restrict: 'AE',
scope: {
myCustomer: "&"
},
require: 'ngModel',
link: function(scope, element, attr, ngModel){
var oldVal;
function fromUser(value){
scope.myCustomer()(value, oldVal);
oldVal = value;
return value;
}
function toUser(value){
return value;
}
ngModel.$parsers.push(fromUser);
ngModel.$formatters.push(toUser);
}
}
}
At the moment I invoke this director by using attribute name and bind it to a function:
<input type="text" my-directive="showInput" ng-model="user.name">
It works fine but what I want is to use element name, something like this:
<my-directive>
The problem is I don't know how to bind to a function as I do with the attributes.
You will need to set 'restrict' to 'E' in your directive definition something like:
bindToController: true,
controller: 'YourController',
controllerAs: 'vm',
restrict: 'E', //<----this is what you want
templateUrl: 'template.html'
You have to pass restrict: 'E' in directive options
angular.module("image-management")
.directive('myDirective', ($modal) => {
return {
restrict: 'E',
scope:{
showInput: '&'
},
template: '',
link: function(){}
})
<my-directive showInput="showInput" ></my-directive>
<my-directive some-function="someFunction"></my-directive>
Then in your directive link function, it is accesible through attr
link: function(scope, element, attr, ngModel){
// Your logic...
attr.someFunction();
}
As #sumair answered, you can do:
<my-directive showInput="showInput" ></my-directive>
But, if you really want to use only
<my-directive>
AND your directive does not require to have an isolated scope, you can just leave the scope property of the directive definition and reference your showInput function directly from inherited scope like so:
app.directive('myDirective', function()
{
return{
restrict: 'AE',
/*scope: { ////// remove this part //////
myCustomer: "&"
},*/
require: 'ngModel',
link: function(scope, element, attr, ngModel){
var oldVal;
function fromUser(value){
scope.showInput()(value, oldVal);
oldVal = value;
return value;
}
function toUser(value){
return value;
}
ngModel.$parsers.push(fromUser);
ngModel.$formatters.push(toUser);
}
}
}

How to make expressions computable in custom directive? (angularjs)

I have a custom directive:
<custom-directive style='height: {{window.innerHeight + "px"}}'></custom-directive>
myApp.directive('customDirective', function(){
return {
restrict: 'E',
templateUrl: '/views/controls/customTemplate.html'
};
});
So, the expression style='height: {{window.innerWidth + "px"}}' doesn't work. App just ignores it. How to make it work?
Write a link function in directive where you will get directive element as and argument use that to change the css of the element.
Like if you want to change the color of the directive text do like this -
link: function(scope, elem, attr) {
elem.css('color','red');
}
Diective
myApp.directive('customDirective', function(){
return {
restrict: 'E',
templateUrl: '/views/controls/customTemplate.html',
link: function(scope, elem, attr) {
elem.css('color','red');
}
};
});

Passing Date/moment object via attribute in AngularJS

I have a directive and I'm trying to pass Date/moment object via attribute. I'm passing it like this: (I know, that I can create isolated-scope and bind it, it is not the case)
<form name="form">
<input name="field" ng-model="fieldModel" form-field-directive field-date="{{fieldDateModel}}" />
</form>
Without curly brackets the result is obvious, but with I'm getting such quoted string "2015-07-03T10:35:13.691Z".
Is there anyway to work with it?
UPDATE:
angular.module('app', [])
.controller('AppCtrl', function($scope) {
$scope.fieldDateModel = moment(); // new Date()
});
angular.module('app')
.directive('formFieldDirective', function() {
return {
restrict: 'A',
require: '^ngModel',
link: function(scope, iElement, iAttrs, ngModelCtrl) {
ngModelCtrl.$validators.fieldDate = function() {
if (angular.isUndefined(iAttrs.fieldDate)) {
return true;
}
console.log(iAttrs.fieldDate);
};
}
};
});
You can actually pull the value from the parent scope using $parse which is more reliable.
angular.module('app')
.directive('formFieldDirective', function($parse) {
return {
restrict: 'A',
require: '^ngModel',
link: function(scope, iElement, iAttrs, ngModelCtrl) {
ngModelCtrl.$validators.fieldDate = function() {
if (angular.isUndefined(iAttrs.fieldDate)) {
return true;
}
console.log(($parse(iAttrs.fieldDate)(scope)).format());
};
}
};
});
http://jsbin.com/qoheraloge/1/edit?js,console,output

Can an angularjs Directive require Itself?

Can a directive require itself? Here's an example:
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^menu',
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
In my test it doesn't seem to work (ctrl is always undefined). See the plunk
BTW, after this question was answered I discovered that in this case the caret (^) has no effect and the controller passed to the link function is always the instance's own controller. [ plunk ]
You should directly define controller function to expose directive API to other directives:
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^menu',
controller: function($scope){ },
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
See http://plnkr.co/edit/cKFuS1lET56VOOYD5rrd?p=preview
With angular 1.4x, you actually can now limit the require statement to parent elements only and exclude the element itself. If you change
require: '?^menu' to require: '?^^menu' (notice the second caret) so that you get
app.directive('menu', function () {
return {
restrict: 'E',
require: '?^^menu',
controller: function($scope){ },
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});
the code now works as expected.
See http://plnkr.co/edit/2uDUO0LcgDX7xEuBtsJ2?p=preview
I guess here the problem is not with the directive referencing itself. The directive will not know which controller to refer to until specified or defined. To access a controller either it has to be defined or referenced in the directive as below.
app.directive('menu', function () {
return {
restrict: 'E',
controller: 'MainCtrl',
require: ['?^menu'],
link: function(scope, element, attrs, ctrl) {
console.log('ctrl: ', ctrl[0]);
if (ctrl) {
element.addClass('nested');
} else {
element.addClass('notnested');
}
}
};
});

AngularJS directive use required controller in controller

Say I have the following two directives that work with each other
directive('parent', function() {
return {
scope: {},
require: 'ngModel',
controller: function($scope) {
this.doSomething = function() {
//How to use ngModelController here?
};
},
link: function(scope, element, attr, ngModelController) {
}
};
});
directive('child', function() {
return {
scope: {},
require: '^parent',
link: function(scope, element, attr, parent) {
parent.doSomething();
}
};
});
How can I use the ngModelController from within the controller of parent? I suppose I could do scope.ngModelController = ngModelController inside the link function but that seems hacky. Is there a better solution?
Is this any less hacky?
directive('parent', function() {
var ngModelCtrl = null;
return {
scope: {},
require: 'ngModel',
controller: function($scope) {
this.doSomething = function() {
//How to use ngModelController here?
if (ngModelCtrl) {
}
};
},
link: function(scope, element, attr, ngModelController) {
ngModelCtrl = ngModelController;
}
};
});

Resources