2

Ok what I am trying to do is count the number of characters inside an element, but the element includes binding (eg. {{data.username}}) and I want to get the string length after the binding occurs.

My thinking so far has been to create a attribute directive and just .text().length the element that is passed into the "link" function — see below:

This was my working so far:

<!DOCTYPE html>
<html ng-app="app">
  <head>
    <title>Counting Characters</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.27/angular.min.js"></script>
  </head>
  <body ng-controller="TestCtrl">
    <div count-chars>{{person.firstName}} {{person.lastName}}</div>
    <script>
      var app = angular.module('app', []);

      app.controller('TestCtrl', function ($scope) {
        $scope.person = {
          firstName: "James",
          lastName: "Smith"
        };
      });

      app.directive('countChars', [function(){
        return {
          link: function(scope, elem, attrs) {
            console.log(elem.text());
            console.log(elem.text().length);
          }
        };
      }]);

    </script>
  </body>
</html>

The problem is that this only returns the string before any bindings occurs (via the console.logss at the moment). What I would want to get is James Smith and 11 characters, but what I get is {{person.firstName}} {{person.lastName}} and 40 chars.

Any ideas?

3 Answers 3

2

Simplest thing you can do is to wrap your code into $timeout service, so it will execute on the next digest loop, which means that all interpolation job will have finished by that time:

app.directive('countChars', ['$timeout', function($timeout) {
    return {
        link: function(scope, elem, attrs) {
            $timeout(function() {
                console.log(elem.text());
                console.log(elem.text().length);
            });
        }
    };
}]);

Demo: http://plnkr.co/edit/4vkQoTPmx0gL3hOUpN0n?p=preview

Sign up to request clarification or add additional context in comments.

3 Comments

While this does work for the use-case at hand, it is limited by two assumptions. First, that the values are going to be assigned in the current "thread". It breaks if the values are assigned asynchronously. Second, that the values are assigned only once and then never changed.
@hon2a I know, that's why I said this is simplest this in this case. More generic solution if we need to track updates/changes would use scope.$watch of course like in your example or Mike Chamberlain.
Yeah, I just added the comment to make the answer more comprehensive by including drawbacks.
1

Inside your directive, could you calculate elem.text().length inside a $watch on person.firstName + person.lastName?

1 Comment

Also true, you can use $watch. However it's preffered to avoid additional watches, and this one will have DOM manipulation inside, so it's not very good for performance. But it's possible, true.
1

You cannot get the interpolated string during compilation, as you're only assigning the values later, in the controller. Therefore, you need to watch for the changes:

compile: function (element) {
    var text = $interpolate(element.text());

    return function link ($scope) {
        $scope.$watch(text, function (interpolatedText) {
            // measure and log (or do whatever with) your interpolated text
            // (the code you put here will run whenever the text changes)
        });
    };
}

(I've put retrieval of the original text in compile phase, so that it works even if the properties on scope are already assigned.)

1 Comment

Very nice usage of $interpolate, +1.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.