2

I have a directive that I would like to force render when a change is made to an underlying model programmatically. $scope.$apply is idiomatic. But I am unable to get the directive to render without clicking repeatedly on the UI (which presumably manually forces the digest).

Can someone help me identify how to manually trigger the digest?

Even this.scope.$root.$apply() has no effect.

Grabbing hold of a parent DOM node and getting the associated scope, then calling $apply on that from the console, does work however.

When using this DOM node approach in the code it does not work however. Why might this be?

C1pher
  • 1,933
  • 6
  • 33
  • 52
Ben Aston
  • 53,718
  • 65
  • 205
  • 331

2 Answers2

4

Two things to note:

  1. Scopes inherit from each other prototypically and form a tree. Scopes have a reference to $parent which can be used to traverse.
  2. A digest cycle is something that happens on a scope object. Ie. $scope.$digest(). This runs a digest on the scope, and then recursively does the same for all of the child scopes. $scope.$apply() is like doing $rootScope.$digest(). Since it starts at the root, it calls a digest on every other scope. So $apply might be excessive.

To answer your question, scope.$parent.$digest() will trigger a digest on scope's parent.


But it seems that the triggering of the digest isn't actually your problem. $apply will trigger digests on all scopes, so if that isn't working for you, I don't think your problem is simply that you need a digest to be triggered.

Perhaps you don't need the whole directive to re-render, but just need a part of it to update? If so, consider data binding to some shared service like this.

HTML

<div ng-app='app'>
  <one></one>
  <two></two>
</div>

JS

angular
  .module('app', [])
  .directive('one', function() {
    return {
      restrict: 'E',
      scope: {},
      template: "<input ng-model='shared.val'> {{ shared.val }}",
      controller: function($scope, Shared) {
        $scope.shared = Shared;
      }
    };
  })
  .directive('two', function() {
    return {
      restrict: 'E',
      scope: {},
      template: '<p> {{ shared.val }}</p>',
      controller: function($scope, Shared) {
        $scope.shared = Shared;
      }
    };
  })
  .factory('Shared', function() {
    return {
      val: ''
    };
  });
Adam Zerner
  • 17,797
  • 15
  • 90
  • 156
1

It somewhat depends. It sounds like $parent is what you want to call. scope.$root simply holds a reference to $rootScope (not your parent element scope). Short answer - try the following...

scope.$parent.$apply()

Longer answer - the above assumes your child directive scope inherits from your parent via scope: true. See SO answer How to access parent scope from within a custom directive with own scope in AngularJS? for more detail on the topic and other possible approaches to access the parent scope. Once you get a handle on your parent scope, just call $apply() on it.

I've made an example which modifies the parent scope on a jQuery .click() event. Consider the following and observe the parent behavior...

app.directive('parent', function() {
    return {
        link: function(scope, elem, attrs) {
             scope.value = 'parent'            
        }
    }
});

app.directive('child', function() {
    return {
        scope: true,
        link: function(scope, elem, attrs) {

            scope.value = 'child'

            elem.click(function() {
                scope.$parent.value = 'modded!!' // -- previously 'parent' 
                scope.$parent.$apply();
            })
        }
    }
});

JSFiddle Link - working example

Community
  • 1
  • 1
scniro
  • 16,844
  • 8
  • 62
  • 106