Passing jqLite .html() handle directly as an AngularJS watch listener

776 views Asked by At

I'm trying to pass the jqLite function element.html directly as a listener of a watcher:

angular.module('testApp', []).directive('test', function () {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      scope.$watch('someVariable', element.html); // <-- Passing the function handle as listener
    }
  };
});

However this does not work for some reason, so as a workaround I wrapped the listener in a function:

angular.module('testApp', []).directive('test', function () {
  return {
    restrict: 'A',
    link: function (scope, element, attrs) {
      scope.$watch('someVariable', function (newValue) {
        element.html(newValue);
      });
    }
  };
});

This second example works.

I don't understand why the first example is broken. Any ideas?

EDIT: I forgot to mention, the browser does not give me any errors. It just shows me an empty element.

2

There are 2 answers

1
Evandro Silva On BEST ANSWER

Actually, it's because of angular's injector that automatically changes the this property of a function, consider this:

var test = function(string) {
    return {
        html: function(value) {
            console.log(this);
        }
    }
}

$scope.$watch('my_watch_expression', test('string').html);

when you check the value of this, here is what you get:

enter image description here

As you can see, it will throw an error on the jQuery library:

enter image description here

this doesn't have an empty function, therefore, it will throw a silent exception and will not work as expected.

2
SoluableNonagon On

In the official docs, https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

the second parameter in $watch is the listener

"The listener is called only when the value ..."

logically if the listener is "called" it has to be a function... or maybe I'm wrong here.

when you do:

link: function (scope, element, attrs) {
  scope.$watch('someVariable', element.html); // <-- Passing the function handle as listener
}

it looks at the element passed in the link function and tries to access .html attribute. It may be a function but it returns a string... hence it successfully runs, but doesn't do any logging because the equivalent is would be something like:

scope.$watch('someVariable', "<div> some content </div>"); 

Which won't do anything, but doesn't cause any errors.

And if you wrap it in a function as you did, then you can do something with it.