diff options
| -rw-r--r-- | src/Scope.js | 51 | ||||
| -rw-r--r-- | test/ScopeSpec.js | 88 |
2 files changed, 94 insertions, 45 deletions
diff --git a/src/Scope.js b/src/Scope.js index e037cb06..6bb9a586 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -257,22 +257,29 @@ Scope.prototype = { * - `string`: Evaluated as {@link guide/dev_guide.expressions expression} * - `function(scope, newValue, oldValue)`: called with current `scope` an previous and * current values as parameters. + * @returns {function()} Returns a deregistration function for this listener. */ $watch: function(watchExp, listener) { - var scope = this; - var get = compileToFn(watchExp, 'watch'); - var listenFn = compileToFn(listener || noop, 'listener'); - var array = scope.$$watchers; + var scope = this, + get = compileToFn(watchExp, 'watch'), + listenFn = compileToFn(listener || noop, 'listener'), + array = scope.$$watchers, + watcher = { + fn: listenFn, + last: Number.NaN, // NaN !== NaN. We used this to force $watch to fire on first run. + get: get + }; + if (!array) { array = scope.$$watchers = []; } // we use unshift since we use a while loop in $digest for speed. // the while loop reads in reverse order. - array.unshift({ - fn: listenFn, - last: Number.NaN, // NaN !== NaN. We used this to force $watch to fire on first run. - get: get - }); + array.unshift(watcher); + + return function() { + angularArray.remove(array, watcher); + }; }, /** @@ -538,6 +545,7 @@ Scope.prototype = { * * @param {string} name Event name to listen on. * @param {function(event)} listener Function to call when the event is emitted. + * @returns {function()} Returns a deregistration function for this listener. * * The event listener function format is: `function(event)`. The `event` object passed into the * listener has the following attributes @@ -553,30 +561,13 @@ Scope.prototype = { this.$$listeners[name] = namedListeners = []; } namedListeners.push(listener); - }, - - /** - * @workInProgress - * @ngdoc function - * @name angular.scope.$removeListener - * @function - * - * @description - * Remove the on listener registered by {@link angular.scope.$on $on}. - * - * @param {string} name Event name to remove on. - * @param {function} listener Function to remove. - */ - $removeListener: function(name, listener) { - var namedListeners = this.$$listeners[name], - i; - if (namedListeners) { - i = indexOf(namedListeners, listener); - namedListeners.splice(i, 1); - } + return function() { + angularArray.remove(namedListeners, listener); + }; }, + /** * @workInProgress * @ngdoc function diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js index 4e21537c..492396c5 100644 --- a/test/ScopeSpec.js +++ b/test/ScopeSpec.js @@ -207,6 +207,7 @@ describe('Scope', function() { expect(log).toEqual('abc'); }); + it('should repeat watch cycle from the root elemnt', function() { var log = ''; var child = root.$new(); @@ -269,6 +270,29 @@ describe('Scope', function() { root.$digest(); expect(callCount).toEqual(1); }); + + + it('should return a function that allows listeners to be unregistered', function() { + var root = angular.scope(), + listener = jasmine.createSpy('watch listener'), + listenerRemove; + + listenerRemove = root.$watch('foo', listener); + root.$digest(); //init + expect(listener).toHaveBeenCalled(); + expect(listenerRemove).toBeDefined(); + + listener.reset(); + root.foo = 'bar'; + root.$digest(); //triger + expect(listener).toHaveBeenCalledOnce(); + + listener.reset(); + root.foo = 'baz'; + listenerRemove(); + root.$digest(); //trigger + expect(listener).not.toHaveBeenCalled(); + }); }); @@ -434,6 +458,55 @@ describe('Scope', function() { describe('events', function() { + describe('$on', function() { + + it('should add listener for both $emit and $broadcast events', function() { + var log = '', + root = angular.scope(), + child = root.$new(); + + function eventFn(){ + log += 'X'; + } + + child.$on('abc', eventFn); + expect(log).toEqual(''); + + child.$emit('abc'); + expect(log).toEqual('X'); + + child.$broadcast('abc'); + expect(log).toEqual('XX'); + }); + + + it('should return a function that deregisters the listener', function() { + var log = '', + root = angular.scope(), + child = root.$new(), + listenerRemove; + + function eventFn(){ + log += 'X'; + } + + listenerRemove = child.$on('abc', eventFn); + expect(log).toEqual(''); + expect(listenerRemove).toBeDefined(); + + child.$emit('abc'); + child.$broadcast('abc'); + expect(log).toEqual('XX'); + + log = ''; + listenerRemove(); + child.$emit('abc'); + child.$broadcast('abc'); + expect(log).toEqual(''); + }); + }); + + describe('$emit', function() { var log, child, grandChild, greatGrandChild; @@ -480,21 +553,6 @@ describe('Scope', function() { }); - it('should remove event listener', function() { - function eventFn(){ - log += 'abc;'; - } - - child.$on('abc', eventFn); - child.$emit('abc'); - expect(log).toEqual('abc;'); - log = ''; - child.$removeListener('abc', eventFn); - child.$emit('abc'); - expect(log).toEqual(''); - }); - - it('should forward method arguments', function() { child.$on('abc', function(event, arg1, arg2){ expect(event.name).toBe('abc'); |
