I try to put in my directive a way to different template, based on a $scope value. I use directive inside ng-repeat and i send her the object of datas and a other direct input
directive :
/**
* angular-streamlist directive
*/
angular.module('ngStreamlist', []).directive('webcams', function() {
return {
restrict: 'A',
scope: {
stream_webcam: '=webcam',
stream_margin: '#margin'
},
templateUrl: function(elem, attr) {
if (scope.stream_webcam.webcam_domain) {
if (scope.stream_webcam.webcam_domain == 'youtube') {
return 'templates/youtube.html';
}
if (scope.stream_webcam.webcam_domain == 'twitcam') {
return 'templates/twitcam.html';
}
}
}
};
});
html :
<div data-ng-repeat="webcam in datas.webcams">
<div data-webcams data-webcam="webcam" data-margin="no"></div>
</div>
and data is like :
{
"id": 1,
"webcam_domain": "youtube", ... etc
}
I have this error :
ReferenceError: scope is not defined
at Object.templateUrl (streamlist.js:13)
I don't understand, scope IS defined, no ?
The problem is that scope is not a local variable available to your templateUrl function. It is a field on the same object. You should be able to access it with this.scope so long as the calling angular code doesn't set this to another object.
You need to add the scope to your templateUrl function as well like so:
templateUrl: function(scope, elem, attr)
Otherwise your function won't have access to it, hence your error message.
Related
I have a controller that i want to use in my directive, how can i use controller for directive i want to access all controller functions in directive ?
directive.js
angular.module('App').directive('deleteButtons', function (prcDeleteFactory,$rootScope) {
'use strict';
return{
restrict:'E',
scope:{
id: '=ids',
name: '#'
},
templateUrl:'scripts/common/prcDeleteDirective.html',
link:function(scope){
scope.deleteButton = function(){
if(scope.name === 'process') {
prcDeleteFactory.deleteObj(scope.name,scope.id);
$rootScope.$broadcast('event');
} else if(scope.name === 'risk') {
prcDeleteFactory.deleteObj(scope.name,scope.id);
} else if(scope.name === 'control'){
prcDeleteFactory.deleteObj(scope.name,scope.id);
}
}
}
}
});
Ctrl.js
angular.module('App').controller('ProcessCtrl', function($scope) {
'use strict';
$scope.confirmationWin = function(){
console.log('Print value');
};
});
You can define a controller using an anonymous function or by passing the string of an existing controller function registered on the same module like below.
return {
restrict:'E',
controller : 'ProcessCtrl'
};
As you're using an isolated scope for your directive, to access controller properties and methods, you had to define the controller explicitly.
In case you don't want to use an isolated scope, you can remove the scope property from your Directive Definition Object that you're returning., The default value is false which means your directive will share the same scope as the enclosing container's controller. You can set scope : true which means your directive will get a new scope which prototypically inherits all methods and properties from the enclosing controller.
For more details on directive scopes, you can refer to this link.
in your directive you can use
controller : 'ProcessCtrl',
controllerAs: 'vm',
bindToController: true
and then access controller properties in your directive template with vm.confirmationWin for example
I have an element directive that looks like this:
app.directive("ngArticle", [function(){
return {
restrict: "E",
replace: true,
templateUrl: "/partials/article.html",
scope: { article: "=article" }
}
}]);
And used like this:
<ng-article data-article="{{object goes here}}"></ng-article>
This works absolutely fine. But now, I want to access part of the object passed through the directive and process it before it renders out. For example, if the object inside data-article looks like this:
{
category: "Going Out",
id: "1234"
}
In the directive, I want to access the category value, replace whitespace with hyphens and change it all to lowercase. Is this possible within the directive?
I don't want to do this in a controller because the directive is used across multiple controllers.
You can access your object before it rendered using link function within directive:
app.directive("ngArticle", [function(){
return {
restrict: "E",
replace: true,
templateUrl: "/partials/article.html",
scope: { article: "=article" },
link(scope, element, attrs) {
...
}
}
}]);
Also, scope is not interpolated before the linking function so you can watch your article object:
scope.$watch('article', function() {
// do your transformations here
}
Remember to clean up your watchers before your directive destroy, this can prevent memory leaks
scope.$on('$destroy', function () {
// clean up watchers here
});
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'm creating a custom directive similar to a list-box. This is my directive definition:
angular.module('Utilities')
.directive('searchList', [
function () {
return {
restrict: 'E',
templateUrl: '/app/scripts/Utilities/search/search.html',
controller: 'SearchCtrl',
scope: {
itemsSource: '=',
itemTemplate: '#',
filterText: '=?',
selectedItem: '=?',
}
};
}
]);
Here's how I want to use it in my view:
<search-list items-source="productsSource"
item-template="{{item.Name}} Selling for: {{item.Price}}"
selected-item="selectedProduct" />
Both productsSource and selectedProduct come from the view's scope (they work fine). I want item-template to be taken straight up as text (there is no item object in the scope used by my view).
Inside SearchCtrl I obtain the items to show in my search-list, and then I want to apply that item-template to each item (through the use of the $compile service).
The problem is that inside SearchCtrl $scope.itemTemplate is equal to Selling for: (the {{}} syntax was resolved, not passed as text)
tl;dr
My search.html template looks like this:
<li ng-repeat="item in itemsDataSource" ng-class-odd="'oddRow'" ng-class-even="'evenRow'">
<div class="searchResultsItem" ng-click="onItemSelected(item)">
<span compile="internalItemTemplate"></span>
</div>
</li>
Since itemTemplate is one-way binding, the SearchCtrl will reassign it to internalItemTemplate.
if (typeof $scope.itemTemplate === 'undefined') {
$scope.internalItemTemplate = '{{item}}';
} else {
$scope.internalItemTemplate = $scope.itemTemplate;
}
The compile directive on the span tag was borrowed from: Angular Docs for $compile
It looks like this:
angular.module('Utilities')
.directive('compile', ['$compile',
function ($compile) {
// directive factory creates a link function
return function (scope, element, attrs) {
scope.$watch(
function (scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function (value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}
]);
If I hard-code $scope.internalItemTemplate in SearchCtrl to be '{{item.Name}} Selling for: {{item.Price}}', than it works.
How do I get my directive to allow the passing of {{}} without trying to resolve it?
Changing the scope type from # to = and then surrounding it in quotes worked.
The directive would look like this:
angular.module('Utilities')
.directive('searchList', [
function () {
return {
restrict: 'E',
templateUrl: '/app/scripts/Utilities/search/search.html',
controller: 'SearchCtrl',
scope: {
itemsSource: '=',
itemTemplate: '=',
filterText: '=?',
selectedItem: '=?',
}
};
}
]);
and the view would look like this:
<search-list items-source="productsSource"
item-template="'{{item.Name}} Selling for: {{item.Price}}'"
selected-item="selectedProduct" />
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?