how to pass directive attribute to directive template? - angularjs

I'm have this code:
<div ng-repeat="param in item.parameters">
<rating param_item="{{param}}"></rating>
</div>
how can i pass the given attribute to the directive template?

Personally, I like creating child scope for element directives:
http://plnkr.co/edit/Dz7fcZaT4KdRDa869rwm?p=preview
app.directive('rating', function() {
return {
restrict: 'E',
scope: {
paramItem: '#'
},
link: function(scope, element, attr){
console.log(scope.paramItem);
}
}
});
There are a couple other ways too, depending on how you expect your directive to work.

Related

angularJS attribute based directive - pass isolated scope

I have written directives before that have an isolated scope but they are element directives
If i write a directive that is attribute based, how can i pass the isolated scope to it?
Thanks
You can do this in exactly the same way as you did with element directive, i.e. attach further attributes that pass in the properties to the isolated scope.
HTML:
<div greeting-directive my-greeting="'hello'" greet-who="'world'"></div>
JS:
angular.module("myApp").directive("greetingDirective", function(){
return {
restrict: 'A',
scope: {
myGreeting: "=",
greetWho : "="
},
template: '<p>{{ myGreeting }}, {{ greetWho }}</p>'
}
});
Sample Plunk
It is very similar to isolate scope for Element directives. Consider this example:
myApp.directive('test', function () {
return {
restrict: 'A',
scope: {test: '='},
link: function (scope, element) {
scope.$watch('test', function () {
console.log(scope.test)
});
}
};
});
I'm using the direcitve name as a scope variable.
<div test="ctrl.val">some stuff</div>
However if I didn't want to leverage the directive name like this:
myApp.directive('notTest', function () {
return {
restrict: 'A',
scope: {test: '='},
link: function (scope, element) {
scope.$watch('test', function () {
console.log(scope.test)
});
}
};
});
Then html might look like this:
<div not-test test="ctrl.val">some stuff</div>

AngularJs optional transclude

I've written a directive without the transclude option.
But now it would be nice when I could activate the transclude function/option when calling the directive with another attribute or something else if possible.
If that's not possible the only Way I see is, to copy the directive and add the Transclude in the second one, but then I've doubled my code whtat I'm not willing to do.
any Ideas how to optionally activate the transclude in Angular 1.2.x
Edit:
alternate problem is also that I need to set the ng-transclude in my directive Template because its a big one and only a few rows can be replaced by the transclusion content.
You could conditionally modify a template to include ng-transclude in the compile: function.
.directive('foo', function () {
return {
restrict: 'E',
transclude: true,
replace: true,
templateUrl: 'foo.html',
compile: function (element, attrs) {
if (attrs.bar !== undefined) {
element.find('.may-transclude-here')
.attr('ng-transclude', '');
}
return function postLink(scope, element, attrs, controllers) {
scope.listEntries = ['apple', 'banana', 'tomato'];
};
}
}
})
and a html template:
<div class="foo">
<h4>Directive title</h4>
<div class="may-transclude-here" ng-repeat="item in listEntries">
Original content: {{item}}
</div>
<span>blah blah blah</span>
</div>
but contents that are transcluded via ng-transclude will not bind with a scope of each item created by ng-repeat. In case you also need the binding, here is the modified version of ng-transclude that do the correct scope binding.
.directive('myTransclude', function () {
return {
restrict: 'EAC',
link: function(scope, element, attrs, controllers, transcludeFn) {
transcludeFn(scope, function(nodes) {
element.empty();
element.append(nodes);
});
}
};
});
Plunker example: http://plnkr.co/edit/8lncowJ7jdbN0DEowdxP?p=preview
hope this helps.

Single angular js directive and many controllers

I want to know how to invoke a function residing in controller on the ng-click event of template element. I have to use this directive in many pages.Hence I need to handle the click event in respective controller pages.The below code invokes the click function (moreitemdetails) residing within the directive.I tried setting the scope as moreitemdetails: '=' . It is also not working.
I have been using the directive
app.directive('groceryList', function){
return {
restrict: 'E',
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
//other code
}
}
};
});
The call for directive is
<grocery-list array="items"></grocery-list>
This is the template file
<div ng-click="listItemClick($event)">
<div>
<div class="item">
<span class="item-details">
{{array[0].Item}}
</span>
<span class="down-arrow"></span>
</div>
<div class="next-items">
<ul>
<li class="item" ng-repeat="list in array">
<div class="item-details" ng-click="moreitemdetails(list.Name,list.Type)">{{list.Item}}</div>
</li>
</ul>
</div>
Is there a way to get around?
I also would like to know the use of $location within another directive. Quoting the previous example (everythin is same except the directive definition and action in moreitemdetails() )
app.ui.directive('groceryList', ['$location', function(location){
return {
restrict: 'E',
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
$location.path('/home/');
}
}
};
}]);
Thanks in advance
So by declaring
scope: {
array: '=',
listItemClick:'&',
moreitemdetails: '&',
},
you are creating an isolated scope for your directive. One solution might be to not declare this scope in your directive. This would mean that your ng-click="moreitemdetails(list.Name,list.Type) would trigger the function on your controllers scope.
Alternatively you could use an emit and listener. To do this, in your directive you could call:
scope.moreitemdetails=function(name,type){
var deets = {
name: name,
type: type
};
scope.emit('moreitemdetails',deets)
}
Then in your various controllers you implement:
scope.$on('moreitemdetails',function(event,details){
// do some code with your name and type
}
I'm not sure exactly what you would like to know about $location, if you could be a bit more specific I might be able to help a more.
Hope this helps in some way!
EDIT:
The directive without any scope declared would look like this:
return {
restrict: 'E',
templateUrl: 'list.html',
link: function(scope, element, attrs) {
scope.label = attrs.label;
scope.listItemClick=function(e){
$(e.currentTarget).find('.next-items').slideToggle('fast');
}
scope.moreitemdetails=function(name,type){
//other code
}
}
};

How to replace the element with ng-transclude

Is it possible to replace the element with ng-transclude on it rather than the entire template element?
HTML:
<div my-transcluded-directive>
<div>{{someData}}</div>
</div>
Directive:
return {
restrict:'A',
templateUrl:'templates/my-transcluded-directive.html',
transclude:true,
link:function(scope,element,attrs)
{
}
};
my-transcluded-directive.html:
<div>
<div ng-transclude></div>
<div>I will not be touched.</div>
</div>
What I am looking for is a way to have <div>{{someData}}</div> replace <div ng-transclude></div>. What currently happens is the transcluded HTML is placed inside the ng-transclude div element.
Is that possible?
I think the best solution would probably be to create your own transclude-replace directive that would handle this. But for a quick and dirty solution to your example you could essentially manually place the result of the transclusion where you want:
my-transcluded-directive.html:
<div>
<span>I WILL BE REPLACED</span>
<div>I will not be touched.</div>
</div>
Directive:
return {
restrict:'A',
templateUrl:'templates/my-transcluded-directive.html',
transclude:true,
link:function(scope,element,attrs,ctrl, transclude)
{
element.find('span').replaceWith(transclude());
}
};
It's easy to create a ng-transclude-replace directive, here is a copycat of the original ng-transclude.
directive('ngTranscludeReplace', ['$log', function ($log) {
return {
terminal: true,
restrict: 'EA',
link: function ($scope, $element, $attr, ctrl, transclude) {
if (!transclude) {
$log.error('orphan',
'Illegal use of ngTranscludeReplace directive in the template! ' +
'No parent directive that requires a transclusion found. ');
return;
}
transclude(function (clone) {
if (clone.length) {
$element.replaceWith(clone);
}
else {
$element.remove();
}
});
}
};
}]);
PS:you can also check this link to see the difference between the ng-transclude
this works in Angular 1.4.9 (and prob earlier too)
return {
restrict: 'E',
replace: true,
template: '<span data-ng-transclude></span>',
transclude: true,
link: function (scope, el, attrs) .........
}
If you don't have to support IE and Edge you can use display:contents in your css. That will destroy the wrapper on the level of css.
You can read more about this new display propertie here:
https://css-tricks.com/get-ready-for-display-contents/
Current browser support (hope to see Edge support in the future):
https://caniuse.com/#feat=css-display-contents

Access element directive children

I'm implementing a navigation directive that should have a elements for each nav item:
<navigation title="My Web Page">
Home
About
</navigation>
How Can I get access to those anchors? Accessing the element's children in link() only returns the template's children, not the 'a''s that I'm looking for.
.directive('navigation', function () {
return {
template: template,
restrict: 'E',
replace: 'true',
scope: {
title: '#'
},
link: function postLink(scope, element, attrs) {
// This only looks in the directive's template
console.log($(element).find('a'));
}
};
});
What am I missing? I'm looking forward to attatch an array of the anchors in the directive's scope and iterate trough them within the template.
In order to move the original content within the new template you need to use the transclude property. When translude is set to true the directive will delete the original content but also make it available for reinsertion within your template through the ng-translude directive. See example below.
Without transluding the orginal data the anchor tags are removed and that is why your link function cannot find them.
.directive('navigation', function () {
return {
template: '<div>Tansclude data here: <span ng-translude></span></div>',
restrict: 'E',
replace: true,
transclude: true,
scope: {
title: '#'
},
link: function postLink(scope, element, attrs) {
console.log($(element).find('a'));
}
};
});
I am a bit confused where your nav-tems are coming from, but I'll give it a go
I'm assuming your nav elements are defined in a controller that is parent to the directive
function myCtrl ($scope){
$scope.navArray=[{title: 'Link1', href: 'www.example.com'}, {...}];
}
you would them have to declare the array as an attribute in your directive
<navigation nav="navArray"></navigation>
and two-way-bind it to the scope of your directive
.directive('navigation', function () {
return {
template: '<div><a ng-repeat="link in nav" href="link.href">{{link.title}}</a></div>',
restrict: 'E',
replace: 'true',
scope: {
nav: '='
},
link: function postLink(scope, element, attrs) {
}
};
});
Remember, you want to stay away from DOM manipulation in your link function. Instead, I recommend using ng-repeat in your template, and make sure the array of nav items is passed to your directive's scope.
Edit: See Fiddle http://jsfiddle.net/nicolasmoise/8YQPh/3/

Resources