How can I pass multiple params into a directive? - angularjs

I have a directive:
app.directive('testDir', [function () {
return {
require: '?ngModel',
link: function ($scope, elm, attr, ngModel) {
var abc=attr.testDir;
var def=< The value zzz >
}
};
}])
I understand I can call this like:
<div test-dir='abcd'>xx</div>
What if I also needed to pass the parameter 'zzz'. How can I pass more than one parameter to my directive?

Use multiple attributes. Your directive has access to all attributes used in the element:
<div my-directive arg-one='abcd', arg-two>xx</div>
app.directive('myDirective', function () {
return {
link: function ($scope, elm, attr, ngModel) {
var abc=attr.argOne;
var def=attr.argTwo;
}
};
});
Notice the change from - to camelCase. This is done by AngularJS.

You can use an array like so
<div test-dir='["abcd","zzz"]'>xx</div>
Then in your directive you can do
var abc = JSON.parse(attr.testDir); // = Array ["abcd","zzz"]

Related

How to add a button to an input field using AngularJS directive?

I need to write a directive that can would add a button to an input field and some other functionality.
So, ideally I want to have something like this
<input type="text" my-directive>
to end up being
<input type="text"><button ng-click="someAction()" ng-class="{'success': isSuccess()}" ng-disabled="isDisabled()">click me</button>
Simplified code for my directive:
app.directive('myDirective', ['$q', '$timeout', function ($q, $timeout) {
return {
restrict: 'A',
template: '<button ng-click="someAction()" ng-class="{\'success\': isSuccess()}" ng-disabled="isDisabled()">click me</button>',
require: '?ngModel',
link: function (scope, element, attrs, ctrl) {
scope.isSuccess = function () {
...
};
scope.isPending = function () {
...
};
scope.someAction = function () {
...
};
....}]);
The problem is that if I add this button in directive's template I end up with <input><button></button></input>
Creating a directive that would include also an input field is unfortunately not an option for me.
Please let me know if I need to provide more info.
After some searching, I have found a solution.
You can add a the element by compiling it in your directive's link function and appending it to the element.
link: function (scope, element, attrs, ctrl) {
var tpl = <button ng-click="someAction()" ng-class="{\'success\': isSuccess()}" ng-disabled="isDisabled()">click me</button>';
var el = $compile(tpl)(scope);
element.after(el);
...}

Custom directive Attribute

In my custom directive im getting attributes values, in my case it could be numbers or arrays, but in my directtive im getting a string array (ex: "[1,2]".
How can i get my array in the attribute not being a string?
view:
<div my-directive to=[1,2]
directive:
angular.module('myApp')
.directive('myDirective',
[
'$http', '$q','$uibModal',
dir
]);
function dir($http, $q, UserService, $uibModal) {
return {
restrict: 'A',
link: function($scope, element, attrs, controller) {
element.on( 'click', function( evt ){
console.log(attrs.to);
});
}
};
}
Try following approach:
On view (init the directive & set the directive param)
<div ng-app='demo'>
<demo-directive to-val='[1,2,3,4,5]'></demo-directive>
</div>
On the directive
var demo = angular.module('demo', []);
demo.directive('demoDirective', function($parse) {
return {
restrict: 'E',
template: '<div ng-repeat="val in toVal">{{val}}</div>',
link: function (scope, element, attrs, controller) {
// parse the attribute array to a scope params
scope.toVal = JSON.parse(attrs.toVal);
}
}
});

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

Is there a way to inject a directive's controller into its link function if that directive uses 'require'?

If a directive doesn't have the require property in the directive definition object set, then the 4th argument passed to link is that directive's controller.
If we do set require, then the 4th property is the controller (or array of controllers) that we require, and the directive loses the reference to its own controller. What is the best way to access this?
angular.module 'example', []
.directive 'directive1', ->
restrict: 'E'
controller: ($log)->
#sayHi = -> $log.info('hi')
link: (scope, element, attributes, controller)->
controller.sayHi() # works.
.directive 'directive2', ->
restrict: 'A'
require: 'directive1'
controller: ($log)->
#sayBye = -> $log.info('bye')
link: (scope, element, attributes, controller)->
controller.sayHi() # works
# how would I access sayBye?
I realise I could put sayBye on the $scope instead, and access through scope in the link function, but is there any way of doing this that doesn't involve the scope?
Is this the only way?
.directive 'directive2', ->
ownCtrl = {}
restrict: 'A'
require: 'directive1'
controller: ($log)->
#sayBye = -> $log.info('bye')
ownCtrl = this
link: (scope, element, attributes, controller)->
controller.sayHi() # works
ownCtrl.sayBye()
You can require an array of controllers including the directive's controller itself. As mentioned in the comments provided in the $compile documentation.
angular.module('demo', [])
.directive('directive1', function() {
return {
controller: function() {
this.sayHello = function(directiveName) {
console.log(directiveName + ' says hi');
};
},
link: function(scope, elem, attr, ctrl) {
ctrl.sayHello('directive1');
}
};
})
.directive('directive2', function() {
return {
require: ['^directive1', 'directive2'],
controller: function() {
this.sayGoodbye = function() {
console.log('goodbye');
};
},
link: function(scope, elem, attr, ctrls) {
var d1Ctrl = ctrls[0],
d2Ctrl = ctrls[1];
d1Ctrl.sayHello('directive2');
d2Ctrl.sayGoodbye();
}
};
});
<div ng-app="demo">
<div directive1>
<div directive2></div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

Angular.js trigger another directive

I'm trying to create a sort of generic toggle feature between directives, where one directive which contains a template does not render, until an event occurs from another directive. Any suggestions on how to link this together?
Thanks!
There are many ways to achieve this.
A
Using events (but be careful, when used exessively, especially for interaction between directives, you can get lost easily! This is why I didnt create a http://plnkr.co for it, even worse: code A is untested!
so pls edit this in case of errors
Use $rootScope.$on('myEvent', function(e, eargs) {...}) on the
master directive.
dispatch the event from some directive:
$rootScope.$broadcast('myEvent', {foo: 'bar'}).
remember to inject $rootScope in both directives.
angular.module('masterDirective', [])
.directive('masterDirective', function ($rootScope, $compile /**injects here*/) {
var templ = '<p ng-bind="someVar"></p>';
return {
restrict: 'EA',
scope: {},
link: function (scope, element, attrs) {
scope.someVar = "I am a template and I was born and visible to the world, because slaveDirective send me an event to do so.";
$rootScope.$on('myEvent', function(e, eArgs) {
// eArgs.myVar will be 'Jackson';
element.append($compile(templ)(scope));
});
}
}
});
angular.module('slaveDirective', [])
.directive('slaveDirective', function ($rootScope) {
return {
restrict: 'EA',
scope: {},
link: function (scope, element, attrs) {
$rootScope.$broadcast('myEvent', {myArg: 'Jackson'});
}
}
});
B
Using a "shared controller" is the cleaner, but more complicated way. This approach is more strongly typed, you express the workflow and once it works, it is not as easy to break.
Demo: http://plnkr.co/WaqKzP
Use a controller on your master directive: controller(scope,element,attrs) {...}
require your masterDirective in slave directive: require: 'myMasterDirective'
the controller of the master directive is the fourth parameter of your slave's link function (because you required it), you can call a function to let the master include the template.
<body ng-app="myApp">
<button ng-click="includeSlave=true">include slave directive</button>
<master-directive>
<div ng-if="includeSlave==true">
<slave-directive></slave-directive>
</div>
</master-directive>
</body>
angular.module('myApp', [])
.directive('masterDirective', function ($rootScope, $compile /**injects here*/) {
var templ = '<p ng-bind="someVar"></p>';
return {
restrict: 'E',
controller: function ($scope, $element) {
return {
slaveLink: function() {
$element.append($compile(templ)($scope));
}
}
},
link: function (scope, element, attrs) {
scope.someVar = "I am a template and I was born and visible to the world, because slaveDirective called a function on myself to do so.";
}
};
})
.directive('slaveDirective', function () {
return {
require: '^masterDirective',
restrict: 'E',
link: function (scope, element, attrs, myMasterController) {
myMasterController.slaveLink();
}
};
});

Resources