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

Commit

Permalink
fix(Scope): allow removing a listener during event
Browse files Browse the repository at this point in the history
  • Loading branch information
vojtajina authored and IgorMinar committed Nov 25, 2012
1 parent 682418f commit e6966e0
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 5 deletions.
28 changes: 23 additions & 5 deletions src/ng/rootScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ function $RootScopeProvider(){
namedListeners.push(listener);

return function() {
arrayRemove(namedListeners, listener);
namedListeners[indexOf(namedListeners, listener)] = null;
};
},

Expand Down Expand Up @@ -686,6 +686,14 @@ function $RootScopeProvider(){
namedListeners = scope.$$listeners[name] || empty;
event.currentScope = scope;
for (i=0, length=namedListeners.length; i<length; i++) {

// if listeners were deregistered, defragment the array
if (!namedListeners[i]) {
namedListeners.splice(i, 1);
i--;
length--;
continue;
}
try {
namedListeners[i].apply(null, listenerArgs);
if (stopPropagation) return event;
Expand Down Expand Up @@ -735,19 +743,29 @@ function $RootScopeProvider(){
},
defaultPrevented: false
},
listenerArgs = concat([event], arguments, 1);
listenerArgs = concat([event], arguments, 1),
listeners, i, length;

//down while you can, then up and next sibling or up and next sibling until back at root
do {
current = next;
event.currentScope = current;
forEach(current.$$listeners[name], function(listener) {
listeners = current.$$listeners[name] || [];
for (i=0, length = listeners.length; i<length; i++) {
// if listeners were deregistered, defragment the array
if (!listeners[i]) {
listeners.splice(i, 1);
i--;
length--;
continue;
}

try {
listener.apply(null, listenerArgs);
listeners[i].apply(null, listenerArgs);
} catch(e) {
$exceptionHandler(e);
}
});
}

// Insanity Warning: scope depth-first traversal
// yes, this code is a bit crazy, but it works and we have tests to prove it!
Expand Down
68 changes: 68 additions & 0 deletions test/ng/rootScopeSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,74 @@ describe('Scope', function() {
});


it('should allow removing event listener inside a listener on $emit', function() {
var spy1 = jasmine.createSpy('1st listener');
var spy2 = jasmine.createSpy('2nd listener');
var spy3 = jasmine.createSpy('3rd listener');

var remove1 = child.$on('evt', spy1);
var remove2 = child.$on('evt', spy2);
var remove3 = child.$on('evt', spy3);

spy1.andCallFake(remove1);

expect(child.$$listeners['evt'].length).toBe(3);

// should call all listeners and remove 1st
child.$emit('evt');
expect(spy1).toHaveBeenCalledOnce();
expect(spy2).toHaveBeenCalledOnce();
expect(spy3).toHaveBeenCalledOnce();
expect(child.$$listeners['evt'].length).toBe(3); // cleanup will happen on next $emit

spy1.reset();
spy2.reset();
spy3.reset();

// should call only 2nd because 1st was already removed and 2nd removes 3rd
spy2.andCallFake(remove3);
child.$emit('evt');
expect(spy1).not.toHaveBeenCalled();
expect(spy2).toHaveBeenCalledOnce();
expect(spy3).not.toHaveBeenCalled();
expect(child.$$listeners['evt'].length).toBe(1);
});


it('should allow removing event listener inside a listener on $broadcast', function() {
var spy1 = jasmine.createSpy('1st listener');
var spy2 = jasmine.createSpy('2nd listener');
var spy3 = jasmine.createSpy('3rd listener');

var remove1 = child.$on('evt', spy1);
var remove2 = child.$on('evt', spy2);
var remove3 = child.$on('evt', spy3);

spy1.andCallFake(remove1);

expect(child.$$listeners['evt'].length).toBe(3);

// should call all listeners and remove 1st
child.$broadcast('evt');
expect(spy1).toHaveBeenCalledOnce();
expect(spy2).toHaveBeenCalledOnce();
expect(spy3).toHaveBeenCalledOnce();
expect(child.$$listeners['evt'].length).toBe(3); //cleanup will happen on next $broadcast

spy1.reset();
spy2.reset();
spy3.reset();

// should call only 2nd because 1st was already removed and 2nd removes 3rd
spy2.andCallFake(remove3);
child.$broadcast('evt');
expect(spy1).not.toHaveBeenCalled();
expect(spy2).toHaveBeenCalledOnce();
expect(spy3).not.toHaveBeenCalled();
expect(child.$$listeners['evt'].length).toBe(1);
});


describe('event object', function() {
it('should have methods/properties', function() {
var event;
Expand Down

0 comments on commit e6966e0

Please sign in to comment.