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

feat($rootScope): adds $onRootScope method #5507

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions src/ng/rootScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,43 @@ function $RootScopeProvider(){
};
},

/**
* @ngdoc function
* @name ng.$rootScope.Scope#$onRootScope
* @methodOf ng.$rootScope.Scope
* @function
*
* @description
* Listens on events of a given type. See {@link ng.$rootScope.Scope#methods_$emit $emit} for
* discussion of event life cycle. This method is similar to the $on method with the only difference
* that it subscribes to events emitted on the $rootScope. This allows to use $rootScope.$emit for
* application wide events rather than $rootScope.$broadcast. For an in detail explanation of why
* to use $rootScope.$emit rather than $rootScope.$broadcast please read this answer on StackOverflow:
* http://stackoverflow.com/questions/11252780/whats-the-correct-way-to-communicate-between-controllers-in-angularjs/19498009#19498009
*
* The event listener function format is: `function(event, args...)`. The `event` object
* passed into the listener has the following attributes:
*
* - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
* `$broadcast`-ed.
* - `currentScope` - `{Scope}`: the current scope which is handling the event.
* - `name` - `{string}`: name of the event.
* - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
* further event propagation (available only for events that were `$emit`-ed).
* - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
* to true.
* - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
*
* @param {string} name Event name to listen on.
* @param {function(event, args...)} listener Function to call when the event is emitted.
* @returns {function()} Returns a deregistration function for this listener.
*/
$onRootScope: function(name, listener) {
var unsubscribe = this.$root.$on(name, listener);
this.$on('$destroy', unsubscribe);

return unsubscribe;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we unsubscribe both listeners? root.$on & this.$on

},

/**
* @ngdoc function
Expand Down
66 changes: 66 additions & 0 deletions test/ng/rootScopeSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,72 @@ describe('Scope', function() {
}));
});

describe('$onRootScope', function() {

it('should add listener for both $emit and $broadcast events that are triggered on $rootScope', inject(function($rootScope) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there isn't really a distinction between $emit and $broadcast events, so this confuses me... I don't think it's terrible to test both, but the name just seems a bit more verbose than it needs to be (it is just my opinion, though)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, you are right. I basically just copied over the tests for $on to make the tests look more or less the same. I'm happy to change it if I should. But it should then be changed for the $on tests as well.

var log = '',
child = $rootScope.$new();

function eventFn() {
log += 'X';
}

child.$onRootScope('abc', eventFn);
expect(log).toEqual('');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you will probably be told to change these to toBe() rather than toEqual() when you're expecting them to have a specific value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. I basically just copied over the tests for $on to make the tests look more or less the same. I'm happy to change it if I should. But it should then be changed for the $on tests as well.


$rootScope.$emit('abc');
expect(log).toEqual('X');

$rootScope.$broadcast('abc');
expect(log).toEqual('XX');
}));


it('should return a function that deregisters the listener', inject(function($rootScope) {
var log = '',
child = $rootScope.$new(),
listenerRemove;

function eventFn() {
log += 'X';
}

listenerRemove = child.$onRootScope('abc', eventFn);
expect(log).toEqual('');
expect(listenerRemove).toBeDefined();

$rootScope.$emit('abc');
$rootScope.$broadcast('abc');
expect(log).toEqual('XX');

log = '';
listenerRemove();
$rootScope.$emit('abc');
$rootScope.$broadcast('abc');
expect(log).toEqual('');
}));

it('should remove listener when local scope gets destroyed', inject(function($rootScope) {
var log = '',
child = $rootScope.$new();

function eventFn() {
log += 'X';
}

child.$onRootScope('abc', eventFn);
expect(log).toEqual('');

$rootScope.$emit('abc');
expect(log).toEqual('X');

child.$destroy();

$rootScope.$emit('abc');
expect(log).toEqual('X');
}));

});

describe('$emit', function() {
var log, child, grandChild, greatGrandChild;
Expand Down