Sophie McCarrell
Sophie McCarrell

Reputation: 2871

watch is triggered on controller, but not directive

I have a directive that watches a binding and strangely that watch goes off if the variable is changed right away, but if it waits till the return of an http call, then it won't trigger the watch. The weird thing is that it triggers the controllers watch just fine, but not the directive one, even though they are essentially on the same one.

Here are some code snippets describing the problem. Directive:

.directive('afcAutocomplete', function() {
return {
  restrict: 'AEC',
  scope: {
      binding: '=ngModel',
      source: '=',
      filter: '=?',
      [...]
  },
  templateUrl: '/afc_template/afc-autocomplete.html',
  controller: function($scope, $element, $attrs, $compile, $timeout) {
      [...]
      $scope.$watch('binding', function(newValue) {
          console.log('new val: ', newValue);
          setDisplayBinding(newValue);
      });
[...]

root controller:

app.controller("editController", function($scope, $http, $routeParams) {
  if($routeParams.id)
    qDocument = editFormUtility.load($routeParams.id, $routeParams.document_type); //returns a $http call to an API
  else {
    qDocument = { 
      header: {
        id: guid(),
        schema   : $routeParams.document_type,
        languages: ["en"]
      },  
      government: $scope.userGovernment() ? { identifier: $scope.userGovernment() } : undefined,
    };
[...]

controller:

app.controller("editMeasure", function ($scope) {
  $controller('editController', {$scope: $scope});

  $scope.$watch('document.government', function() {
    if($scope.document)
      console.log('government changed: ', $scope.document.government);
  });
[...]

editMeasure template:

<div afc-autocomplete ng-model="document.government" source="ac_countries" filter="genericFilter"></div>

What's baffling me is that if I start a new document, then the watch works, but if i edit a document, and it goes through the $http call, then the controller watch still works, but the directive watch doesn't work. In terms of the console logs, I see "document: ", then "government changed" as the last message, so no "new val:" message after the "document:" log, which is the indicator that we changed the document. Another note. The document is initially undefined, then becomes an object with 'government' set to another object, something like {id: 'ca'}.

Upvotes: 0

Views: 469

Answers (1)

Sophie McCarrell
Sophie McCarrell

Reputation: 2871

Thanks to Vaidik for helping me find the answer.

The problem is if a watch is created on a binding that is currently undefined, then when you do set it, it won't work. So you need to ensure the binding is set to something, anything.

In my case I set $scope.document = {}, and then the binding worked.

Angular is a weird beast. Can anyone explain in the comments why setting $scope.document = {}, allows my watch for $scope.document.government to suddenly work, even though I end up completely replacing the object that is assigned to $scope.document (I give it a brand new object, I'm not just setting the properties)? I'm confused, because you couldn't possibly do that with regular references, because I'm setting a new reference for $scope.document, so Angular is doing something else magical and I feel the knowledge of what it's doing could help me troubleshoot future issues.

Upvotes: 1

Related Questions