aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Scope.js51
-rw-r--r--test/ScopeSpec.js88
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');