Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get evaluated attributes inside a custom directive

I'm trying to get an evaluated attribute from my custom directive, but I can't find the right way of doing it.

I've created this jsFiddle to elaborate.

<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 missing?

like image 604
Shlomi Schwartz Avatar asked Sep 11 '12 13:09

Shlomi Schwartz


People also ask

Which of the given directives is an attribute selector?

The ngModel directive which is used for two-way is an example of an attribute directive.

Which decorator is used while creating custom attribute directives?

Creating a Custom Attribute Directive To create a custom directive we have to replace @Component decorator with @Directive decorator. So, let's get started with creating our first Custom Attribute directive. In this directive, we are going to highlight the selected DOM element by setting an element's background color.

What are types of directives and give example of attribute directives?

There are three kinds of directives in Angular: Components—directives with a template. Structural directives—change the DOM layout by adding and removing DOM elements. Attribute directives—change the appearance or behavior of an element, component, or another directive.

How do you create an attribute directive?

Steps to create a custom attribute directiveAssign the attribute directive name to the selector metadata of @Directive decorator. Use ElementRef class to access DOM to change host element appearance and behavior. Use @Input() decorator to accept user input in our custom directive.


2 Answers

Notice: I do update this answer as I find better solutions. I also keep the old answers for future reference as long as they remain related. Latest and best answer comes first.

Better answer:

Directives in angularjs are very powerful, but it takes time to comprehend which processes lie behind them.

While creating directives, angularjs allows you to create an isolated scope with some bindings to the parent scope. These bindings are specified by the attribute you attach the element in DOM and how you define scope property in the directive definition object.

There are 3 types of binding options which you can define in scope and you write those as prefixes related attribute.

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 case, in the scope of directive (whether it's in linking function or controller), we can access these properties like this:

/* 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 . 

"Still OK" Answer:

Since this answer got accepted, but has some issues, I'm going to update it to a better one. Apparently, $parse is a service which does not lie in properties of the current scope, which means it only takes angular expressions and cannot reach scope. {{,}} expressions are compiled while angularjs initiating which means when we try to access them in our directives postlink method, they are already compiled. ({{1+1}} is 2 in directive already).

This is how you would want to use:

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 you should notice here is that, if you want set the value string, you should wrap it in quotes. (See 3rd input)

Here is the fiddle to play with: http://jsfiddle.net/neuTA/6/

Old Answer:

I'm not removing this for folks who can be misled like me, note that using $eval is perfectly fine the correct way to do it, but $parse has a different behavior, you probably won't need this to use in most of the cases.

The way to do it is, once again, using scope.$eval. Not only it compiles the angular expression, it has also access to the current scope's properties.

var myApp = angular.module('myApp',[]);  myApp.directive('myDirective', function () {     return function (scope, element, attr) {         element.val("value = "+ scope.$eval(attr.value));     } });  function MyCtrl($scope) {     }​ 

What you are missing was $eval.

http://docs.angularjs.org/api/ng.$rootScope.Scope#$eval

Executes the expression on the current scope returning the result. Any exceptions in the expression are propagated (uncaught). This is useful when evaluating angular expressions.

like image 121
Umur Kontacı Avatar answered Nov 03 '22 09:11

Umur Kontacı


For an attribute value that needs to be interpolated in a directive that is not using an isolated scope, e.g.,

<input my-directive value="{{1+1}}"> 

use Attributes' method $observe:

myApp.directive('myDirective', function () {   return function (scope, element, attr) {     attr.$observe('value', function(actual_value) {       element.val("value = "+ actual_value);     })  } }); 

From the directive page,

observing interpolated attributes: 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.

If the attribute value is just a constant, e.g.,

<input my-directive value="123"> 

you can use $eval if the value is a number or boolean, and you want the correct type:

return function (scope, element, attr) {    var number = scope.$eval(attr.value);    console.log(number, number + 1); }); 

If the attribute value is a string constant, or you want the value to be string type in your directive, you can access it directly:

return function (scope, element, attr) {    var str = attr.value;    console.log(str, str + " more"); }); 

In your case, however, since you want to support interpolated values and constants, use $observe.

like image 25
Mark Rajcok Avatar answered Nov 03 '22 10:11

Mark Rajcok