0

I need to pass in a value from a parent directive (bound to controller X) to a child directive, update it, then pass it back all the way to the controller X updateItem function.

This causes the parent controller X to receive the updated value, which triggers a new $digest loop, and passes the item down through the chain, and so on. This causes an infinite loop (discussed here).

I need to update the value from the child directive though, so how can I avoid re-triggering the $digest cycle once the controller's $scope.items is updated?

Parent controller X:

$scope.updateItem = function(item) {
  $scope.items = item;
};

Parent directive template: Bound to parent controller

<div>
   <custom-phrases item-to-update="item" update-item="updateItem"></custom-phrases>
</div>

Parent directive js:

angular
  .module('app')
  .directive('itemlist',
    function($rootScope, $state) {
      return {
        restrict: 'EA',
        templateUrl: 'directives/cms/itemlist/itemlist.tpl.html',
        scope: {
        },
        link: function(scope, element) {
            //
        },
        controller: parentControllerX
      };
    });

Child directive js:

angular
  .module('app')
  .directive('customPhrases',
    function($rootScope) {
      return {
        restrict: 'AE',
        scope: {
          itemToUpdate: '=',
          updateItem: '=',
        },
        templateUrl: 'directives/cms/customPhrases/custom_phrases_directive.tpl.html',
        link: function(scope, element) {

          scope.itemToUpdate.attr1 += 1;
          scope.itemToUpdate.attr2 += 1;

          // then pass it back to parent directive
          scope.updateItem(scope.itemToUpdate);
          ...

If I change to {{ item }}:

<div>
   <custom-phrases item-to-update="{{ item }}" update-item="updateItem"></custom-phrases>
</div>

And pass it into the child directive, it comes through as a string and not an object.


EDIT 2:

If I simply update items in the child directive, like so:

   scope.items = {
     test: 'testing 123'
   };

And maintain 2-way binding in the child directive:

items: '=',

scope.items is never updated at the parent directive and controller level.

Community
  • 1
  • 1
user3871
  • 12,432
  • 33
  • 128
  • 268
  • What is supposed to kick off the update? – Yatrix Mar 09 '17 at 05:33
  • The item is passed down through the chain to the child directive on instantiation of the controller and directives. The child directive updates the value and returns to the parent directive and controller – user3871 Mar 09 '17 at 05:38
  • So it only updates once then? – Yatrix Mar 09 '17 at 05:39
  • The issue is... it should only update once... but when I update it in the child directive and pass it back to parent controller X's `$scope.updateItem` and update `$scope.items` with the new item... it triggers the templates to update and a new $digest loop, thereby repeating the process – user3871 Mar 09 '17 at 05:42
  • See my answer. I think you're just making this way too hard. – Yatrix Mar 09 '17 at 05:45
  • Try set scope: false and transclude: false in the child directive so that you will have the same scope(with parent element). Also remove all the isolating scope in the child directive – digit Mar 09 '17 at 05:52
  • I don't get it. You pass `item` to the child directive but want to update `items` in the parent directive? That doesn't make sense to me. I also don't understand why you use `=` instead of `&` to bind the function. – a better oliver Mar 09 '17 at 09:35
  • @zeroflagL yes because I have a save function in the parent scope that I need to pass the updated value to – user3871 Mar 09 '17 at 09:42
  • `item` already IS updated. What's `items` for? – a better oliver Mar 09 '17 at 09:45
  • Its not appearing as updated in the parent scope – user3871 Mar 09 '17 at 10:11

2 Answers2

0

If your child directive taking care to update the value and passed it back parent then you don't need two way binding with =.

You can use one way binding with <

scope: {
      itemToUpdate: '<', // use one way binding
      updateItem: '=',
    }

EDIT :

My bad : You already having two way data binding,you dont need to pass it back from child directive.

scope.updateItem(scope.itemToUpdate);. //not needed

It will automatically available there.

RIYAJ KHAN
  • 15,032
  • 5
  • 31
  • 53
0

I believe your problem is that you're calling the update in your link function. So, you're updating the item, which causes the view to update, which causes a digest cycle and then you're looping forever.

Your approach doesn't make any sense. If your parent controller has the function that's going to update your item, why are you bothering to bind the function at all? Just bind item and let the parent controller handle the updating without trying to "pass it" back and forth. Whatever is bound to the attributes is coming from the parent, so just add it to item directly in the update function as parameters.

You're overcomplicating what is a simple binding solution by trying to call the parent controller's update method from the child instead of just letting the two-way binding do it's magic on item.

Yatrix
  • 13,361
  • 16
  • 48
  • 78
  • Please see my above comment. I started off with simple by updating the `$scope.items` in my child directive, but it's never updated. – user3871 Mar 09 '17 at 05:52