Using a custom function as an expression - angularjs

i wrote a function called getDepth(object) that gives me the depth of an object and then returns a string, for example if the depth of the object was 3 it will return the string "sub-sub-sub", and in my directive template i want to call that function from ng-class such as
'<span ng-class="getDepth(item)">{{item.content}}</span>'
but i am not sure where to put that function in my directive, should it just be inside the link function?

This is typically the job for a controller. So you can create an anonymous controller in your directive and place it there, or in the scope of the parent controller looking after this section of code - that is assuming of course one exists.
Maybe though you would like to reuse this functionality later in the app, so I recommend placing it high on the controller tree to allow others to inherit its function.
The linker functions job is strictly DOM manipulation, and this is not DOM manipulation this is a function returning a string and the ng-class directive in turn does the DOM manipulation.
If you check the docs:
Directives that want to modify the DOM typically use the link option. link takes a function with the following signature, function link(scope, element, attrs) { ... } where:
.directive('myCurrentTime', function($interval, dateFilter) {
function link(scope, element, attrs) {
var format,
timeoutId;
function updateTime() {
element.text(dateFilter(new Date(), format));
}...
As you can see the link function is changing the DOM.
So to answer the question, this is how the directive should be structured.
app.directive('myDirective', function() {
var controller = function($scope) {
$scope.getDepth = function (item) {
return "text-success";
};
}
return {
template: '<span ng-class="getDepth(item)">{{item.content}}</span>',
scope: {item: '='},
controller: controller // or this can be the name of an outside reference controller as well - which i prefer for unit testing and reusability purposes.
}
};
}
]);

if you want to let this control to your directive instead controller you can put it in the link function, an example directive should be like this...
app.directive('myDirective', ['$compile',
function($compile) {
return {
template: '<span ng-class="getDepth(item)">{{item.content}}</span>',
scope: {item: '=item'},
link: function(scope, element, attrs) {
scope.getDepth = function (item) {
return "text-success";
};
}
};
}
]);
here is PLUNKER

Related

Angularjs: How to use function in a custom directive that is declared from the controller [duplicate]

We're running into a problem trying to call a function passed into a directive using the ampersand '&' in our directive's link function.
It seems the function is called on the controller but no arguments are passed in the call. All the examples we have seen involve passing through by creating a call in template. Is there a way to call a function on your directive from its template, then do something in the directive that calls the controller function passed into it?
Are you passing the arguments inside {}s? E.g., inside the directive's link function, you'll want to call the method like so: scope.someCtrlFn({arg1: someValue});
<div my-directive callback-fn="ctrlFn(arg1)"></div>
app.directive('myDirective', function() {
return {
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
}
});
function MyCtrl($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
}
Fiddle.
In addition to Mark's answer I'd like to point out that you can spare some letters using the & shorthand. This will assume that the callback-fn referenced in your HTML exists as scope.callbackFn in your scope. Everthing else is still the same, so there are only 2 lines to change. I kept Mark's version as comments, so you should be able to spot the difference easily.
<div my-directive callback-fn="ctrlFn(arg1)"></div>
app.directive('myDirective', function() {
return {
scope: { callbackFn: '&' }, //scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.callbackFn({arg1: 22}); //scope.someCtrlFn({arg1: 22});
},
}
});
function MyCtrl($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
}

Directive view does not get updated while updating scope variable in post link

I am not able to get my view updated while updating scope variable in post link function.
Following is the use of my directive.
<my-directive color='purple'>
</my-directive>
Following is the definition of my directive.
app.directive('myDirective', function () {
console.log('My Directive Called');
return {
restrict: 'E',
scope: {
localVar: '#color'
},
//template: '<span></span>', // When I enable this template it works fine.
/* This link way it is working fine.
link: function (scope, iElement, iAttrs) {
console.log(iElement);
iAttrs.color = 'red';
}*/
//This is not working Reason don't know.
compile: function (tElement, tAttrs) {
var spanElem = angular.element('<span> {{ localVar }} </span>');
spanElem.attr('color', tAttrs.color);
tElement.replaceWith(spanElem);
return function (scope, iElement, iAttrs) {
iAttrs.color = 'red';
};
}
};
});
I want to know the reason why this code is not working. It will work if I specify the template property in directive definition object. But I want to know what is going wrong in above code.
Please help me.
It's much easy if you do somehting like this:
JSFiddle
angular.module('myApp', [])
.directive('myDirective', function () {
return {
restrict: 'E',
scope: {
localVar: '#color'
},
template: '<span> {{ localVar }} </span>'
};
});
Without calling link function there is no two way data binding between template created by compile function and scope.
That's why when you turn on link function you get the desired result.
From angular docs.Please read this point.
HTML compilation happens in three phases:
$compile traverses the DOM and matches directives.
If the compiler finds that an element matches a directive, then the directive is added to the list of directives that match the DOM element. A single element may match multiple directives.
Once all directives matching a DOM element have been identified, the compiler sorts the directives by their priority.
Each directive's compile functions are executed. Each compile function has a chance to modify the DOM. Each compile function returns a link function. These functions are composed into a "combined" link function, which invokes each directive's returned link function.
$compile links the template with the scope by calling the combined linking function from the previous step. This in turn will call the linking function of the individual directives, registering listeners on the elements and setting up $watchs with the scope as each directive is configured to do.
The result of this is a live binding between the scope and the DOM. So at this point, a change in a model on the compiled scope will be reflected in the DOM.
EDIT CODE :
If you want to do it withour compile and link function,try to use isolated scope
EDIT CODE 2:
.directive('myDirective', function () {
console.log('My Directive Called');
return {
restrict: 'E',
scope: {
localVar: '#color'
},
template : '<span> {{ localVar }} </span>'
};
});
HTML :
<my-directive color='purple'>
</my-directive>
EDIT CODE 3:
directive('myDirective', function () {
console.log('My Directive Called');
return {
restrict: 'EA',
template: '<span>{{ localVar }}</span>', // When I enable this template it works fine.
compile: function (tElement, tAttrs) {
return {
post: function postLink(scope, iElement, iAttrs, controller) {
scope.localVar = 'red';
}
}
}
};
})

Angular: calling controller function inside a directive link function using &

We're running into a problem trying to call a function passed into a directive using the ampersand '&' in our directive's link function.
It seems the function is called on the controller but no arguments are passed in the call. All the examples we have seen involve passing through by creating a call in template. Is there a way to call a function on your directive from its template, then do something in the directive that calls the controller function passed into it?
Are you passing the arguments inside {}s? E.g., inside the directive's link function, you'll want to call the method like so: scope.someCtrlFn({arg1: someValue});
<div my-directive callback-fn="ctrlFn(arg1)"></div>
app.directive('myDirective', function() {
return {
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
}
});
function MyCtrl($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
}
Fiddle.
In addition to Mark's answer I'd like to point out that you can spare some letters using the & shorthand. This will assume that the callback-fn referenced in your HTML exists as scope.callbackFn in your scope. Everthing else is still the same, so there are only 2 lines to change. I kept Mark's version as comments, so you should be able to spot the difference easily.
<div my-directive callback-fn="ctrlFn(arg1)"></div>
app.directive('myDirective', function() {
return {
scope: { callbackFn: '&' }, //scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.callbackFn({arg1: 22}); //scope.someCtrlFn({arg1: 22});
},
}
});
function MyCtrl($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
}

How do I pass an array into a directive without it turning into a string?

Righty, so I'm just getting into directives and they seem pretty awesome. I ran into a problem though:
I need to pass an array of images into a directive so I can filter them by certain criteria. Here's my html invoking the directive:
<img cover="{{challenge.images}}">
This is my directive:
myproject.directive('cover', function() {
return {
link: function ($scope, element, attrs) {
console.debug("attrs.cover", Array(attrs.cover));
}
};
});
The output is a String. is there a way to prevent attr turning into a String?
I'm assuming here that you don't want to create isolated scope, so:
myproject.directive('cover', function($parse) {
return {
link: function ($scope, element, attrs) {
var covers = $parse(attrs.cover)($scope);
console.debug("attrs.cover", covers);
}
};
});
and then use the directive like so:
<img cover="challenge.images">

Angular reusable functions

I have several directives that need to call the same function after doing their thing. This function needs to access the main controller scope, but also modify the DOM. How and where should this function be declared?
You should use a service, services has access to $rootScope, although is it better to keep DOM modification at directive level, in certain cases you can go for it.
angular.module("myModule", [])
.factory("MyService", function ($rootScope) {
return {
myFunction: function () { // do stuff here }
}
})
.directive("MyDirective", function (MyService) {
return {
link: function (scope, iElement, iAttrs) {
// try do keep dom modification here, you have access to MyService,
// let it do the algorithm and try to keep dom modification here.
// PS: you can also inject $rootScope to directives.
};
};
});
If this function needs to access Controller's scope, I would use the scope which is accessible in the directive and pass it as param, like so:
var MyDirective = function() {
var linkFn = function(scope, element, attrs) {
callAwesomeFn(scope);
};
return {
link: linkFn
}
};
Now, where to put it?
if this is utility function, I would put in some utilities namespace, like MyFunctions.callAwesomeFn()
if this is a component which needs to interact with other objects managed by Angular - use Dependency Injection and inject it into directive

Resources