I have an AngularJS directive with function parameter, and it works perfectly when I call the directive simply it works, and I want to generalize it.
What I got so far:
.directive('panelBox', function () {
return {
restrict: 'E',
scope: {
values: '=',
calculatefn: '&'
},
templateUrl: '/ProfitCalculator/PanelBox',
controller: function ($scope) {
$scope.calculate=function() {
$scope.calculatefn();
}
}
}
})
in the main scope:
$scope.smartBookValues= {
name:'Smart Book',
text:'Smart book header',
controls:[]
};
and the html:
<panel-box values="smartBookValues" calculateFn="smartBookCalculateFn()"></panel-box>
now I'm trying to bind the values and calculateFn, so I started with calculateFn and did:
$scope.smartBookValues= {
name:'Smart Book',
text:'Smart book header',
controls:[],
calculateFn:'smartBookCalculateFn()'
};
and the html:
<panel-box values="smartBookValues" calculateFn="{{smartBookValues.calculateFn}}"></panel-box>
but i get: [$parse:syntax]
Syntax Error: Token '{' invalid key at column 2 of the expression [{{smartBookValues.calculateFn}}] starting at [{smartBookValues.calculateFn}}].
First, you declare as:
calculatefn: '&' <-- small 'f'
so syntax in html is like:
<panel-box values="smartBookValues" calculatefn="smartBookValues.calculateFn"></panel-box> <-- no need for {{}}, as you passing as reference to scope, not as text
search Google for "how to pass function in to angular directive"
Related
I'm trying to create a custom component (directive) which is composed of an <input> box and a [-] and [+] buttons. Currently, the example below only implements the input box.
So, say I have the following HTML for my directive:
<my-input ng-blur="onBlur($event)" ng-focus="onFocus($event)"></my-input>
And for testing purposes, I use this code:
app.run(function ($rootScope) {
$rootScope.onBlur = function ($event) {
console.log('onBlur', $event);
};
$rootScope.onFocus = function ($event) {
console.log('onFocus', $event);
};
});
Now I want to create my custom <my-input> directive which has an <input> box on the template and I need the ng-blur and ng-focus set on <my-input> to respond to blur/focus events on the input box.
I have the following solution almost working: http://codepen.io/anon/pen/KpELmj
1) I have a feeling that this can be achieved in a much better way, I just can't seem to do it. Thoughts?
2) $event seems to be undefined and I can't understand why. Thoughts?
Ok figured it out. Doron's answer was a good starting point for research, but now I think I have what you are looking for. The key is you have to use & in the link section in order to get it to execute the expression.
.directive('myInput', function($timeout) {
return {
restrict: 'E',
scope: {
data: '=',
blur: '&myBlur' //this is the key line
},
template: '<input ng-blur="blur($event)" ng-model="data">'
}
})
This is how you use it:
<my-input my-blur="runBlurFunc()"></my-input>
If you really want to define the function on the root scope, you can use $scope.$root.onBlur() instead of runBlurFunc()
Hope I got your question right, did you try to use the link function?
app.directive('myInput', function () {
return {
restrict: 'E',
scope: {
ngBlur: '&',
ngFocus: '&'
},
bindToController: true,
controller: controllerFn,
controllerAs: 'ctrl',
link:function(scope){
scope.onBlur = function(ev){
console.log(ev);
}
scope.onFocus = function(ev){
console.log(ev);
}
},
template: '[-]<input ng-blur="onBlur($event)" ng-focus="onFocus($event)"></input>[+]'
}
});
I'm creating directives for a library that customers can use. I need to let the customers create their own templates for a directive and pass the absolute url value of that template into my directives. One of my directives will have another custom directive inside of it, and it's template will be figured out based upon the value of one of the parent directive's attributes. Here's an example:
<parent-dir menu-template="this.html" item-template="that.html"></parent-dir>
I have a template for this directive that looks like this:
<ul style="list: none" ng-repeat="item in menu">
<child-dir template="{{itemTemplate}}"></child-dir>
</ul>
My directives look like this:
angular.module('myApp')
.directive('parentDir', function () {
return {
restrict: 'E',
scope: {
menuTemplate: '#',
itemTemplate: '#',
menuType: '#',
menuName: '#',
menuId: '#',
},
templateUrl: function (element, attrs) {
alert('Scope: ' + attrs.menuTemplate);
return attrs.menuTemplate;
},
controller: function ($scope, $element, $attrs) {
$scope.defaultSubmit = false;
alert('Menu: '+$attrs.menuTemplate);
alert('Item: ' + $attrs.itemTemplate);
$scope.itemTemplate = $attrs.itemTemplate;
if ($attrs.$attr.hasOwnProperty('defaultSubmit')) {
alert('It does');
$scope.defaultSubmit = true;
}
}
};
})
.directive('childDir', function () {
return {
restrict: 'E',
require: '^parentDir',
templateUrl: function (element, attrs) {
alert('Item Template: ' + attrs.template);
return attrs.template;
},
controller: function ($scope, $element, $attrs) {
$scope.job;
alert('Under job: ' + $scope.itemTemplate);
}
};
});
I'm not showing all of the code but this is the main piece of my problem. When I run this, I keep getting undefined for the template on the childDir.
What is the best practice in perpetuating the value of itemTemplate from the parentDir so that the childDir can use it as it's template?
The reason you're running into problems is because the function that generates the templateUrl is running before a scope has been assigned to your directive - something that has to be done before interpolated data can be replaced.
In other words: at the point that the templateUrl function runs, the value of the template attribute is still "{{itemTemplate}}". This will remain the case until the directive's link (preLink to be precise) function runs.
I created a plunker to demonstrate the point here. Be sure to open the console. You'll see that templateUrl runs before both the parent and child linking functions.
So what do you do instead?
Fortunately, angular provides a $templateRequest service which allows you to request the template in the same way it would using templateUrl (it also uses the $templateCache which is handy).
put this code in your link function:
$templateRequest(attrs.template)
.then(function (tplString){
// compile the template then link the result with the scope.
contents = $compile(tplString)(scope);
// Insert the compiled, linked element into the DOM
elem.append(contents);
})
You can then remove any reference to the template in the directive definition object, and this will safely run once the attribute has been interpolated.
something simple im sure..
ive set up a very simple directive that accepts an isolated scope item
the template simply prints out the name.
.directive('createDirective', function () {
return {
restrict: 'E',
template: '<div>{{name}} - 123</div>',
scope: { name:'&myName'}
}
});
the HTML:-
<create-directive my-name="bob" ></create-directive>
however, its failing to print out 'bob'
im failing to see why not?
Scope configuration is incorrect. It should be:
scope: {
name: '#myName'
}
Special character & is used to setup a function reference to the outer scope function.
I am setting the following:
function appRun(
$rootScope
) {
$rootScope.abc = 99;
}
I am calling a directive like this:
<admin-retrieve-button ctrl="exam" home="home" Network="Network"></admin-retrieve-button>
Here's my directive:
app.directive('adminRetrieveButton', ['stateService', function (stateService) {
return {
scope: {
ctrl: '=',
home: '=',
Network: '=',
abc: '='
},
restrict: 'E',
template: "xxxx {{ abc }} dddd",
link: function (scope, element, attrs) {
scope.stateService = stateService;
scope.entity = attrs["entity"];
}
};
}]);
However when my HTML page comes up it shows:
xxxx dddd
Can someone tell me why the rootScope value of 99 does not show up?
Because you're declaring an isolate scope on your directive, thus creating a new parent less scope. One way to fix this would be to obviously explicitly pass in abc in your HTML:
<admin-retrieve-button ctrl="exam" abc="abc" home="home" Network="Network"></admin-retrieve-button>
Or you could change your directive to not create a scope of its own:
scope:false
or just leave the scope property alone (don't declare it), however passing in a directives dependencies through a scope of its own is good practice IMO, it's more explicit and gets rid of hidden dependencies.
I would like to manipulate the data sent to my directive, ie. I have a myUser directive that displays the user name and is used like this:
<my-user id="25" name="John Doe"></my-user>
I would like it to convert to:
<a ng-click="navTo('/user/25')">John Doe</a>
So I would like to replace any spaces with and set the new location according to the user's id. Here is my directive definition:
angular.module('myApp').directive('myUser', function ($location, $log) {
return {
restrict: 'EA',
scope: {
id: '#',
name: '#'
},
template: '<a ng-click="navTo(\'/users/\' + {{id}})">{{name}}</a>',
controller: function ($scope) {
$scope.name.replace(' ', ' ');
},
link: function (scope, element, attrs, fn) {
scope.name.replace(' ', ' ');
scope.navTo = function (route) {
$log.info('Navigating to ' + route);
$location.path(route);
};
}
};
});
However, the replace does not take place. I assume both the controller and link functions are executed after the template has been rendered.
Also, the navTo function returns the following error:
Error: [$parse:syntax] Syntax Error: Token 'id' is unexpected, expecting [:] at column 21 of the expression [navTo('/users/' + {{id}})] starting at [id}})].
Any ideas on how to solve this would be greatly appreciated.
Just replace your template with below line and it should work
template: '<a ng-click=\'navTo("/users/{{id}}")\'>{{name}}</a>',
ng-click takes an expression and doesn't need interpolation, so you don't need to use {{}}. You can just remove them, like this...
template: '<a ng-click="navTo(\'/users/\' + id)">{{name}}</a>'
Live Demo - Fiddle