How do I pass a parameter to a controller from a directive? - angularjs

My HTML looks like this
<td currency-convert idrVal="{{(btcAsk.btcValue * btcAsk.btcAmnt).toFixed(2)}}"></td>
The AngularJS code
The controller has the method:
$scope.convertIDRtoUSD = function(idrValue) {
return CurrencyConversions.convertToUSD(idrValue, 'IDR');
};
And the directive looks like..
bitcoinApp.directive("currencyConvert", function() {
return {
restrict: 'A',
scope: {
idrval: '#'
},
template: '<span class="has-tip" tooltip="convertIDRtoUSD({{idrval}})" tooltip-animation="false">{{idrval}}</span>'
};
});
This is currently not calling the convertIDRtoUSD method.
Reading online, I think I'm supposed to use the Isolate Scope "&" but have not had success so far.

I'll try to word this as best I can, so bear with me! Your template contains a method. At run time, it expects this method to be present on the current scope. However, at that point, the scope is the isolated scope you created with the directive, and simply contains the value of idrval, so that method is undefined.
You need to either add the function convertIDRtoUSD() to the scope of the directive, or pass the function into your directive along with idrval. If you choose the former, you'r directive might look like this:
bitcoinApp.directive("currencyConvert", function() {
return {
restrict: 'A',
scope: {
idrval: '#'
},
template: '<span class="has-tip" tooltip="convertIDRtoUSD({{idrval}})" tooltip-animation="false">{{idrval}}</span>',
link: function(scope) {
scope.convertIDRtoUSD = function(idrValue) {
return CurrencyConversions.convertToUSD(idrValue, 'IDR');
};
}
};
});
If you want to pass the function in, and the function exists on the controller, your html would look something like this:
<td currency-convert my-func="convertIDRtoUSD(val)" idrVal="{{(btcAsk.btcValue * btcAsk.btcAmnt).toFixed(2)}}"></td>
And your directive:
bitcoinApp.directive("currencyConvert", function() {
return {
restrict: 'A',
scope: {
idrval: '#',
myFunc: '&'
},
template: '<span class="has-tip" tooltip="myFunc({val: idrval})" tooltip-animation="false">{{idrval}}</span>'
};
});
Some things to note - the attribute name of the function should be 'dash-named' not camel-case named, and parameters to the function passed into the directive have to be passed an an object with named values.
Hope this helps!

Related

Angular: Bind callback function using & and pass-in arguments

I have a (simplified) directive
angular.module('myApp')
.directive('myButton', function () {
return {
restrict: 'E',
scope: {
callbackFn: '&'
},
template: '<button ng-click=ca;;backFn($evenb)'
}
});
Now, in some parent controller I have defined a callback function:
this.myCallback = function ($event) {
this.doIt($event);
}
and the HTML:
<my-button callback-fn="page.myCallback()"></my-button>
(I'm using things like bindToController and controllerAs)
The issue is that the $event is never passed to myCallback, which most likely has to do with how I bind this function (&). But on the other hand, inside myCallback I would like to use this.
Is there some way to fix this ? without doing things like
var self = this;
this.myCallback = function ($event) {
self.doIt($event);
}
You haven't completely set up your bindings correctly. You can pass back arguments from the directive to the parent controller via a key-value map. According to the angular docs (emphasis mine):
& or &attr - provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given <widget my-attr="count = count + value"> and widget definition of scope: { localFn:'&myAttr'}, then isolate scope property localFn will point to a function wrapper for the count = count + value expression. Often it's desirable to pass data from the isolated scope via an expression to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22}).
So that means in your consuming HTML you need to add parameters:
<my-button callback-fn="page.myCallback(parentEvent)"></my-button>
And then in the directive:
......
restrict: 'E',
scope: {
callbackFn: '&'
},
template: '<button ng-click="ctrl.callbackFn({parentEvent: $event})">Callback</button>'
,
According to me you should do it this way :
In your HTML page :
<my-button callback-fn="page.myCallback(event)"></my-button>
In your directive :
angular.module('myApp')
.directive('myButton', function () {
return {
restrict: 'E',
controller: 'Controller',
bindToController: true,
scope: {
callbackFn: '&'
},
template: '<button ng-click=foo($event)'
}
});
function Controller() {
this.foo = function (event) {
this.callbackFn({event: event});
}
}
But I'm not sur what's the point of your question.

loop function through nested directive - angularjs

I try to loop a function through a nested directive. From the console.info in myCtrl I would expect the string "this should be logged".
angular.module('myApp')
.controller('myCtrl', function ($scope) {
$scope.aFunction = function(input) {
console.info(input.message);
}
})
.directive('levelOneDirective', function () {
return {
templateUrl: '<level-two-directive aFunction="aFunction(object)"></level-two-directive>',
restrict: 'EA',
scope: {
aFunction:"&"
},
link: function (scope, element, attrs) {
}
};
})
.directive('levelTwoDirective', function () {
return {
templateUrl: '<div ng-click="aFunction({message: 'this should be logged'})"></div>',
restrict: 'EA',
scope: {
aFunction:"&"
},
link: function (scope, element, attrs) {
}
};
});
And in my index.html I have something like:
<div ng-controller="myCtrl">
<level-one-directive aFunction="aFunction(object)"></level-one-directive>
</div>
But the console says undefined.
How to connect a function through nested directives?
You have several mistakes in your code but I assume it's because you try to adjust it to the question (such as aFunction as attribute instead of a-function and templateUrl instead of template).
You can have a 2-way binding (=) in your directives (both of them):
scope: {
aFunction:"="
},
And pass the function reference without the object:
<level-one-directive a-function="aFunction"></level-one-directive>
In the second directive HTML have:
<div ng-click="invokeFunction()"></div>
And then in the link function of your 2nd directive you can do:
scope.invokeFunction = function () {
scope.aFunction({message: 'this should be logged'});
}
The above works and I find it more convenient than & binding, which as you can see, is not quite easy to work with, and frankly I haven't messed around enough with it to figure out how (and if possible) to pass arguments through it.
I've seen this question, but it's binding straight on the link function, and you want it with an ng-click so it might not work for you. But perhaps you'll find your solution there.

How do you pass in data to custom directives as a value of the attribute itself?

I have a directive defined as such:
angular.module("main.directives").directive("todo", function() {
return {
restrict: "A",
scope: {
todo: "=entity"
},
replace: false,
templateUrl: "todo.html",
link: function(scope, element, attributes) {
}
};
});
which I use like this from templates:
<div todo entity="todoData"></div>
todoData comes from a controller or some other the local scope. Anyway it all works like a charm, so that's cool!
My question is the following: How do I have to modify the directive definition so that it also works with a markup of this type:
<div todo="todoData"></div>
As you can see the data is now passed in as the value of the attribute marking the directive. Just like ng- directives do:
<p ng-repeat="bit in data"></p>
<p ng-click="whatever()"></p>
How can that be achieved?
Thanks
Replace
scope: {
todo: "=entity"
},
by
scope: {
todo: "=todo"
},
or simply
scope: {
todo: "="
},
When you write an attribute directive in angularjs you might want to have it fed by an attribute value.
For example, something like this:
<div my-attribute="somevalue"></div>
How then do you create a new scope that takes that in? It's not obvious. Any here's how you do it:
app.directive('myAttribute', function() {
return {
restrict: 'A',
scope: {
myAttribute: '='
},
template: '<div style="font-weight:bold">{{ myAttribute | number:2}}</div>'
};
});
The trick to notice is that the "self attribute" because of the name of the attribute in camel case.
Here is the Reference to This Answer!
you must eval the value of the attribute inself. The isolate scope is not one of my favorites kind of scopes for a directive. Instead you can use, scope = true, to inherit from the parent controller. This will allow you to use all the variable exposes on the parents scopes.
in your case.
angular.module("main.directives").directive("todo", function() {
return {
restrict: "A",
scope: true,
replace: false,
templateUrl: "todo.html",
link: function(scope, element, attributes) {
scope.todo = scope.$eval(attributes[todo]);
}
};
});
now your todo directive could be used. Like any other ng- directive.
example:
<div todo="getTodoList()"></div>
<div todo="[{description:'hahahha'}]"></div>

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.

Directive referencing its own id

I've used a directive to utilize jqueryUI dialogs.
app.directive('popUp', function() {
return {
restrict: 'E',
scope: {
myId: '#',
onCancel: '&'
},
template:
'<div id="{{myId}}">
<button ng-click="onCancel()">...</button>
...
</div>'
link: function(scope, element, attrs) {
scope.closeDialog = function() {
$("#" + id).dialog('close');
}
// question 1: how to reference id of this directive (self)?
// question 2: should it be here, in compile, or in directive controller?
// question 3: 'ng-click=closeDialog()' missing when popup element inspected in firebug/dev tool
// question 4: is there a way to avoid jquery like selector $("#" + id) to reference this element?
}
};
});
And this is the html:
<pop-up my-id="success" on-cancel="closeDialog()"> ... </pop-up>
If I declare an external controller and closeDialog function attached to its $scope, this works fine, like this:
app.controller('DialogCtrl', function($scope) {
$scope.closeDialog = function(id) {
$("#" + id).dialog('close');
};
});
html
<div ng-controller="DialogCtrl">
<pop-up my-id="success" on-cancel="closeDialog('success')"> ... </pop-up>
</div>
But what I want to avoid is redundancy of the id. So I want the directive to have its own close function. If you also have answers on the other questions above, it is very much appreciated. Thanks.
This is essentially what you want. There's no need to use $ selectors and ids to find your dialog since element in the link function will give you a reference to the element that the directive is applied to.
Define the closeDialog function on the directive's scope and you can reference it from the directive's template. Each instance of the directive will have its own close function.
app.directive('popUp', function() {
return {
restrict: 'E',
scope: {
myId: '#'
},
template:
'<div id="{{myId}}">
<button ng-click="closeDialog()">...</button>
...
</div>'
link: function(scope, element, attrs) {
// initialise dialog
element.dialog();
// the template's ng-click will call this
scope.closeDialog = function() {
element.dialog('close');
};
}
};
});
No need for an on-cancel attribute now.
<pop-up my-id="success"></pop-up>

Resources