Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

ngModel + ngList binding to array does not update view when model changes #1751

Open
danilsomsikov opened this issue Dec 28, 2012 · 6 comments

Comments

@danilsomsikov
Copy link
Contributor

See: http://jsfiddle.net/fjQcu/4/

The reason for that is object reference comparison at input.js:1050

gonzaloruizdevilla pushed a commit to gonzaloruizdevilla/angular.js that referenced this issue Apr 30, 2013
When model value is an array, its values must be stored in a new array
to allow a comparison between old and new values when checking
if a value is out of sync with the model.

Closes angular#1751
gonzaloruizdevilla pushed a commit to gonzaloruizdevilla/angular.js that referenced this issue May 2, 2013
When model changes are detected, a equality comparison with a local copy
of the value ($modelValue).

ngModelController provides the method $setCompareByEquality to change between
equality and reference comparisons.

Closes angular#1751
@btford btford closed this as completed Aug 24, 2013
@btford
Copy link
Contributor

btford commented Aug 24, 2013

As part of our effort to clean out old issues, this issue is being automatically closed since it has been inactivite for over two months.

Please try the newest versions of Angular (1.0.8 and 1.2.0-rc.1), and if the issue persists, comment below so we can discuss it.

Thanks!

@danilsomsikov
Copy link
Contributor Author

The issue hasn't been fixed in either version.
See http://jsfiddle.net/fjQcu/8/ for 1.2.0-rc.1 and http://jsfiddle.net/fjQcu/9/ for 1.0.8

@btford btford reopened this Aug 26, 2013
gonzaloruizdevilla pushed a commit to gonzaloruizdevilla/angular.js that referenced this issue Sep 4, 2013
…he former array data

related to angular#1751
When watching arrays, $watchCollection returned the new data both in the newCollection
and the oldCollection arguments of the listener
gonzaloruizdevilla pushed a commit to gonzaloruizdevilla/angular.js that referenced this issue Sep 4, 2013
fixes angular#1751

When the model referenced the same same array and the array values
where changed, the list wasn't updated.
Now watchCollection is used to detect changes in NgModelController.

isEmpty now detects empty arrays

BEHAVIOUR CHANGE
There is a change in the behaviour of ngList when typing a list.
When “a , b” is typed is automatically changed to “a, b”.
gonzaloruizdevilla pushed a commit to gonzaloruizdevilla/angular.js that referenced this issue Jan 7, 2014
…he former array data

related to angular#1751
When watching arrays, $watchCollection returned the new data both in the newCollection
and the oldCollection arguments of the listener
gonzaloruizdevilla pushed a commit to gonzaloruizdevilla/angular.js that referenced this issue Jan 8, 2014
fixes angular#1751

When the model referenced the same same array and the array values
where changed, the list wasn't updated.
Now watchCollection is used to detect changes in NgModelController.

Changes in input.js breaked tests:
— select select-multiple should require
— select ngOptions ngRequired should treat an empty array as invalid when `multiple` attribute used

Changes in select.js fixed them again: changes in the collections should trigger the formatters and render again.

BEHAVIOUR CHANGE
There is a change in the behaviour of ngList when typing a list.
When “a , b” is typed is automatically changed to “a, b”.
@lordfriend
Copy link

I also encounter this issue when using ng-model in my custom directive. It seems ngModelController using reference comparison leads the $render callback never being called when array is just an old reference but its content changes. angular v1.2.23

@jamie-pate
Copy link

Workaround:

make a new directive, something like:

function ngeModelCollection() {
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            $scope.$watchCollection(attr.ngModel, function ngModelWatch(value, old) {
            if (!Array.isArray(value) || old !== value) {
                return;
            }
    ///copypasta from ngModelWatch()
      var formatters = ctrl.$formatters,
          idx = formatters.length;

      ctrl.$modelValue = value;
      while(idx--) {
        value = formatters[idx](value);
      }

      if (ctrl.$viewValue !== value) {
        ctrl.$viewValue = value;
        ctrl.$render();
      }
    ////endcopypasta

        }//end link function
    }
}

(untested, may need tweaking)

@kapace
Copy link
Contributor

kapace commented Dec 11, 2014

👍 Just formatting Jamie's workaround

function ngeModelCollection() {
    return {
        require: 'ngModel',
        link: function(scope, elem, attr, ctrl) {
            $scope.$watchCollection(attr.ngModel, function ngModelWatch(value, old) {
                if (!Array.isArray(value) || old === value) {
                    return;
                }

                ///copypasta from ngModelWatch()
                var formatters = ctrl.$formatters,
                    idx = formatters.length;

                ctrl.$modelValue = value;
                while (idx--) {
                    value = formatters[idx](value);
                }

                if (ctrl.$viewValue !== value) {
                    ctrl.$viewValue = value;
                    ctrl.$render();
                }
            });
            ////endcopypasta
        }//end link function
    };
}

@Hotell
Copy link
Contributor

Hotell commented Feb 8, 2015

I've created this workaround via new directive.
It's more performant than @jamie-pate solution , 'cause the $viewValue update is processed only on external $modelValue changes and only one $formatter ngList formatter is run and $modelValue is not reassigned(there is no reason to do that) inside listener function.

  angular.module('myApp')
    .directive('emNgList', emNgList)
    ;

    function emNgList(){

      return {
        restrict: 'A',
        require: 'ngModel',
        priority: 100, // this has to be eq or gt as ngList.priority to get ngList $formatter
        link: postLink
      };

      function postLink(scope, elem, attrs, ctrl){

          var ngListFormatter = getNgListFormatter(ctrl);

          var killMe = scope.$watchCollection(attrs.ngModel, function(modelValue){

            var newViewValue = ngListFormatter(modelValue);

            if(newViewValue !== ctrl.$viewValue){

              ctrl.$viewValue = newViewValue;
              ctrl.$render();

            }

          });

        scope.$on('$destroy',function(){ killMe(); });

      }

      function getNgListFormatter(ngModelCtrl){

        var formatters = ngModelCtrl.$formatters;
        var lastIdx = formatters.length - 1;

        return formatters[lastIdx];
      }
    }

showcase:
http://plnkr.co/edit/3ExCYEhmhAp1pL21ll3X?p=preview

@Narretz Narretz self-assigned this Sep 14, 2015
@Narretz Narretz modified the milestones: 1.5.x - migration-facilitation, Backlog Sep 14, 2015
@Narretz Narretz modified the milestones: 1.5.x, Backlog Apr 21, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.