I would like to define both controller, and require on a directive. Something like this:
return {
controller: 'ValidateElementCtrl as validate',
require: 'ngModel',
scope: {
ngModel: '='
},
link: link
};
Now when defining either controller, or requiring ng model, in the link function you get access only to the fourth argument. I know that the fourth argument can be an object and contain multiple controllers and such, but that's in case when you are defining the controllers as an array.
This is what I have and I don't know how to access the controller, I get the required ngModel as a fourth argument:
function link(scope, element, attrs, ctrlGetsNgModel) {
console.log(ctrlGetsNgModel) // Returns the ngModel, no sign on the controller
}
I know I could define a controller on a directive, and pass it in as a scope property, but in this case I would like to define a controller for the directive which would handle the validation and similar, and that controller would be assigned only to this directive.
EDIT:
Found a way how to do this:
To have both ngModel and controller in the link function you can assign the controller to the template like this:
Then define the scope access to the someDirectiveName: '=', and you can access the controller on your directive scope `scope.someDirectiveName' =< the controller scope.
Not exactly sure what you are trying to achieve but I don't think you can put 'controller as' in a string like that in a directive definition. Use the controllerAs property, so something like:
return {
// ...
controller: controller,
controllerAs: 'validate'
// ....
};
You will to also use bindToController: true if you want to access any properties defined on an isolate scope. But I'm not sure if you need an isolate scope at all..
Could you clarify what your actual goal is? Is this what you are aiming for???
DEMO
html
<body ng-controller="MainCtrl">
<foo ng-model="someModel"></foo>
</body>
js
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
// the element on which you apply the directive will need to have a model
// defined if you use `require: 'ngModel'`
$scope.someModel = {modelValue: 'blah'};
});
app.directive('foo', function(){
return {
controller: controller,
controllerAs: 'validate',
template: '<div>{{validate.someProp}}</div>',
require: 'ngModel',
link: link
};
function controller(){
this.someProp = 'bar';
}
function link(scope, element, attrs, ctrlGetsNgModel) {
console.log('ctrlGetsNgModel',ctrlGetsNgModel) // Returns the ngModel, no sign on the controller
}
});
Suppose your directive's name is "validateField", then you can pass an array like this:
return {
controller: controller,
controllerAs: 'validate',
require: ['ngModel', 'validateField'],
scope: {
ngModel: '='
},
link: link
};
Then in the link function the fourth parameter will be an array that contains both ngModel controller and the directive's controller.
function link(scope, element, attrs, ctrlArray) {
console.log(ctrlArray[0]) // Returns the ngModel controller
console.log(ctrlArray[1]) // Returns your controller
}
Related
I am trying to use $emit in my code for a directive.
I have
(function () {
angular
.module('myApp')
.directive('testDirective', testDirective);
function testDirective() {
var directive = {
link: link,
restrict: 'A',
controller: testCtrl,
controllerAs: 'vm'
};
return directive;
function link(scope, element, attrs) {
}
}
function testCtrl() {
var vm = this;
// do something
vm.$emit('someEvent', {'id': '123'})
}
})();
However, I am getting 'TypeError: vm.$emit is not a function'. I am not sure how to fix this. Can anyone help? Thanks a lot!
controllerAs just means that scoped variables are attached directly to the controller -- it doesn't mean that the controller is an instance of an angular scope itself. In this case, you'll need to inject the scope into the controller and then emit the event from on the scope:
function testCtrl($scope) {
// do something
$scope.$emit('someEvent', {'id': '123'})
}
Also beware, the normal injection rules apply -- If you're going to minify this, you'll probably need something like:
testCtrl['$inject'] = ['$scope'];
Problem: The attribute I pass to my directive's controller is not evaluated. For example, I get {{ attribute.value }} instead of 5.
Desired Outcome: My directive's controller has access to a primary key contained in an object from a parent controller. I need it to make API calls like MyResource.save({id: attribute});.
Code Snippets:
Calling directive from HTML
<div ng-controller="BoatDetailController as boatCtrl">
<div class="row">
<booking-widget boat-id="{{ boatCtrl.boat.id }}"></booking-widget>
</div>
Directive
(function () {
'use strict';
angular.
module('trips').
directive('bookingWidget', bookingWidget);
bookingWidget.$inject = [];
function bookingWidget() {
return {
restrict: 'E',
scope: {
boatId: '#'
},
templateUrl: "/static/app/trips/trips.bookingwidget.template.html",
controller: 'BookingWidgetController as bookingCtrl'
}
}
})();
Controller
(function () {
'use strict';
angular.
module('trips').
controller('BookingWidgetController', BookingWidgetController);
BookingWidgetController.$inject = ['Trip', 'Booking', 'Messages', '$scope', '$attrs'];
function BookingWidgetController(Trip, Booking, Messages, $scope, $attrs) {
var vm = this;
vm.boatId = $attrs.boatId;
...
activate();
//////////////////////////////
function activate() {
console.log(vm.boatId);
//
}
Console Results:
With $scope.boatId: (logs a blank line)
With $attrs.boatId: {{ boatCtrl.boat.id }} (a string)
Recap: The boat-id attribute of my directive is not resolving. Can you help me figure out how to fix it?
You can actually create a custom directive like this:
function bookingWidget() {
return {
restrict: 'E',
scope: {
boatId: '#'
},
templateUrl: "/static/app/trips/trips.bookingwidget.template.html",
controller: 'BookingWidgetController as bookingCtrl',
link : function(scope, element, attrs, controller){
console.log(attrs.boatId);
scope.boatId = attrs.boatId;
}
}
}
The link function actually allows you to have an access to the element, the scope of the directive, the attributes associated to the directive and the controller of the directive. The function is called after everything associated to the directive has been performed. In other words, this is the last stage.
The same scope would be shareable between the link function and controller.
Now, to make the API call, you may actually add a function in your controller that accepts the boatID, makes a call to the API and accepts the response onto the controller object. After that, add a watcher within the link function that watches over "vm.boatId", within which you may call that function which makes the API call. So, even if the controller has initialized before the link function, you would still be able to perform what you wish to. So, it would be a "link-based activation".
You may give this solution a try. Hope it helps.
You can pass a function and call it. Need to use & then.
https://thinkster.io/egghead/isolate-scope-am
So I am so confused! I have HTML that looks like this:
<span paper-embed="" url="theUrl"></span>
and theUrl is a variable that loads a different URL from my ng-controller. Then I have an Angular directive looks like this:
app.directive('paperEmbed', function() {
return {
restrict: 'AEC',
transclude:true,
scope: {
key: '=',
value: '='
},
link: function(scope, element, attrs) {
// is does some jQuery here
}
};
});
my question is, I want to access the URL inside the directive - the variable named theUrl, so how do I do that? I looked up on SA and seemed like
console.log({{theUrl}});
might work but it does not.
Change your scope to
scope: {
key: '=',
value: '=',
url: '='
},
And in your link function you can use it like scop.url
You can access it with attrs.url
Since you are trying to implement a directive with the following:
<span paper-embed="" url="theUrl"></span>
you should use restrict: 'A',
the url attribute can be accessed either through your scope declaration as # or through the attrs object passed into your link function.
as to the console.log({{theUrl}}). {{}} is specifically design to interpolate javascript-keys in the markup, that is processed through the $compile cycle.
Here is something to get you started:
HTML
<span paper-embed url="http://example.com" />
javascript
var app = angular.module('exampleApp', []);
app.directive('paperEmbed', function() {
return function(scope, elem, attrs) {
console.log(attrs.url);
};
});
get a little more detailed information here: https://docs.angularjs.org/api/ng/service/$compile#directive-definition-object
You can either use attrs.url to access it or you can add the url to the scope:
app.directive('paperEmbed', function() {
return {
restrict: 'AEC',
transclude:true,
scope: {
key: '=',
value: '=',
url: '#'
},
link: function(scope, element, attrs) {
console.log(scope.url);
}
};
Whenever you add a variable to the scope like above, the directive is expecting an html attribute to supply that value. In this case url.
<div paper-embed="obj1" key="obj2" value="obj3" url="obj4"></div>
scope.url === obj4;
Among many others, there are two easy ways to do this:
- First method is to access it using the attrs.
- Second method is to pass the parameters by defining in the scope.
First method is to use attrs. attrs has all the attributes. You can simply access the url in the link function as attrs.url
link:function(scope,element,attrs){
console.log(attrs.url)
}
You will see whatever is placed instead of theUrl is displayed on the console.
The other alternative is to change your directive to the following:
app.directive('paperEmbed', function() {
return {
restrict: 'AEC',
transclude:true,
scope: {
key: '=',
value: '=',
theUrl: '='
},
link: function(scope, element, attrs) {
// is does some jQuery here
}};});
Now you can access it in the link function as scope.url. To set the value to be passed simply set $scope.theUrl='whatever your url is' in your ng-controller.
I am developing a directive which shows and hides it's contents based on a click event (ng-click) defined in it's template. On some views where the directive is used I'd like to be able to know if the directive is currently showing or hiding it's contents so I can respond to the DOM changes. The directive has isolated scope and I am trying to notify the parent scope when the directive has been "toggled". I'm attempting to accomplish this by passing a callback function to the directive where it is used that can be called when the directive's state changes i.e hides or shows
I'm not sure how to correctly implement this being that the state of the directive (hidden or shown) is stored in the directive's isolated scope and is determined after the ng-click. Therefore I need to call the parent scope's function from within the directive and not from withing the view.
This will make WAAY more sense with an example. Here is a plunked demonstrating what I'd like to do:
http://plnkr.co/edit/hHwwxjssOKiphTSO1VIS?p=info
var app = angular.module('main-module',[])
app.controller('MainController', function($scope){
$scope.myValue = 'test value';
$scope.parentToggle = function(value){
$scope.myValue = value;
};
});
app.directive('toggle', function(){
return {
restrict: 'A',
template: '<a ng-click="toggle();">Click Me</a>',
replace: true,
scope: {
OnToggle: '&'
},
link: function($scope, elem, attrs, controller) {
$scope.toggleValue = false;
$scope.toggle = function () {
$scope.toggleValue = !$scope.toggleValue;
$scope.OnToggle($scope.toggleValue)
};
}
};
});
I'm relatively new to Angular. Is this a bad idea to begin with? Should I be using a service or something rather than passing around function refs?
Thanks!
Update
You can also use & to bind the function of the root scope (that is actually the purpose of &).
To do so the directive needs to be slightly changed:
app.directive('toggle', function(){
return {
restrict: 'A',
template: '<a ng-click="f()">Click Me</a>',
replace: true,
scope: {
toggle: '&'
},
controller: function($scope) {
$scope.toggleValue = false;
$scope.f = function() {
$scope.toggleValue = !$scope.toggleValue;
$scope.toggle({message: $scope.toggleValue});
};
}
};
});
You can use like this:
<div toggle="parentToggle(message)"></div>
Plunk
You could bind the function using =. In addition ensure the property name in your scope and tag are matching (AngularJS translates CamelCase to dash notation).
Before:
scope: {
OnToggle: '&'
}
After:
scope: {
onToggle: '='
}
Furthermore don't use on-toggle="parentToggle({value: toggleValue})" in your main template. You do not want to call the function but just passing a pointer of the function to the directive.
Plunk
I have a directive inside another directive. Outer directive shares its scope with controller, while inner one has its own. I'd like to pass a reference to controller's function to inner directive so it can be called from there. But I cannot figure out how to pass the function and its parameters to inner directive so it can properly call the controller's function.
Here is planker to illustrate my problem.
If you click on "Dir 2 Click me" the alert says the parameters have came undefined.
You can pass in the outer controller method using '=' and adjust the code accordingly...
angular.module('app', [])
.controller('ctrl', function($scope){
$scope.myCtrlMethod = function(msg, b) {
alert(msg + ' and b='+b);
};
})
.directive('dir1', [function(){
return {
restrict: 'E',
replace: true,
template: '<div><p ng-click="myDir1Method(\'my dir1 method\',\'b\')">Dir 1 Click me</p><dir2 my-ctrl-method="myCtrlMethod"></dir2></div>',
link: function(scope, elem, attrs){
scope.myDir1Method = function(msg,b){
scope.myCtrlMethod(msg, b);
};
}
};
}])
.directive('dir2', [function(){
return {
restrict: 'E',
scope: {
myCtrlMethod: '='
},
replace: true,
template: '<p ng-click="myDir2Method(\'my dir2 method\',\'b\')">Dir 2 Click me</p>',
link: function(scope, elem, attrs){
scope.myDir2Method = function(msg,b){
scope.myCtrlMethod(msg, b);
};
}
};
}]);
Plunker: http://plnkr.co/edit/xbSNXaSmzWa3G1GSH6Af?p=preview
Edit: '=' evaluates the expression in the context of the parent scope and its result is bound to the property on the inner scope. In this example, 'myCtrlMethod' is evaluated against the parent scope, which returns myCtrlMethod from the parent scope (a function). This function is bound to myCtrlMethod on the inner scope, and can be invoked with scope.myCtrlMethod(msg, b).
you can use controller as a reference to your directive
See: http://jsbin.com/vayij/1/edit
directive('sonDirective', function(){
return {
restrict: 'E'
scope: {},
replace: true,
template: '<div....'
controller: 'MainController' //controller as a reference
}
})
Just put the controller on the scope : $scope.$b=this;
See : http://plnkr.co/edit/skDF8D1scFJYrTUmcXIL?p=preview