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

Commit 82f45ae

Browse files
committed
fix(Scope): $broadcast and $emit should set event.currentScope to null
When a event is finished propagating through Scope hierarchy the event's `currentScope` property should be reset to `null` to avoid accidental use of this property in asynchronous event handlers. In the previous code, the event's property would contain a reference to the last Scope instance that was visited during the traversal, which is unlikely what the code trying to grab scope reference expects. BREAKING CHANGE: $broadcast and $emit will now reset the `currentScope` property of the event to null once the event finished propagating. If any code depends on asynchronously accessing thei `currentScope` property, it should be migrated to use `targetScope` instead. All of these cases should be considered programming bugs. Closes #7445 Closes #7523
1 parent def5b57 commit 82f45ae

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

src/ng/rootScope.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,8 @@ function $RootScopeProvider(){
971971
*
972972
* - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
973973
* `$broadcast`-ed.
974-
* - `currentScope` - `{Scope}`: the current scope which is handling the event.
974+
* - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
975+
* event propagates through the scope hierarchy, this property is set to null.
975976
* - `name` - `{string}`: name of the event.
976977
* - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
977978
* further event propagation (available only for events that were `$emit`-ed).
@@ -1065,11 +1066,16 @@ function $RootScopeProvider(){
10651066
}
10661067
}
10671068
//if any listener on the current scope stops propagation, prevent bubbling
1068-
if (stopPropagation) return event;
1069+
if (stopPropagation) {
1070+
event.currentScope = null;
1071+
return event;
1072+
}
10691073
//traverse upwards
10701074
scope = scope.$parent;
10711075
} while (scope);
10721076

1077+
event.currentScope = null;
1078+
10731079
return event;
10741080
},
10751081

@@ -1142,6 +1148,7 @@ function $RootScopeProvider(){
11421148
}
11431149
}
11441150

1151+
event.currentScope = null;
11451152
return event;
11461153
}
11471154
};

test/ng/rootScopeSpec.js

+37-3
Original file line numberDiff line numberDiff line change
@@ -1563,15 +1563,30 @@ describe('Scope', function() {
15631563

15641564
describe('event object', function() {
15651565
it('should have methods/properties', function() {
1566-
var event;
1566+
var eventFired = false;
1567+
15671568
child.$on('myEvent', function(e) {
15681569
expect(e.targetScope).toBe(grandChild);
15691570
expect(e.currentScope).toBe(child);
15701571
expect(e.name).toBe('myEvent');
1572+
eventFired = true;
1573+
});
1574+
grandChild.$emit('myEvent');
1575+
expect(eventFired).toBe(true);
1576+
});
1577+
1578+
1579+
it("should have it's `currentScope` property set to null after emit", function() {
1580+
var event;
1581+
1582+
child.$on('myEvent', function(e) {
15711583
event = e;
15721584
});
15731585
grandChild.$emit('myEvent');
1574-
expect(event).toBeDefined();
1586+
1587+
expect(event.currentScope).toBe(null);
1588+
expect(event.targetScope).toBe(grandChild);
1589+
expect(event.name).toBe('myEvent');
15751590
});
15761591

15771592

@@ -1584,6 +1599,7 @@ describe('Scope', function() {
15841599
});
15851600
event = grandChild.$emit('myEvent');
15861601
expect(event.defaultPrevented).toBe(true);
1602+
expect(event.currentScope).toBe(null);
15871603
});
15881604
});
15891605
});
@@ -1698,6 +1714,24 @@ describe('Scope', function() {
16981714

16991715
describe('listener', function() {
17001716
it('should receive event object', inject(function($rootScope) {
1717+
var scope = $rootScope,
1718+
child = scope.$new(),
1719+
eventFired = false;
1720+
1721+
child.$on('fooEvent', function(event) {
1722+
eventFired = true;
1723+
expect(event.name).toBe('fooEvent');
1724+
expect(event.targetScope).toBe(scope);
1725+
expect(event.currentScope).toBe(child);
1726+
});
1727+
scope.$broadcast('fooEvent');
1728+
1729+
expect(eventFired).toBe(true);
1730+
}));
1731+
1732+
1733+
it("should have the event's `currentScope` property set to null after broadcast",
1734+
inject(function($rootScope) {
17011735
var scope = $rootScope,
17021736
child = scope.$new(),
17031737
event;
@@ -1709,7 +1743,7 @@ describe('Scope', function() {
17091743

17101744
expect(event.name).toBe('fooEvent');
17111745
expect(event.targetScope).toBe(scope);
1712-
expect(event.currentScope).toBe(child);
1746+
expect(event.currentScope).toBe(null);
17131747
}));
17141748

17151749

0 commit comments

Comments
 (0)