Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ng-bind happend after my directive so I don't have the value

I have a div element with ng-bind directive:

<div ng-bind="sometext"></div>

I have a directive that gets an element, checks its value / text and adds a color to the element according to the content. I am using this directive like that:

<div ng-bind="sometext" my-directive></div>

The problem is that on the time the directive is executing, there is no value or text on the div because ng-bind didn't happened yet.
I am getting the text using element.text().
Any idea how to make the text available inside my directive?

like image 983
Naor Avatar asked Jan 11 '14 20:01

Naor


People also ask

What is applicable to Ng-bind directive?

The ng-bind directive tells AngularJS to replace the content of an HTML element with the value of a given variable, or expression. If the value of the given variable, or expression, changes, the content of the specified HTML element will be changed as well.

What is the difference between ng model and Ng-bind?

ngModel usually use for input tags for bind a variable that we can change variable from controller and html page but ngBind use for display a variable in html page and we can change variable just from controller and html just show variable. Save this answer.

What is the difference between Ng-bind and expression?

firstName expression in the application's scope are also reflected instantly in the DOM by Angular. ng-bind is about twice as fast as {{}} expression bind. ng-bind places a watcher on the passed expression and therefore the ng-bind only applies, when the passed value actually changes.

What is Ng-bind HTML used for?

The ng-bind-html Directive in AngularJS is used to bind the innerHTML of an HTML element to application data and remove dangerous code from the HTML string. $sanitize service is a must for the ng-bind-html directive. It is supported by all HTML elements.


2 Answers

Your directive may be running before ngBind has bound it's value - both your directive and ngBind are priority 0 so either could run first, more on that in a moment- but let's look at the ngBind source code to see the root of the problem:

var ngBindDirective = ngDirective(function(scope, element, attr) {
  element.addClass('ng-binding').data('$binding', attr.ngBind);

  scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
    element.text(value == undefined ? '' : value);
  });

});

We see that ngBind doesn't immediately update the DOM, but rather places a watch on the ngBind attribute. So the element won't be updated until that watch is run on the next $digest cycle (which is why a $timeout works).

So one option is to mimic ngBind and place your own watch on it's attribute- then you'll be updated whenever the ngBind result changes:

angular.module('myApp').directive('myDirective', function() { 
    return {
        priority: 1,
        link: function(scope,element,attrs) {
            scope.$watch(attrs.ngBind, function(newvalue) {
              console.log("element ",element.text());
            });           
        }
    };      
});

You'll note I set priority to 1. You do need to be sure that this directive's watch is placed after the ngBind watch in the watch queue. That will ensure the element has been updated by ngBind first.

By default a directive's link function runs post-link, so as the $compile docs note:

Directives with greater numerical priority are compiled first. Pre-link functions are also run in priority order, but post-link functions are run in reverse order.

Therefore, since ngBind is priority 0, anything over 0 will ensure your directive's watch will come after the ngBind watch..

demo fiddle

like image 169
KayakDave Avatar answered Sep 18 '22 02:09

KayakDave


Edit 2

The other option is to use ng-class or ng-style for changing the color of the text. Then you don't have to create a new directive at all.

Original Answer

I would not depend on the ng-bind directive at all... This seems much cleaner in my opinion.

<div ng-bind="someModel" my-directive="someModel"></div>

And then define your directive as...

angular.module('myApp').directive('myDirective', function() { 
    return {
        link: function(scope, element, attrs) {
            scope.$watch(attrs.myDirective, function(newValue, oldValue) {
              // Your Code here...
            });           
        }
    };      
});

This way you can use your directive even if you don't have an ng-bind on the element (for example, if you use curly braces instead).

<div my-directive="someModel">{{someModel}}</div>

Alternatively you can use attrs.$observe(...) (documentation) instead of scope.$watch(...).

<div ng-bind="someModel" my-directive="{{someModel}}"></div>

and

angular.module('myApp').directive('myDirective', function() { 
    return {
        link: function(scope, element, attrs) {
            attrs.$observe('myDirective', function(interpolatedValue) {
              // Your Code here...
            });           
        }
    };      
});

You can find more information about the differences between scope.$watch(...) and attrs.$observe() here.

Edit

Given that your directive is basically mutating the DOM after the ng-bind directive, why not skip the ng-bind all together?

<div my-directive="{{someModel}}"></div>

and

angular.module('myApp').directive('myDirective', function() { 
    return {
        link: function(scope, element, attrs) {
            attrs.$observe('myDirective', function(interpolatedValue) {
              if (interpolatedValue) {
                // Modify interpolatedValue if necessary...
              }
              element.text(interpolatedValue == undefined ? '' : interpolatedValue);
            });           
        }
    };      
});
like image 41
rtcherry Avatar answered Sep 21 '22 02:09

rtcherry