I am trying to send an array of objects from parent directive to child directive but i am getting console.log($scope.$eval(attrs.model)) as undefined .
angular.module('tester').directive('gChart',gChart);
function gChart() {
var template = getTemplate;
return {
restrict: 'E',
require: "^Logs",
replace:true,
template: template,
scope:{
model: "="
},
link: function($scope, $element, attrs, LogsCtrl) {
console.log($scope.$eval(attrs.model));
LogsCtrl.show($scope.$eval(attrs.model));
}
};
function getTemplate() {
return'<div id="chart" style="width: 600px; height: 500px;margin: 0px auto;"></div>';
}
}
In this situation I would use factory and inject that factory in both directive. Sharing as per coding standard in angular should be done via factory. It will make ur life easy. If u need any help with that let me know but use factory to share data between controller and directive make application more testable also
You need to post all of your code for us to understand. Besides, your array of data should be in $scope.model, and not in the attrs.model. This is because you define the scope of your directive as
scope:{
model: "="
},
So, in your html, you should have something like:
<tester model="YOUR_DATA" ></tester>
Related
as an angular newbie this is my problem
If I have two directives in HTML like this
<parent-dir param="par">
<child-dir></child-dir>
</parent-dir>
and in JS like this (in parent)
app.directive('parentDir', function(){
return {
restrict: 'E',
scope: {
param: '='
}
}
})
and in child
app.directive('childDir', function(){
return {
restrict: 'E',
require: '^parentDir',
controller: function($scope, $element){
<-- SHOULD I PUT WATCHER HERE -->
},
link: function(scope, element, attrs, parentdirCtrl){
<-- SHOULD I PUT WATCHER HERE -->
}
}
})
where in the child directive should I make an optional $watch in order to catch all changes to the param model?
Off course if I use $watch in the parent controller, all changes in the param are reflected in the parent directive but I can`t seem to find a way to pass this information to child directive.
You should place it inside the link function which have access of the parent controller using 4th parameter of link function parentdirCtrl. Actually you don't need to worry about the params variable because it uses = two way binding inside directive that does update the value in both parent controller scope & directive scope. Additionally you need define controller in your parentDir directive so that the scope of parentDir directive shared with the childDir.
Code
app.directive('childDir', function() {
return {
restrict: 'E',
require: '^parentDir',
template: '<div class="test">INner {{param}}</div>',
controller: function($scope, $element) {
},
link: function(scope, element, attrs, parentdirCtrl) {
scope.$watch('param', function(newVal, oldVal) {
console.log(newVal);
}) //true only if its object.
}
}
})
Demo Plunkr
I made directive with isolated scope with "=" method, in that directive i pass empty array, then i push data on that array.... How that change can be reflected on original array in my controller?
Here is the example:
angular.module('myModule').controller('MyController', ['$scope', function($scope) {
$scope.test = [];
}]);
angular.module('myModule').directive('mydirective', function() {
return {
scope: {
test: "=",
bread: "="
},
restrict: 'E',
link: function(scope, element, attribute) {
scope.test.push('one more')
},
replace: true,
templateUrl: 'some template'
};
});
HTML
<div ng-controller='MyController'>
<mydirective test='test'></mydirective>
<div ng-bind='test'> </div>
</div>
When i push something on array i dont have a reflection of that in my controller.
How can i fix that?
Here's how to do what you are trying to achieve.
HTML
<!-- myCtrl contains the original array, which we push here to the scope as 'ctrl' -->
<div ng-controller='myCtrl as ctrl'>
<!-- we pass your directive 'ctrl.test', which tells angular to two-way bind to the
test property on the 'ctrl' object on the current scope -->
<mydirective test='ctrl.test'>
<!-- we're inside the isolate scope: test here refers to mydirective's idea of test,
which is the two-way bound array -->
<div ng-bind='test'></div>
</mydirective>
</div>
JS
angular.module('app', [])
.directive('mydirective', function() {
scope: {
test: '='
},
link: function($scope) {
$scope.test.push('one more');
}
})
.controller('myCtrl', function() {
this.test = [];
});
Any alterations to the array will now be reflected in the ng-bind. Please note that it is bad practice to place primitives on $scope without being part of an object (due to the mechanics of prototypical inheritance) so you'd want to change $scope.test to something else.
So I have this directive that has its own scope but I want to access to a function inside its parent controller. I can do this if the parent controller exposes the function with a $scope.getElementsList(), although I'm trying to avoid the use of $scope and I have the function exposed with self.getElementsList() and the directive cannot reach it.
Directive:
angular.module('myApp').directive('accountBalance', function() {
return {
scope: {
elementId: '=elementid'
},
transclude: true,
restrict: 'E',
templateUrl: '../views_directives/account-balance.html',
controller: function($scope) {
$scope.removeElement = function(){
//this where I want to access the parent function
console.log($scope.$parent.getElementsList());
console.log("ALSO I WANT TO ACCESS THIS DIRECTIVE elementId WITHOUT USING $scope", $scope.elementId);
}
}
};
});
ParentController:
angular.module('myApp').controller('AppDesignCtrl', function ($scope) {
var self = this;
self.elementsList = [];
self.getElementsList = function(){
return self.elementsList;
}
});
I also want to know what is the best way to access, inside the directive controller, the data passed to the directive's $scope.
scope: {
elementId: '=elementid'
},
UPDATE
<div>
<i class="fa fa-arrows element-drag"></i>
<i class="fa fa-trash-o element-remove" ng-click="removeElement()"></i>
</div>
And what about calling functions from the directive template inside the controller of the directive? Do I need to expose them with something like $scope.removeElement()? How do I use this.removeElement() and be able to access it from the template?
Sorry about the long question. I'm trying to set the best practices to my new project since I've been away from angular for a year+.
Thanks in advance
(Going from bottom to top...)
To call functions in the controller without using the scope in Angular >= 1.2, use the controllerAs syntax:
<div ng-controller="AppDesignCtrl as appDesignCtrl">
...
<i class="fa fa-trash-o element-remove" ng-click="appDesignCtrl.removeElement()"></i>
</div>
And removeElement() must be a method of the controller:
angular.module('myApp').controller('AppDesignCtrl', function ($scope) {
...
this.removeElement = function() {
...
};
});
To access the scope data from the controller in Angular >= 1.3, use the new bindToController: true configuration (this is especially useful when combined with the new controllerAs syntax):
angular.module('myApp').directive('accountBalance', function() {
return {
...
scope: {
elementId: '=elementid'
},
controller: function() {
// now elementId is a member of the controller:
console.log(this.elementId);
}
};
});
Having said these, the answer to how you can call getElementsList from the directive would be:
angular.module('myApp').directive('accountBalance', function() {
return {
...
scope: {
elementId: '=elementid',
getElementList: '&'
},
controller: function() {
...
// invoking the expression that was passed to us
var theElements = this.getElementList();
}
};
});
The correct expression should be passed as:
<div ng-controller="AppDesignCtrl as appDesignCtrl">
<account-balance element-id="xxx"
get-elements-list="appDesignCtrl.getElementsList()"></account-balance>
</div>
It is generally not recommended, because directives are meant to be self-contained. It isn't critical if you don't plan to reuse the directive. And wise usage of isolate scope can solve this.
angular.module('myApp').directive('accountBalance', function() {
return {
scope: {
outerScope: '#'
elementId: '='
},
transclude: true,
restrict: 'E',
templateUrl: '../views_directives/account-balance.html',
controller: function($scope) {
console.log("we can use anything from other controller", $scope.outerScope.elementsList)
$scope.elementId = "and share data with any other scope";
}
};
});
Controller is defined as ng-controller="AppDesignCtrl as appDesign", and directive usage is
<account-balance element-id="sharedParentScopeVar" outer-scope="appDesign">
So there won't be any problem if the directive should be moved to other controller.
I guess 'best practice' may be to set up a service that embraces the data and is used by both app controller and directive, so directive controller operates on data items and not DOM elements.
And what about calling functions from the directive template inside
the controller of the directive? Do I need to expose them with
something like $scope.removeElement()?
You surely don't. If there's a need to use functions from outside, you're doing something wrong. Send a message to respective element to run the function if it is DOM-related. Or put the function into the service if it is data-related.
So I want to create a nested struture on my nested scope inside a directive like this:
angular.module('myAddress').directive('myAddress', [function () {
return {
restrict: 'AE',
controller: 'myAddressController',
templateUrl: 'my-address.html',
scope: {
address: {
'form': '=addressForm',
'model': '=addressModel'
}
}
};
}]);
But I get an exception that undefined is not a function that I don't get if I remove the address nesting.
How do I put attribute arguments inside a named key on my scope?
Also, If I define $scope.address via the controller it doesn't work as well. But what will execute first? The scope: { 'form' = 'addressForm'} part in my directive or the controller's $scope.form?
With the scope property you define which $scope variables should pass to the directive scope and the type of data-binding.
If you want to create an nested structure within the directive $scope, you could create it in the directive controller function.
For example:
angular.module('myAddress').directive('myAddress', [function () {
return {
restrict: 'AE',
controller: 'myAddressController',
templateUrl: 'my-address.html',
scope: {
addressForm: '=', // Two-way databinding
addressModel: '='
},
controller: function($scope){
$scope.address = {
form: $scope.addressForm,
model: $scope.addressModel
}
},
link: function($scope,$element,$attributes){
//Your code here
}
};
}]);
You can also, define $scope.address in the module controller scope. Then your scope property in the directive should be look like this
scope: {
address: '='
}
UPDATE:
Another question is: Does your directive need an dedicated scope? If not you could set the scope property false. Then your directive can access the $scope variables in your module controller.
I am new to angularjs and i am stuck in accessing directive attributes in controller.
Directive
<rating max-stars="5" url="url.json"></rating>
app.directive('rating', [function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
link: function (scope, iElement, iAttrs) {
console.log(iAttrs.url); //works
}
controller
app.controller('ratingController', ['$scope', '$attrs' , '$http','$routeParams',function ($scope,$attrs,$http,$routeParams) {
console.log($attrs.url); //shows undefined
}]);
How do i access the url attribute in controller?
If you want to associate a controller with a directive, you can use the Directive Definition Object's controller property (either specifying the controller as a function or specifying the name of the controller (in which case it can be registered anywhere in your module)).
app.directive('rating', [function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
controller: 'ratingController'
};
});
// Meanwhile, in some other file
app.controller('ratingController', function ($scope, $element, $attrs) {
// Access $attrs.url
// Better yet, since you have put those values on the scope,
// access them like this: $scope.url
...
});
When using two-way data-binding via =, the corresponding attribute's value should not be a literal (because you can't have two-way data-binding to a literal), but a string specifying the name of a property in the current scope.
Using <rating max-stars="5"... together with scope: {maxStars: '='... is wrong.
You hould either use <rating max-stars="5"... and scope: {maxStars: '#'...
or <rating max-stars="someProp"... and scope: {maxStars: '='... while the enclosing scope has a property named someProp with a numeric value (e.g. $scope.someProp = 5;).
app.directive('myDirective',function(){
return{
controller: function($scope,$attrs){
console.dir($attrs);
}
}
});
That's it. If you want to access the elements attributes on a controller, you have to set up a controller for the directive.
(You could however, use a shared service to make those attributes available to another controller, if that's want you want to achieve)
http://jsbin.com/xapawoka/1/edit
Took your code and made a jsBin out of it. I can't see any problems whatsoever, so I'm assuming this is a simple typo somewhere in your code (could be the stray [ bracket at the top of your directive definition).
Here's the code:
var app = angular.module('app', []);
app.controller('ratingController',
function ($scope, $element, $attrs) {
console.log('ctrl.scope', $scope.url);
console.log('ctrl.attrs', $attrs.url);
});
app.directive('rating', function () {
return {
restrict: 'E',
scope: {
maxStars: '=',
url: '#'
},
controller: 'ratingController',
link: function (scope, el, attrs) {
console.log('dir.scope', scope.url);
console.log('dir.attrs', attrs.url);
}
};
});
And here's the output:
http://cl.ly/image/031V3W0u2L2w
ratingController is not asociated with your directive. Thus, there is no element which can hold attributes bound to that controller.
If you need to access those attributes, the link function is what you need (as you already mentioned above)
What exactly do you want to achieve?