How to call a function when its name is a string - angularjs

I have this:
angular.module("angular-table").directive("atPagination", [
function() {
return {
restrict: "E",
scope: false,
replace: true,
template: paginationTemplate,
link: function($scope, $element, $attributes) {
//The function name
let fn = '$scope.' + $attributes.getDataFn;
console.log($scope.$eval(fn),$attributes,$attributes.getDataFn, "$eval", fn);
}
}
}
I am calling the function using $scope.$eval in the console(last line). But the function is not getting called.
I am trying to change this library I am using for table and pagination. I am trying to pass a function from my controller into its directive so that it gets called when next is clicked in the pagination.
I can't pass the function the regular way because the scope is false and I don't want to change it. So I am passing it like this
<at-pagination at-config="tableConfig"
get-data-fn="getPaginatedData()" at-list="personnelsdata">
</at-pagination>
I am trying to call the function using $eval but it's not working. What am I doing wrong?

app.directive("myDirective", function() {
return {
restrict: "E",
scope: false,
template: paginationTemplate,
link: function(scope, element, attrs) {
//The function name
̶l̶e̶t̶ ̶f̶n̶ ̶=̶ ̶'̶$̶s̶c̶o̶p̶e̶.̶'̶ ̶+̶ ̶ ̶$̶a̶t̶t̶r̶i̶b̶u̶t̶e̶s̶.̶g̶e̶t̶D̶a̶t̶a̶F̶n̶;̶
let fn = attrs.getDataFn;
console.log(scope.$eval(fn));
}
})
The scope.$eval method evaluates an AngularJS function, not a JavaScript function.
For more information, see
AngularJS Developer Guide - AngularJS Expressions vs. JavaScript Expressions
AngularJS scope API Reference - $eval

Related

Call method in controller from directive

HTML :
<div id="idOfDiv" ng-show="ngShowName">
Hello
</div>
I would like to call the function which is declared in my controller from my directive.
How can I do this? I don't receive an error when I call the function but nothing appears.
This is my directive and controller :
var d3DemoApp = angular.module('d3DemoApp', []);
d3DemoApp.controller('mainController', function AppCtrl ($scope,$http, dataService,userService,meanService,multipartForm) {
$scope.testFunc = function(){
$scope.ngShowName = true;
}
});
d3DemoApp.directive('directiveName', [function($scope) {
return {
restrict: 'EA',
transclude: true,
scope: {
testFunc : '&'
},
link: function(scope) {
node.on("click", click);
function click(d) {
scope.$apply(function () {
scope.testFunc();
});
}
};
}]);
You shouldn't really be using controllers and directives. Angularjs is meant to be used as more of a component(directive) based structure and controllers are more page centric. However if you are going to be doing it this way, there are two ways you can go about it.
First Accessing $parent:
If your directive is inside the controllers scope you can access it using scope.$parent.mainController.testFunc();
Second (Preferred Way):
Create a service factory and store your function in there.
d3DemoApp.factory('clickFactory', [..., function(...) {
var service = {}
service.testFunc = function(...) {
//do something
}
return service;
}]);
d3DemoApp.directive('directiveName', ['clickFactory', function(clickFactory) {
return {
restrict: 'EA',
transclude: true,
link: function(scope, elem) {
elem.on("click", click);
function click(d) {
scope.$apply(function () {
clickFactory.testFunc();
});
}
};
}]);
Just a tip, any time you are using a directive you don't need to add $scope to the top of it. scope and scope.$parent is all you really need, you will always have the scope context. Also if you declare scope :{} in your directive you isolate the scope from the rest of the scope, which is fine but if your just starting out could make things quite a bit more difficult for you.
In your link function you are using node, which doesn't exist. Instead you must use element which is the second parameter to link.
link: function(scope, element) {
element.on("click", click);
function click(d) {
scope.$apply(function() {
scope.testFunc();
});
}

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 directive - scope undefined inside function

I can't seem to reach the link function scope variable from inside a function in my directive. The "elem" variable is defined, but the scope isn't. Why is that??
Here's my directive:
function contextMenu($document){
return {
scope: {
target: '=',
},
restrict: 'A',
link: function(scope, elem, attr) {
elem.bind('contextmenu',handleRightClick);
function handleRightClick(event) {
// do something with scope (scope is undefined)
}
}
}
};
How can I user the scope variable?
Thanks!
Uri
EDIT1:
I found I can use this to pass the scope to the function:
Passing parameters to click() & bind() event in jquery?, but this still doesn't explain why the scope variable is undefined.
EDIT2:
For completeness sake, this is how my directive is set up:
app.js
angular
.module('myModule', [])
.directive('contextMenu', ['$document', components.contextMenu])
and in the html:
<div context-menu target="testObject">
Make sure you are using the directive correctly. Since you didn't include the use of the directive in your template, I hope you used it something like this:
<div context-menu target="something"></div>
Then I am confused about the setup of your directive. Try this:
MyDirectiveModule.directive('contextMenu', function(){
return {
restrict: 'A',
scope: {
target: '#'
},
link: function(scope, element){
console.log(scope);
// don't use $scope!
}
};
});
Make sure to use scope instead of $scope in the link: part.

How to use isolated scope action with parameter from one directive to another

I have a directive I want to pass isolated scoped action with parameter from one directive to another. please see the Plunker.
plnkr
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
$scope.singleClick = function (test) {
alert('singleClick'+test);
}
});
app.directive('myButton', [function () {
return {
restrict: 'E',
template: '<input type="button" value="Click" ng-click="click("test")" />',
scope: { onSingleclick: '&singleclickFn' },
link: function (scope, iElm, iAttrs, controller) {
scope.click = function (test) {
alert('singleClick'+test);
scope.onSingleclick(test);
}
}
};
}]);
app.directive('myNewButton', [function () {
return {
restrict: 'E',
scope: { singleclick: '&singleclickFn' },
template: '<my-button singleclick-fn="singleclick(test)" />',
};
}]);
In controller method I have got parameter is undefined.
To pass data from directive with isolated scope to the parent scope pass an object as argument instead of the primitive. So in your myButton directive use something like
ng-click="click({inp:'test'})
and access the argument as object in the singleClick function of your controller.
In your plunkr i can see you pass the result of the function execution to you directive instead of the function itself. Therefore it is undefined as the function doesn't return anything.
use singleclick-fn="singleClick" to pass the funciton to your directive's scope.
Please make your plunkr work (I get errors in the console if i open it), so i can verify the error

AngularJS - How to access the isolated scope from the linking function?

Consider the following directive example: (Live Demo)
app.directive('phone', function() {
return {
restrict: 'E',
scope: {
tel: '#'
},
template: '<div>{{tel}}</div>',
link: function(scope, element, attrs) {
console.log(scope.tel); // undefined
}
};
});
which is used like this:
<phone tel="1234"></phone>
tel is accessible in the template, but in the linking function it is undefined. Why? How could I access the isolated scope from the linking function?
It won't get interpolated before the linking function is done (I'm not sure why this is), but you have a couple of options:
app.directive('phone', function($timeout, $interpolate) {
return {
restrict: 'E',
scope: {
tel: '#'
},
template: '<div>{{tel}}</div>',
link: function(scope, element, attrs) {
//Use $timeout service with no delay:
$timeout(function(){
console.log(scope.tel); // 1234
});
//Use $watch - will get called every time the value changes:
scope.$watch('tel',function(tel){
console.log(scope.tel); // 1234
});
//You can even use the $intrapolate service, this is basically what `#` does:
console.log($interpolate(element.attr('tel'))(scope.$parent));
// in your example tel isn't an expression but a constant so you could also do this:
console.log(attrs.tel); // 1234
}
};
});
Since # isolate scope can only pass strings, your only option is
console.log(attrs.tel)
"The # local scope property is used to access string values that are defined outside the directive" - http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-2-isolate-scope

Resources