Search code examples
javascriptarraysangularjsangularjs-scopeangular-bootstrap

Angular model not updating when being replaced


This Plunkr demonstrates what I'm having an issue with.

I have an array that's presented both in a dialog and in it's parent page. Modifying that array within the scope of the dialog works just fine, but when I replace the array entirely, the changes are only seen within the scope of the dialog. Why is that?

My code, in case Plunkr eats it:

angular.module('app', ['ui.bootstrap'])
  .controller('demoController', function($modal) {
    var vm = this;

    vm.message = 'Binding Problems';
    vm.list = [1,2,3];

    vm.modal = function() {
      $modal.open({
        controller: 'modalController as vm',
        templateUrl: 'modal.html',
        resolve: {
          list: function() {
            return vm.list;
          }
        }
      });
    };
  })
  .controller('modalController', ['$modalInstance', '$scope', 'list',
    function($modalInstance, $scope, list) {
      var vm = this;

      vm.list = list;
      vm.modalText = 'Modal Text';

      vm.cancel = function() {
        $modalInstance.dismiss();
      };
      vm.clear = function() {
        vm.list = []; // This does not propagate to the parent controller...
      };

      vm.add = function() {
        vm.list.push(4); // ...but this does.
      };

    }
  ]);

UPDATE: Plunkr has been updated Ok, so I understand what's going on now, but not how to fix it in my code. In my actual code, I'm replacing the data with the result of an AJAX call. I suppose I have a few options in this case, like vm.list.length = 0 then push()ing each of the items returned from the AJAX call, but that seems horribly inefficient. How is this typically done?


Solution

  • It is because you are overwriting the array reference value help by modal's scope with a new array instance by doing.

      vm.clear = function() {
        vm.list = []; // This does not propagate to the parent controller...
      };
    

    Instead clear the array so that the modal controller's scope still holds the same reference as that of parent controller:-

     vm.clear = function() {
        vm.list.splice(0, vm.list.length); //or vm.list.length = 0;
      };
    

    Demo


    Update based on your actual issue:-

    If you really want to share the data that need to be refreshed by overwriting, resolve from your parent an object reference that contains the array that will be modified from the modal scope.

    Example:-

    In your parent controller:-

      vm.data = {list : [1,2,3];};
    

    and resolve as:-

       resolve: {
          data: function() {
            return vm.data;
          }
        }
    

    In your modal:-

    Inject data and use it:-

     .controller('modalController', ['$modalInstance', '$scope', 'data',
                           function($modalInstance, $scope, data) {
    

    Plnkr

    This will make sure both the controllers share the same underlying object reference even though its child property reference changes you will see it getting reflected on the other side.

    There is another way you could propagate data from modal to container controller is using modal promise chain.

    Chain though modal promise which is available as a property result

     $modal.open({
        controller: 'modalController as vm',
        templateUrl: 'modal.html',
        resolve: {
          list: function() {
            return vm.list;
          }
        }
      }).result.then(function(list){
        vm.list = list; //<-- refresh new list
      });
    

    When you close the modal use modal close and pass data:-

     vm.cancel = function() {
        $modalInstance.close(vm.list); //<-- Pass data
      };
    

    Plnkr