How can I access a variable in the scope of a directive?
I have a directive like this:
angular.module('app').directive('any', function() {
return {
restrict: 'E',
scope: {
attr: '#'
},
controller: function($scope) {
$scope.attr = $scope.attr != undefined;
},
template: 'Result (true/false): {{attr}}'
};
});
And in the HTML, for example:
<any attr="bla"></any>
But the variable $scope.attr remains bla, does not change with the value of true or false. How can I do?
Just don't use the same variable to hold the comparison with undefined and the actual attr value.
Every time angular executes its digest loop, it resets the new value of attr in the scope.
When you create an isolate scope in AngularJS, AngularJS provides 3 types of prefixes:
"#" ( Text binding / one-way binding )
"=" ( Direct model binding / two-way binding )
"&" ( Behaviour binding / Method binding )
What you are using is the one-way binding method. For you to use it in your HTML correctly, you need to do this:
<any attr="{{ bla }}"></any>
Related
I'm having a go at a directive which will dynamically load a template based on a scope value passed into it.
I am using ng-repeat on my directive, and am using the iterated item as an attribute property:
<my-form-field ng-repeat="field in customFields" field="field">
In my directive I have the following code to set the template being used.
(function() {
'use strict';
angular
.module('app')
.directive('myFormField', myFormField);
function myFormField() {
var directive = {
restrict: 'E',
scope: {
field: '='
},
link: function(scope){
scope.getContentUrl = function() {
return 'app/modules/form_components/form_field/' + scope.field.type + '-template.html';
}
},
template: '<div ng-include="getContentUrl()"></div>'
};
return directive;
}
})();
Whilst the above works (which I found from other posts), I wonder if there is a better way.
For example I have seen examples of calling a function on the templateUrl config option instead, and then in that function access the scope attributes being passed in. When I tried this way, my field attribute was a literal 'field' string value (they are objects in my customFields array), so I think at that point the scope variables had not yet been evaluated.
With this current solution I am using, all of my templates get wrapped in an extra div since I am using ng-include, so I am just trying to make the rendered markup more succinct.
Any suggestions\advice is appreciated.
Thanks
I have passed an object from controller to directive but when i am reading object in directive i am not able to, it seems in directive object is being read as string.Code is below , i wane to read City and State from the object.
Html File
<div ng-controller="WeatherController">
<div weather ng-object="{{Object}}"></div>
</div>
Controller
.controller('WeatherController', ['$scope', function ($scope) {
$scope.webpartData.OverviewData.Person.Address.City;
$scope.Object = {
City: '',
State: ''
};
$scope.Object.City = 'TestCity';
$scope.Object.State = 'TestState';
});
})
}])
Directive
angular.module('WeatherModule', [])
.directive('Weather', ["$timeout", function($timeout) {
return {
restrict: 'EA',
template: '<div id="weather"></div>',
scope: {
ngObject: '#ngObject'
},
link: function(scope, element, attrs) {
scope.$watch('ngObject', function(value) {
scope.ngObject = value;
});
$timeout(function() {
console.log('Location' + scope.Object.City + ',' + scope.Object.State);
}, 100);
}
};
}])
There are differences between #, = and & in referencing the directive scope members.
1. "#" (Text binding / one-way binding)
2. "=" (Direct model binding / two-way binding)
3. "&" (Behaviour binding / Method binding)
# means that the changes from the controller scope will be reflected in the directive scope but if you modify the value in the directive scope, the controller scope variable will not get affected.
# always expects the mapped attribute to be an expression. This is very important; because to make the “#” prefix work, we need to wrap the attribute value inside {{}}.
= is birectional so if you change the variable in directive scope, the controller scope variable gets affected as well
& is used to bind controller scope method so that if needed we can call it from the directive
In your case you may need to use = instead of #
Have a look on the following fiddle (It's not mine, but it have good and to the point illustration) http://jsfiddle.net/maxisam/QrCXh/
Some Related Questions also:
What is the difference between '#' and '=' in directive scope in AngularJS?
What is the difference between & vs # and = in angularJS
You should use "=" instead "#". And ng-object="Object" instead ng-object="{{Object}}".
I created my own directive in angularjs and I noticed that directive scope is not refreshing when I change scope in main controller.
I made simple example which after 3s changing scope value, but the value is not changed in the directive. The issue exists only if I assign directive provided value to the directive scope.
Entire example is available here: jsfiddle
My directive:
myApp.directive('textPresenter', function() {
return {
transclude : false,
restrict: 'E',
scope: {
adfName : '='
},
template: '{{xxx}}' ,
controller: function($scope){
$scope.xxx = $scope.adfName; //this is the issue
}
};
});
$scope.xxx should refresh, after scope change in main controller.
The scope value adfName in the template is bound to the one in your main controller by Angular, and will change when that one changes. If you change the template to:
template: '{{adfName}}'
as in http://jsfiddle.net/6bp7c/1/ , then you can see this.
However, from your comment, you say you can't do this. Then one possibility would be setup a watcher in your directive, to watch adfName, and set xxx to be equal to it when it changes:
$scope.$watch('adfName', function(newAdfName) {
$scope.xxx = newAdfName;
});
as can be seen at http://jsfiddle.net/7V9FV/ . This is necessary because
$scope.xxx = $scope.adfName
just copies the contents of adfName and put it into xxx. If adfName later changes, then the contents of xxx are unaffected.
I am curious as to why you have to copy the variable though, and not just change the template to use the one passed into the directive.
I want to pass a product's id to a directive like so:
<widget product-id="product.id"></widget>
I prefer not to use curly braces:
<widget product-id="{{product.id}}"></widget>
because it's more verbose, and I want to match the style of ng-model usage.
I want to use isolate scope so that I can't accidentally modify product.id inside the widget.
If I use:
scope {
productId: '#'
}
Then in my directive template: {{productId}} gives me the string "product.id"
If I use:
scope {
productId: '&'
}
I see no output in my directive template {{productId}}, and as I understand it & is for binding functions not properties.
If I use:
scope {
productId: '='
}
I get the exact value that I want (a number), but isn't this two-way binding and vulnerable to changing the product.id in the widget?
If you want to avoid the double curlys you can use $parse to process the Angular expression yourself:
Expressions are JavaScript-like code snippets that are usually placed
in bindings such as {{ expression }}. Expressions are processed by the
$parse service.
To use $parse in your directive you need to provide a scope (context) to parse against. Since you're using an isolated scope the value of product.id will be on the directive's parent's scope- so we'll use scope.$parent.
Here's an example:
myApp.directive('widget', function ($parse) {
return {
restrict: "E",
scope: {
productId: '#'
},
link: function(scope, element, attr) {
nvalue = $parse(scope.productId)(scope.$parent);
console.log("parse result",nvalue);
}
};
});
demo fiddle
I'm afraid there is no binding that fits your need, you can try this:
link:function(scope, element, attr){
scope.productId = scope.$parent.$eval(attr.productid);
}
But this approach is not recommended
DEMO
The recommended approach is using # binding and {{}} in your html
scope:{
productId: '#'
}
Your HTML:
<widget product-id="{{productId}}"></widget>
DEMO
The directive has isolate scope, and the scope attributes are being passed with "#".
This is how the directive is called:
<div ng-init="classForIcon = 'vladClass'"></div>
<div ng-init="textForIcon = 'Icon\'s text'"></div>
<div ng-init="routeForIcon = 'www.google.com'"></div>
<div ng-init="tooltipForIcon = 'my tooltip'"></div>
<div ng-init="imageForIcon = 'images/HOME_ICON1.png'"></div>
<rl-icon-widget icon-class="{{classForIcon}}" icon-text = "{{textForIcon}}" icon-tooltip="{{tooltipForIcon}}" icon-route="{{routeForIcon}}" icon-image="{{imageForIcon}}"></rl-icon-widget>
This is how the directive is defined:
'use strict';
fcPortalApp.directive('rlIconWidget', ['localizationAssistant', function(localizationAssistant) {
var obj = {
restrict: 'E',
templateUrl: 'scripts/directives/rliconwidget/rliconwidget.html',
//require: 'ngModel',
scope: {
//ngModel: '#',
iconClass: "#",
iconRoute: "#",
iconText: "#",
iconTooltip: "#",
iconImage: "#"
},
link: function(scope, element, attrs) {
console.log(scope);
console.log(scope.iconImage);
console.log(scope.iconTooltip);
console.log(scope.iconRoute);
}
};
console.log(obj);
return obj;
}]);
When I inspect the scope object (click on the output of console.log(scope_ in firebug), it looks like it has iconImage, iconTooltip and iconRoute properties set correctly.
Yet console.log(scope.iconImage), console.log(scope.iconTooltip) and console.log(scope.iconRoute) print "undefined"
Use $observe to observe the value changes of attributes that contain interpolation (e.g. src="{{bar}}"). Not only is this very efficient but it's also the only way to easily get the actual value because during the linking phase the interpolation hasn't been evaluated yet and so the value is at this time set to undefined. -- directive doc
By the time you manually inspect the scope, the values get defined.
The reason we need to use $observe (actually $watch will also work for isolate scope properties defined with '#') is because a directive will likely need to do something whenever the interpolated value changes. E.g., if the value of textForIcon changes, you may want to modify something in the DOM that is managed by your directive.
If you need the values defined when the linking function runs, you have two options:
Use '=' instead of '#'. This will require that you remove the {{}}s from the HTML.
If the values won't change, pass strings: <rl-icon-widget icon-class="vladClass" ...> Then in your directive, simply use attrs.iconClass -- no need for '#'.