Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bidirectional Binding not updating within ng-repeat

I'm having trouble with a bi-directional binding in an ng-repeat. I would expect for the below $watch to be triggered when you select a color from the list.

$scope.$watch('favoriteColors', function (newValue) {
    console.log('example-favoriteColors', newValue);
});

I would expect for Orange to appear in $scope.favoriteColors when checked.

Example: http://plnkr.co/edit/k5SEQw4XFnxriD2I8ZG7?p=preview

directive('checkBox', function () {
    return {
        replace: true,
        restrict: 'E',
        //require: '^ngModel',
        scope: {
            'externalValue': '=ngModel',
            'value': '&'
        },
        template: function (el, attrs) {
            var html =
                '<div class="ngCheckBox">'+
                    '<span ng-class="{checked: isChecked}">' +
                        '<input type="checkbox" ng-model="isChecked"/>'+
                    '</span>'+
                '</div>';
            return html;
        },
        controller: ['$scope', '$timeout', function ($scope, $timeout) {
            var initialized = false;
            console.log($scope.value());
            if (angular.isArray($scope.externalValue)) {
                $scope.isChecked = $scope.externalValue.indexOf($scope.value()) > 0;
            } else {
                $scope.isChecked = !!$scope.externalValue;
            }

            $scope.$watch('isChecked', function (newValue) {
                if (angular.isDefined(newValue)) {
                    //add or remove items if this is an array
                    if (angular.isArray($scope.externalValue)) {
                        var index = $scope.externalValue.indexOf($scope.value());
                        if(index > -1) {
                            $scope.externalValue.splice(index, 1);
                        } else if (initialized) {
                            $scope.externalValue.push($scope.value());
                        }
                    } else {
                        //simple boolean value
                        $scope.externalValue = newValue;
                    }
                    if (initialized)
                        console.log($scope.externalValue);
                }
            });

            $timeout(function () {
                initialized = true;
            });
        }],
        link: function (scope, el, attrs) {

        }
    };
});
like image 457
Webnet Avatar asked Dec 06 '25 03:12

Webnet


1 Answers

Please check out this plunk: http://plnkr.co/edit/pbHz4ohBPi7iYq6uJI8X?p=preview

There were lots of changes. Some of them are:

  • The template needs not be a function, since it is static.
  • The initialized (and consequently the $timeout) is not needed.
  • I implemented my own indexOf function; there is a chance the objects are not the same in == sense, but equals in the x.name === y.name sense; (I have some doubts about this though)
  • The add or remove items if this is an array part was wrong; you need to update the array based on the value of isChecked, not based on whether the item already exists in the array (indexOf).
  • Initialize favoriteColors as an array, not as a single object, to be consistent, i.e. $scope.favoriteColors = [$scope.colors[1]];
  • (minor) Added a little more descriptive log when favoriteColors change.
  • Use $watch("favoriteColors", function() {...}, true) to watch for changes inside the array (not the true last argument).
like image 184
Nikos Paraskevopoulos Avatar answered Dec 07 '25 17:12

Nikos Paraskevopoulos



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!