I'm trying to learn AngularJS a bit and I want to do the following:
Create a table which has sortable columns and have an arrow image that describes the sorting direction.
What I tried to do is creating a directive called sort which takes the sorting column and changes the sortByColumn variable. This works fine. The only problem is when I try to change the class of the image.
At first I tried using the template to use like this:
link: function(scope, element, attrs)
{
...
},
template: '<span class="image" ng-class="{is-visible: someVar == attrs.sort}"></span>'
This doesn't work because the attrs variable is not defined so the first question would be... is there a way to access the element's attributes inside the template?
If not, what I tried to do next is manually set the html inside the link:
link: function(scope, element, attrs)
{
element.html('' + element.html() + '<span class="sort-image" ng-class="{visible: true}"></span>');
}
This doesn't work because it doesn't evaluate the ng-class.
How can I achieve such a thing?
myApp.directive('sort', function () {
return {
scope: {
sort: "#"
},
link: function (scope, element, attrs) {
...
},
template: '<span style="display:inline-block;" ng-transclude></span><span class="sort-image" ng-class="{visible: sort == (sortByColumn)}"></span>',
transclude: true
};
});
I figured out the answer. I can just use the isolate scope "#" to pass the attribute value.
Related
I want to have a tag like <h1> that I can pass the level as an attribute (for nested templates to pass the depth).
This could look like:
.directive('hx', function() {
return {
restrict: 'E', transclude: true, replace: true,
link: function(scope, element, attrs) {
this.template = '<h' + attrs.level + ' ng-transclude></h' + scope.level + '>'
}
}
})
This approach does not work as expected as you can see at http://plnkr.co/edit/tt1oJySS4j0FmamEYBEr?p=preview
You can set a template on a directive. Each time the link function runs, you are changing the template. The first <hx> element in your code has no template, so nothing appears. The second will use the template from the first (h1) and the third will use the template from the second (h1 again).
Instead, you want to use the transclude function for directives:
link: function(scope, element, attrs, ctrl, transclude) {
transclude(scope, function (clone) {
const header = angular.element('<h' + attrs.level + '></h' + attrs.level + '>');
header.append(clone);
element.append(header);
// element.replaceWith(header); // variant replace=true
});
}
This gives you access to the transcluded content in clone. Then, we create the new header element with the appropriate level, append the content (in clone) to that, and then append that header element to the hx.
http://plnkr.co/edit/ED7NU8NmZ1g3G8efQNlu?p=preview
I have a directive that is restricted to attribute. I want to do either of 2 things. If a certain condition is met, I want to use the children for the element with the directive attribute as the model (thats the content) or if that condition is not met then I want to data bind from a service instead, so the directive would replace the children with something i was given. I have a directive that is doing the latter but Im finding it very hard to have it grab the children before the compiler comes in and replaces it with its template... Anyone know how this is done if its possible?
I think what you're looking for is element.context in your directive's link (or compile) function.
Inside your link function (pre or post), the original element that your directive was found on is stored in the passed-in element's context property. So if your service call returns no data, you can just replace the compiled element with the original element by doing element.replaceWith(element.context).
So your directive would look something like this:
.directive('ttContent', ['mySvc', function (mySvc) {
return {
restrict: 'A',
replace: true,
transclude: false,
template: '<div class="content new-content" ng-bind-html="htmlContent | sanitize"></div>',
scope: {
testDataReturned: '#'
},
link: {
pre: function (scope, element, attrs) {
},
post: function (scope, element, attrs){
mySvc.fetchContent().then(function success(data){
if (data) {
scope.htmlContent = data;
} else {
// element.context is our original, pre-compiled element
element.replaceWith(element.context);
}
}, function fail(data){
element.replaceWith(element.context);
});
}
}
};
}]);
Here's a plunk.
i have html markup that i need to use at many places so rather to copy paste it again and again but with different Headings
i thought to create a directive it looks like this
myModule.directive('row', function ($compile) {
return {
restrict: 'E',
template: '<div class="row">'+
'<div class="col-md-10 margin-top-10px font-18px">{{heading}}</div>'+
' <div class="col-md-2">'+
'<div class="margin-top-10px"><span ng-click="close()" class="close helvetica color-black">×</span></div>'+
'</div>'+
'</div>',
replace: true,
link: function (scope, element, attrs, ctrl) {
var test = attrs.heading;
}
};
});
and how i m trying to use it
<row heading="its my heading "></row>
i cant make it work .i dont want to create isolated scope or child scope i just wanted to pass value from attribute and when template gets replaced i want its interpolated value .
can some one help me how can id o this
Try with:
link: function (scope, element, attrs, ctrl) {
scope.heading = attrs.heading;
}
It might also be a good idea to set
scope: true
You have to actually attach the value from the attr object to the scope before it can be used in the template.
add
scope: {
heading: '='
}
to your directive's definition to create 2 way data-binding with that attribute. you would do this if you are passing in a scope variable.
or use #
scope: {
heading: '#'
}
if you are just passing in a string.
minimalist plunkr to demonstrate: http://plnkr.co/edit/U5OzlMiXaIsHEdb5gmRT
I have the following directive
courseApp.directive("courseoverview", function() {
function link(scope, element, attrs) {
scope.switched = false;
//hover handler
scope.hoverItem = function(hovered){
if (hovered) {
//element.addClass('hover');
$('#course-'+ scope.$index +' figure').addClass('tint');
//console.log(scope.$index);
}
else
//element.removeClass('hover');
$('#course-'+ scope.$index +' figure').removeClass('tint');
};
}
return {
restrict : 'A',
replace: true,
transclude: true,
templateUrl: "views/course-overview.html",
link: link
}});
The directive is called with the following code
<li data-ng-repeat="item in social" class="social-{{item.name}}"
ng-mouseover="hoverItem(true);"
ng-mouseout="hoverItem(false);"
current-social="{{item.name}}">
The hover function works great but i need access to this attribute in the directive, well i need the value of it.
Any ideas on how to achieve this would be great.
First, your directive is named 'courseoverview' but I don't see that attribute anywhere in the markup you provided.
To address your question, you say I need access to this attribute in the directive. But I think you have the attributes in the link function - see the attrs parameter. Those are the attributes on the element that fired your directive.
function link(scope, element, attrs) { ... }
See this answer for more.
The idea is to replace the directive element with the dynamic template which refers to interpolated strings.
If I use element.html() in my directive then the strings are interpolated fine but this leaves the original custom directive html element.
If I use element.replaceWith() then strings are not interpolated. I guess it has related to scope but can't figure out what's wrong.
Plunker: http://plnkr.co/edit/HyBP9d?p=preview
UPDATE
Found the solution. Using element.replaceWith($compile(html)(scope)); works.
Updated plunker: http://plnkr.co/edit/HyBP9d?p=preview
Not sure what objective is regarding replaceWIth. The problem is likely that when you replace an elememnt, you replace all events and data bound to it. This would include the angular scope for the element.
For demo provided could do it like this:
app.directive('status', function($compile) {
var linker = function(scope, element, attrs) {
element.contents().wrap('<h'+attrs.value+'>')
};
return {
restrict: 'E',
replace: true,
template:'<div>{{value}}</div>',
transclude: true,
link: linker,
scope: {
value: '='
}
};
});
DEMO:http://plnkr.co/edit/QDxIwE?p=preview