diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ng/rootScope.js | 141 | 
1 files changed, 141 insertions, 0 deletions
| diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js index 48ba038f..1fad7b0e 100644 --- a/src/ng/rootScope.js +++ b/src/ng/rootScope.js @@ -320,6 +320,147 @@ function $RootScopeProvider(){          };        }, + +      /** +       * @ngdoc function +       * @name ng.$rootScope.Scope#$watchCollection +       * @methodOf ng.$rootScope.Scope +       * @function +       * +       * @description +       * Shallow watches the properties of an object and fires whenever any of the properties change +       * (for arrays this implies watching the array items, for object maps this implies watching the properties). +       * If a change is detected the `listener` callback is fired. +       * +       * - The `obj` collection is observed via standard $watch operation and is examined on every call to $digest() to +       *   see if any items have been added, removed, or moved. +       * - The `listener` is called whenever anything within the `obj` has changed. Examples include adding new items +       *   into the object or array, removing and moving items around. +       * +       * +       * # Example +       * <pre> +          $scope.names = ['igor', 'matias', 'misko', 'james']; +          $scope.dataCount = 4; + +          $scope.$watchCollection('names', function(newNames, oldNames) { +            $scope.dataCount = newNames.length; +          }); + +          expect($scope.dataCount).toEqual(4); +          $scope.$digest(); + +          //still at 4 ... no changes +          expect($scope.dataCount).toEqual(4); + +          $scope.names.pop(); +          $scope.$digest(); + +          //now there's been a change +          expect($scope.dataCount).toEqual(3); +       * </pre> +       * +       * +       * @param {string|Function(scope)} obj Evaluated as {@link guide/expression expression}. The expression value +       *    should evaluate to an object or an array which is observed on each +       *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the collection will trigger +       *    a call to the `listener`. +       * +       * @param {function(newCollection, oldCollection, scope)} listener a callback function that is fired with both +       *    the `newCollection` and `oldCollection` as parameters. +       *    The `newCollection` object is the newly modified data obtained from the `obj` expression and the +       *    `oldCollection` object is a copy of the former collection data. +       *    The `scope` refers to the current scope. +       * +       * @returns {function()} Returns a de-registration function for this listener. When the de-registration function is executed +       * then the internal watch operation is terminated. +       */ +      $watchCollection: function(obj, listener) { +        var self = this; +        var oldValue; +        var newValue; +        var changeDetected = 0; +        var objGetter = $parse(obj); +        var internalArray = []; +        var internalObject = {}; +        var oldLength = 0; + +        function $watchCollectionWatch() { +          newValue = objGetter(self); +          var newLength, key; + +          if (!isObject(newValue)) { +            if (oldValue !== newValue) { +              oldValue = newValue; +              changeDetected++; +            } +          } else if (isArray(newValue)) { +            if (oldValue !== internalArray) { +              // we are transitioning from something which was not an array into array. +              oldValue = internalArray; +              oldLength = oldValue.length = 0; +              changeDetected++; +            } + +            newLength = newValue.length; + +            if (oldLength !== newLength) { +              // if lengths do not match we need to trigger change notification +              changeDetected++; +              oldValue.length = oldLength = newLength; +            } +            // copy the items to oldValue and look for changes. +            for (var i = 0; i < newLength; i++) { +              if (oldValue[i] !== newValue[i]) { +                changeDetected++; +                oldValue[i] = newValue[i]; +              } +            } +          } else { +            if (oldValue !== internalObject) { +              // we are transitioning from something which was not an object into object. +              oldValue = internalObject = {}; +              oldLength = 0; +              changeDetected++; +            } +            // copy the items to oldValue and look for changes. +            newLength = 0; +            for (key in newValue) { +              if (newValue.hasOwnProperty(key)) { +                newLength++; +                if (oldValue.hasOwnProperty(key)) { +                  if (oldValue[key] !== newValue[key]) { +                    changeDetected++; +                    oldValue[key] = newValue[key]; +                  } +                } else { +                  oldLength++; +                  oldValue[key] = newValue[key]; +                  changeDetected++; +                } +              } +            } +            if (oldLength > newLength) { +              // we used to have more keys, need to find them and destroy them. +              changeDetected++; +              for(key in oldValue) { +                if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) { +                  oldLength--; +                  delete oldValue[key]; +                } +              } +            } +          } +          return changeDetected; +        } + +        function $watchCollectionAction() { +          listener(newValue, oldValue, self); +        } + +        return this.$watch($watchCollectionWatch, $watchCollectionAction); +      }, +        /**         * @ngdoc function         * @name ng.$rootScope.Scope#$digest | 
