Problem
I’m trying to retrieve an evaluated attribute from a custom directive, but I’m not sure how to go about it.
To further explain, I’ve made this jsFiddle.
<div ng-controller="MyCtrl">
<input my-directive value="123">
<input my-directive value="{{1+1}}">
</div>
myApp.directive('myDirective', function () {
return function (scope, element, attr) {
element.val("value = "+attr.value);
}
});
What am I overlooking?
Asked by Shlomi Schwartz
Solution #1
Please note that I will continue to update this response when new solutions become available. I also preserve past replies for future reference if they are still relevant. The most recent and best response is given first.
Angularjs directives are extremely strong, but understanding the procedures that go behind them takes time.
Angularjs allows you to build an isolated scope with some ties to the parent scope while creating directives. The attribute to which you attach the element in the DOM, as well as how you define the scope property in the directive specification object, determine these bindings.
You can define three sorts of binding choices in scope and write them as prefixes associated attributes.
angular.module("myApp", []).directive("myDirective", function () {
return {
restrict: "A",
scope: {
text: "@myText",
twoWayBind: "=myTwoWayBind",
oneWayBind: "&myOneWayBind"
}
};
}).controller("myController", function ($scope) {
$scope.foo = {name: "Umur"};
$scope.bar = "qwe";
});
HTML
<div ng-controller="myController">
<div my-directive my-text="hello {{ bar }}" my-two-way-bind="foo" my-one-way-bind="bar">
</div>
</div>
In that situation, we may access these properties in the directive’s scope (whether it’s in a linking function or a controller) as follows:
/* Directive scope */
in: $scope.text
out: "hello qwe"
// this would automatically update the changes of value in digest
// this is always string as dom attributes values are always strings
in: $scope.twoWayBind
out: {name:"Umur"}
// this would automatically update the changes of value in digest
// changes in this will be reflected in parent scope
// in directive's scope
in: $scope.twoWayBind.name = "John"
//in parent scope
in: $scope.foo.name
out: "John"
in: $scope.oneWayBind() // notice the function call, this binding is read only
out: "qwe"
// any changes here will not reflect in parent, as this only a getter .
I’m going to replace this answer with a better one because it was approved but has some flaws. $parse appears to be a service that does not exist in the current scope’s attributes, implying that it only accepts angular expressions and cannot reach scope. , expressions are compiled while angularjs is starting up, which means they are already compiled when we try to access them in our directives postlink method. (In the directive, 1+1 equals 2 already.)
This is how you should utilize it:
var myApp = angular.module('myApp',[]);
myApp.directive('myDirective', function ($parse) {
return function (scope, element, attr) {
element.val("value=" + $parse(attr.myDirective)(scope));
};
});
function MyCtrl($scope) {
$scope.aaa = 3432;
}
.
<div ng-controller="MyCtrl">
<input my-directive="123">
<input my-directive="1+1">
<input my-directive="'1+1'">
<input my-directive="aaa">
</div>
One thing to keep in mind is that if you wish to change the value string, you must wrap it in quotes. (See the third input)
Here’s how to play the fiddle: http://jsfiddle.net/neuTA/6/
Note that using $eval is totally good and the correct method to do it, but $parse has a different behavior, and you generally won’t need to use this in most circumstances.
The solution is to use scope.$eval once more. It not only compiles the angular expression, but it also has access to the attributes of the current scope.
var myApp = angular.module('myApp',[]);
myApp.directive('myDirective', function () {
return function (scope, element, attr) {
element.val("value = "+ scope.$eval(attr.value));
}
});
function MyCtrl($scope) {
}
$eval is what you’re lacking.
Answered by Umur Kontacı
Solution #2
When an attribute value needs to be interpolated in a directive that doesn’t use an isolated scope, for example,
<input my-directive value="{{1+1}}">
utilize the $observe method of Attributes:
myApp.directive('myDirective', function () {
return function (scope, element, attr) {
attr.$observe('value', function(actual_value) {
element.val("value = "+ actual_value);
})
}
});
According to the directive page,
If the attribute value is just a constant, e.g.,
<input my-directive value="123">
If the value is a number or a boolean, and you want the right type, you can use $eval:
return function (scope, element, attr) {
var number = scope.$eval(attr.value);
console.log(number, number + 1);
});
You can directly access the attribute value if it is a string constant or if you want the value to be string type in your directive:
return function (scope, element, attr) {
var str = attr.value;
console.log(str, str + " more");
});
Use $observe in your example because you want to support interpolated values and constants.
Answered by Mark Rajcok
Solution #3
All of the other responses are correct and valuable. However, there are occasions when you just want something simple: a plain old parsed value upon directive instantiation, without the requirement for updates or the need to fool with isolate scope. For example, providing a declarative payload in the form of an array or hash-object in your directive can be useful:
my-directive-name="['string1', 'string2']"
In that situation, you can get down to the point and use a simple angular. $eval(attr.attrName).
element.val("value = "+angular.$eval(attr.value));
Working Fiddle.
Answered by XML
Solution #4
I was seeking for an Angularjs directive with ng-Model to solve the same problem. Here’s the code that fixes the issue.
myApp.directive('zipcodeformatter', function () {
return {
restrict: 'A', // only activate on element attribute
require: '?ngModel', // get a hold of NgModelController
link: function (scope, element, attrs, ngModel) {
scope.$watch(attrs.ngModel, function (v) {
if (v) {
console.log('value changed, new value is: ' + v + ' ' + v.length);
if (v.length > 5) {
var newzip = v.replace("-", '');
var str = newzip.substring(0, 5) + '-' + newzip.substring(5, newzip.length);
element.val(str);
} else {
element.val(v);
}
}
});
}
};
});
HTML DOM
<input maxlength="10" zipcodeformatter onkeypress="return isNumberKey(event)" placeholder="Zipcode" type="text" ng-readonly="!checked" name="zipcode" id="postal_code" class="form-control input-sm" ng-model="patient.shippingZipcode" required ng-required="true">
My Result is:
92108-2223
Answered by Satish Singh
Solution #5
var myApp = angular.module('myApp',[]);
myApp .directive('myDirective', function ($timeout) {
return function (scope, element, attr) {
$timeout(function(){
element.val("value = "+attr.value);
});
}
});
function MyCtrl($scope) {
}
Because the directive is called after the dom has loaded, your modifications will not be applied.
Answered by user1693371
Post is based on https://stackoverflow.com/questions/12371159/how-to-get-evaluated-attributes-inside-a-custom-directive