i read Angularjs documentation .thereare examples for defining directives without passing a value.for example:
angular.module('docsTemplateUrlDirective', [])
.controller('Controller', ['$scope', function($scope) {
$scope.customer = {
name: 'Naomi',
address: '1600 Amphitheatre'
};
}])
.directive('myCustomer', function() {
return {
templateUrl: 'my-customer.html'
};
});
and HTML is
<div ng-controller="Controller">
<div my-customer></div>
</div>
but i want to create a directive like ng-model in which an attribute get passed.for example
<div ng-controller="Controller">
<div my-customer="Hello"></div>
</div>
i want to retrieve this hello in my directive definition link function.How to achieve that ??
You can pass as many as attributes and can access them directly using third argument in link function. Here you go:
.directive('myCustomer', function() {
return {
templateUrl: 'my-customer.html',
link: function(scope, element, attr) {
console.log('Attribute:', attr.myCustomer, attr.otherData);
}
};
});
<div my-customer="hello" other-data="foo"></div>
Just scroll a lil more in docs (Angular Directives) where you got above code, you will get how to get your answer attr of directive
.directive('myCustomer', function() {
return {
templateUrl: function(elem, attr){
...
//attr will be having the value
return attr;
}
};
If you want to use isolated scope, then you can do this:
.directive('myCustomer', function() {
return {
scope: {
myCustomer : '=' //If expected value is an object use '='. If it is just text, use '#'
}
templateUrl: 'my-customer.html',
link: function(scope, ele, attr){
console.log(scope.myCustomer);
}
};
});
If you don't want to use isolated scope, then
.directive('myCustomer', function($parse) {
return {
templateUrl: 'my-customer.html',
scope: true,
link: function(scope, ele, attr){
// if expected value is object
var hello = $parse(attr.myCustomer)(scope);
// if expected value is just text
var hello = attr.myCustomer;
}
};
});
Related
This is follow up to these 2 questions:
Pass argument between parent and child directives
Parent directive controller undefined when passing to child directive
I have this part working; however, when the value for ng-disabled for parent directive changes, the child directive values don't get updated.
Please see thin plunkr example.
HTML:
<div ng-app="myApp">
<div ng-controller="MyController">
{{menuStatus}}
<tmp-menu ng-disabled="menuStatus">
<tmp-menu-link></tmp-menu-link>
<tmp-menu-link></tmp-menu-link>
</tmp-menu>
<button ng-click="updateStatus()">Update</button>
</div>
</div>
JavaScript(AngularJS):
angular.module('myApp', [])
.controller('MyDirectiveController', MyDirectiveController)
.controller('MyController', function($scope){
$scope.menuStatus = false;
$scope.updateStatus = function(){
$scope.menuStatus = $scope.menuStatus?false:true;
}
})
.directive('tmpMenu', function() {
return {
restrict: 'AE',
replace:true,
transclude:true,
scope:{
disabled: '=?ngDisabled'
},
controller: 'MyDirectiveController',
template: '<div>myDirective Disabled: {{ disabled }}<ng-transclude></ng-transclude></div>',
link: function(scope, element, attrs) {
}
};
})
.directive('tmpMenuLink', function() {
return {
restrict: 'AE',
replace:true,
transclude:true,
scope:{
},
require:'^^tmpMenu',
template: '<div>childDirective disabled: {{ disabled }}</div>',
link: function(scope, element, attrs, MyDirectiveCtrl) {
console.log(MyDirectiveCtrl);
scope.disabled = MyDirectiveCtrl.isDisabled();
}
};
})
function MyDirectiveController($scope) {
this.isDisabled = function() {
return $scope.disabled;
};
}
How can I detect change in parent directive and pass it to child directive without adding angular watcher.
Solution 1
i've set up a working plnkr here: https://plnkr.co/edit/fsxMJPAc05imhBqefaRk?p=preview
the reason of this behaviour is that tmpMenuLink kept a copy of the value returned from MyDirectiveCtrl.isDisabled(). no watcher is set up , so the only way to resolve this is to manually watch for any changes and then update the field.
scope.$watch(function(){
return MyDirectiveCtrl.isDisabled();
}, function(){
scope.disabled = MyDirectiveCtrl.isDisabled();
})
Solution 2
An alternative without watchers is to pass the reference of an object instead of a primitive type, something like:
$scope.menuStatus = {status: false};
new plnkr here: https://plnkr.co/edit/RGEK6TUuE7gkPDS6ygZe?p=preview
Let us say I have this html:
<div ng-controller="MyCtrl">
<br>
<my-directive my-name="name">Hello, {{name}}!</my-directive>
</div>
with this simple controller:
myApp.controller('MyCtrl', function ($scope) {
$scope.name = 'Superhero';
});
And I have a directive in which I want to change the 'name' using require like this:
myApp.directive('myDirective', function($timeout) {
var controller = ['$scope', function ($scope) {
$scope.name = "Steve";
}];
return {
restrict: 'EA',
require: 'myName',
controller: controller,
link: function(scope, element, attrs, TheCtrl) {
TheCtrl.$render = function() {
$timeout(function() {
TheCtrl.$setViewValue('StackOverflow');
}, 2000);
};
}
};
});
But throws:
Error: No controller: myName
Here is the fiddle
But if I implement it using ng-model, works. Look here in this other fiddle
I have read that if you use 'require' in a directive, you need to have a controller for it.
So:
What I'm doing is wrong? It is not in this way? I need to do any other thing?
Well finally I got it.
Essencially what I'm trying to do is something called: 'Communication between directives using controllers'. I have found an article explaining this, and helped me a lot:
The view:
<div ng-controller="MyCtrl">
<br>
<my-directive my-name>Hello, {{name}}!</my-directive>
</div>
As you see above, there are two directives: my-directive and my-name. I will call inside my-directive a function from the controller of my-name directive using require.
myDirective:
myApp.directive('myDirective', function($timeout) {
return {
require: 'myName',
link: function(scope, element, attrs, myNameCtrl) {
$timeout(function() {
myNameCtrl.setName("Steve");
}, 9000);
} // End of link
}; // return
});
myName:
myApp.directive('myName', function($timeout) {
var controller = ['$scope', function ($scope) {
// As I tried, this function can be only accessed from 'link' inside this directive
$scope.setName = function(name) {
$scope.name = name;
console.log("Inside $scope.setName defined in the directive myName");
};
// As I tried, this function can accessed from inside/outside of this directive
this.setName = function(name) {
$scope.name = name;
console.log("Inside this.setName defined in the directive myName");
};
}];
return {
controller: controller,
link: function(scope, element, attrs, localCtrl) {
$timeout(function() {
localCtrl.setName("Charles");
}, 3000);
$timeout(function() {
scope.setName("David");
}, 6000);
} // End of link function
};
});
Interesting and works like a charm. Here is the fiddle if you want to try it out.
Also, you can get communication between directives using events. Read this answer here on SO.
I have a controller:
function myController($scope) {
$scope.clicked = false;
}
and a directive:
function myDirective() {
return {
restrict: 'E',
link: function(scope, elem, attrs) {
elem.bind('click', function() {
// need to update controller $scope.clicked value
});
},
template: '<div>click me</div>';
replace: true;
}
}
and I´m using it like this:
<div ng-controller="myController">
<my-directive></my-directive>
</div>
How can I change the controller value of $scope.clicked ?
thanks!
As you don't use isolated scope in your directive, you can use scope.$parent.clicked to access the parent scope property.
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$parent.clicked = ...
});
},
I would not recommend using scope.$parent to update or access the parent scope values, you can two way bind the controller variable that needs to be updated into your directive, so your directive becomes:
function myDirective() {
return {
restrict: 'E',
scope: {
clicked: '='
},
link: function(scope, elem, attrs) {
elem.bind('click', function() {
// need to update controller $scope.clicked value
$scope.clicked = !$scope.clicked;
});
},
template: '<div>click me</div>';
replace: true;
}
}
now pass this clicked from parent:
<div ng-controller="myController as parentVm">
<my-directive clicked="parentVm.clicked"></my-directive>
</div>
function myController() {
var parentVm = this;
parentVm.clicked = false;
}
I would recommend reading up on using controllerAs syntax for your controller as that would really solidify the concept of using two way binding here.
I like to use $scope.$emit for such purposes. It allows to send data from directive to the controller.
You should create custom listener in your controller:
$scope.$on('cliked-from-directive', function(event, data){
console.log(data)
})
As you can see, now you have full access to your controller scope and you can do whatever you want. And in your directive just to use scope.$emit
link: function(scope, elem, attrs) {
elem.bind('click', function() {
scope.$emit('cliked-from-directive', {a:10})
});
Here I've created jsfiddle for you
I have created custom angular directive. For an example:
Custom directive:
var app=angular.module('app',[]);
app.directive('customDirective',function(){
return{
restrict:A,
controller:customDirectiveController,
scope:{
someArray:"="
}
}
})
Custom directive controller:
app.controller('customDirectiveController',function(scope){
scope.someArray=[];
scope.someArray.push(1);
scope.someArray.push(2);
scope.someArray.push(3);
});
Parent controller:
app.controller('parentCtrl',function($scope){
$scope.result=[];
});
HTML:
<div data-ng-controller="parentCtrl">
<div data-custom-directive="result">
</div>
How can I get value of this someArray from custom directive into Parent controller( result variable should in Parent controller be same as someArray from custom directive controller)?
Here is jsfiddle http://jsfiddle.net/mehmedju/RmDuw/302/
Thanks
You can apply a '$watch' on the array like this:
In the controller:
app.controller('MainCtrl', function($scope) {
$scope.someArray = [];
})
In the HTML:
<div custom-directive arr="someArray">
</div>
In the directive:
app.directive('customDirective', function(){
return {
scope: {
arr: '='
}
}
link: function(scope, element, attrs) {
scope.$watch('arr', function(newVal, oldVal) {
//do your array manipulation here
}
}
})
Alternatively, if you just want to send data back, here's the method:
In the controller, create a function which will accept the value returned from the directive, example:
app.controller('MainCtrl', function(){
$scope.watchVal = function(val) {
//do array manipulation
$scope.apply(); //to update the scope
}
})
In the HTML:
<div custom-directive data-method="watchVal">
</div>
In the directive:
app.directive('customDirective', function(){
return {
scope: {
sendVal: '&method'
},
link: function(scope, element, attrs){
scope.updateVal = function(){
var func = scope.sendVal();
func(scope.someArray);
}
}
}
})
Let's just say you are using tempController
So your code should be
app.controller('tempController', function($scope) {
scope.someArray = []
});
Html Code for this is
<div ng-controller="tempController">
<div custom-directive some-array="someArray">
</div>
You have here a very interesting article about watchers
http://teropa.info/blog/2014/01/26/the-three-watch-depths-of-angularjs.html
You need use a watchCollection
And, If you are playing creating the array and change the reference inside the directive you can lost the reference inside the watcher. This mean don't create o change the array reference inside.
Another interesting way is use ngModel
http://jsfiddle.net/ta66J/
var app = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.formVals = {
dirVals: [
{val: 'one'},
{val: 'two'}
]
};
}
app.directive('dir', function($compile) {
return {
restrict: 'E',
compile: function(element, attrs) {
var html = "<input id='inputId' type='text' ng-model='" + attrs.dirModel + "' />";
element.replaceWith(html);
return function(scope, element, attrs, ngModel) {
$compile(angular.element(element))(scope);
};
},
};
});
The jsfidler is from this interesting thread:
https://groups.google.com/forum/#!topic/angular/QgcRBpjiHAQ
Here is the fixed issue.
<div custom-directive="" data-some-array="result"></div> {{result}}
[http://jsfiddle.net/mehmedju/RmDuw/304/][1]
I have the following code:
<div id='parent'>
<div id='child1'>
<my-select></my-select>
</div>
<div id='child2'>
<my-input></my-input>
</div>
</div>
I also have two directives which get some data from the data factory. I need the two directives to talk to each other such that when a value in select box is changed the input in changes accordingly.
Here's my two directives:
.directive("mySelect", function ($compile) {
return {
restrict: 'E',
scope:'=',
template: " <select id='mapselectdropdown'>\
<option value=map1>map1</option> \
<option value=map2>map2</option> \
</select>'",
link: function (scope, element, attrs) {
scope.selectValue = //dont konw how to get the value of the select
}
};
})
.directive("myInput", function($compile) {
return {
restrict: 'E',
controller: ['$scope', 'dataService', function ($scope, dataService) {
dataService.getLocalData().then(function (data) {
$scope.masterData = data.input;
});
}],
template: "<input id='someInput'></input>",
link: function (scope, element, attrs) {
//here I need to get the select value and assign it to the input
}
};
})
This would essentially do the onchange() function that you can add on selects. any ideas?
You could use $rootScope to broadcast a message that the other controller listens for:
// Broadcast with
$rootScope.$broadcast('inputChange', 'new value');
// Subscribe with
$rootScope.$on('inputChange', function(newValue) { /* do something */ });
Read Angular docs here
Maybe transclude the directives to get access to properties of outer scope where you define the shared variable ?
What does this transclude option do, exactly? transclude makes the contents of a directive with this option have access to the scope outside of the directive rather than inside.
-> https://docs.angularjs.org/guide/directive
After much research this is what worked...
I added the following:
.directive('onChange', function() {
return {
restrict: 'A',
scope:{'onChange':'=' },
link: function(scope, elm, attrs) {
scope.$watch('onChange', function(nVal) { elm.val(nVal); });
elm.bind('blur', function() {
var currentValue = elm.val();
if( scope.onChange !== currentValue ) {
scope.$apply(function() {
scope.onChange = currentValue;
});
}
});
}
};
})
Then on the element's link function I added:
link: function (scope, elm, attrs) {
scope.$watch('onChange', function (nVal) {
elm.val(nVal);
});
}
Last added the attribute that the values would get set to in the scope:
<select name="map-select2" on-change="mapId" >