Intercept the creation of all scopes - angularjs

I want to add a destroy listener to all my scopes so I can do some custom cleanup things. I don't want to add $scope.$on('$destroy') to all my controllers so I found the $provide.decorator method and tried this:
angular.module('app').config(['$provide', function($provide) {
$provide.decorator('$rootScope', ['$delegate', decorateScope]);
function decorateScope(scope) {
var oldNew = scope.$new.bind(scope);
scope.$new = function() {
var newScope = oldNew(arguments);
console.log('Scope created');
newScope.$on('$destroy', function() {
console.log('Scope destroyed');
});
return decorateScope(newScope);
};
return scope;
}
}
]);
this will recursively intercept the creation of all scopes. The console logs the creation and destruction of all scopes. but does not work as before, the scope inheritance chain seems somehow broken.
How could this be solved?

Related

Angularjs: $scope.emit is not working

I am a newbie at angularjs and i am creating a web application to earn experience and practice. The problem i have is that $scope.$emit does not seem to be working, i am looking for ways to contact functions between controllers and so far i have found on the internet that $scope.emit and $scope.on seems to fit for this kind of task, if there is another way, i would like to know, anyways the code are written like this:
loginController.js
(function(angular)
{
var app = angular.module('Organizer');
// inject $scope to the controller in order to try $scope.$emit
app.controller('login', function($http, $scope)
{
// i define the scope like this so i can access inside other functions
var scope = this;
scope.processing = false;
scope.message = null;
scope.submit = function()
{
scope.processing = true;
// store data for post
var post = {
username: scope.username,
password: scope.password
};
// send post data
$http.post('api/default/login', post).
success(function(data, status)
{
// hide processing feedback and show the result
scope.processing = false;
scope.message = data.message;
}).
error(function(data, status)
{
scope.processing = false;
});
};
// Function i use to emit
this.closeDialog = function()
{
$scope.$emit('closeDialog');
};
});
})(angular);
siteController.js
(function(angular)
{
var app = angular.module('Organizer');
app.controller('site', function($mdDialog, $scope)
{
this.menu = ['test1', 'test2'];
this.dialog = function()
{
$mdDialog.show({
templateUrl: 'site/login',
});
};
// this does not seem to be working
$scope.$on('closeDialog', function(event)
{
console.log('close dialog');
});
});
})(angular);
Note: i am using angular material and you can see i am showing a dialog which is a login, the login has its controller (i wanted it to use the same site controller, but i don't know how) and this dialog has a button which calls the function closeDialog() in loginControler and should close the dialog, but for now for testing reasons i am just logging if it's calling the event
The $emit function propagate an event only to the scopes parents.
The $broadcast function propagate an event to the scopes childs.
So what you need depends on how the controllers are use it...
If you want an event to reach all the app you have to use the $rootScope:
$rootScope.$broadcast('myEvent');
Here you have the doc of the scope, include $emit and $broadcast
You could not emit or broadcast in dialog controller because dialog in angular material has isolated scope. Because of that, when you emit or broadcast an event, it does not go anywhere. The $emit and $broadcast only works when you have scope hierarchy. $emit propagate event up the hierarchy and $broadcast propagate event down the hierarchy.

How to access another scope with controller?

I know that you can create binding with a directive but is it possible with a controller? Maybe this is stupidly simple but I couldn't figure out.
For example I would like to click on a text in x scope's view and execute a function in another scope.
function firstCtrl() {
var vm = this;
vm.data = 'First scope stuff!';
}
function secondCtrl() {
var vm = this;
vm.data = 'Second scope stuff!';
}
vm.clicked = function() {
alert('firstScope is clicked!');
};
Here is what I have: http://plnkr.co/edit/CpPKsEGPZWzufB2K1FK3
There are multiple ways how to communicate between controllers.
One way is emitting an event on scope:
function FirstCtrl($scope)
{
$scope.$on('someEvent', function(event, args) {});
// another controller or even directive
}
function SecondCtrl($scope)
{
$scope.$emit('someEvent', args);
}
Another way sharing a service.
Hope it helps. Also check this https://stackoverflow.com/a/9407953/4782034

asynchronous loading controller's code

how do you bootstrap a controller that loaded asynchronously via require.js?
if I have something like that:
$routeProvider.when('/',
{
templateUrl:'view1.html',
controller:'ctrl',
resolve:{
load:function($q){
var dfrd = $q.defer();
require(['view1-script'],function(){
dfrd.resolve();
})
return dfrd.promise;
}
}
})
why angular still won't find the controller? I am resolving the route after it loads the script
check out this plunkr
try calling $controllerProvider.register to create your controller. I would also call $apply() on the $rootScope after resolving the deferred because without it, the view does not seem to appear:
load: function($q, $rootScope){
var dfrd = $q.defer();
require(['view1'],function(){
dfrd.resolve();
$rootScope.$apply();
})
return dfrd.promise;
}
http://plnkr.co/edit/fe2Q3BhxPYnPmeiOORHP
in addition, here is a good post: http://weblogs.asp.net/dwahlin/archive/2013/05/22/dynamically-loading-controllers-and-views-with-angularjs-and-requirejs.aspx
It's been 3 years, but just in case anyone still interested, a few months ago I wrote a post about a similar technique to do it.
The most important part is that second parameter of the method $routeProvider.when(route, ctrl) method can handle promises, so you can simply emulate it:
function controllerFactory(ctrl) {
return {
then: function (done) {
var self = this;
require(['./controller/' + ctrl], function (ctrl) {
self.controller = ctrl;
self.resolve = ctrl.resolve;
self.templateUrl = ctrl.templateUrl;
done();
});
}
};
}
And you can end up writing your route definition like this:
$routeProvider.
when('/some/route', controllerFactory('some/route')).
when('/other/route', controllerFactory('other/route'))

How do i use $on in a service in angular?

i have been able to get controller to use the $on listener
with $scope.$on.
but i don't see any documentation on how to get services to listen for events.
I tried $rootScope.$on, but that only allows one listener. i want listeners in multiple services regardless of whether their parent controllers are in scope or not.
after experimenting a fair bit it turns out that getting events to the service can be done with minimal code.
sample service code follows in case anyone else runs into this.
The sample saves and restores the service model to local storage when it gets the respective broadcasts
app.factory('userService', ['$rootScope', function ($rootScope) {
var service = {
model: {
name: '',
email: ''
},
SaveState: function () {
sessionStorage.userService = angular.toJson(service.model);
},
RestoreState: function () {
service.model = angular.fromJson(sessionStorage.userService);
}
}
$rootScope.$on("savestate", service.SaveState);
$rootScope.$on("restorestate", service.RestoreState);
return service;
}]);
Since $on is a scope method, you could create a scope in your service, then listen for events on it:
app.factory('myService', function($rootScope) {
var scope = $rootScope.$new(); // or $new(true) if you want an isolate scope
scope.$on('testEvent', function() {
console.log('event received');
})
return {}
});
function MyCtrl($scope, myService, $rootScope) {
$rootScope.$broadcast('testEvent');
}
fiddle
However, I would not recommend this approach, since scopes are not normally associated with services.

Decorator for Scope

Is it possible, and if so how, to decorate $scope so all scopes have some extra function / property?
I'm trying to do this:
$provide.decorator('$scope', function($scope)
{
$scope.cakes = true;
return $scope;
});
But it explodes with:
Unknown provider: $scopeProvider from App.
I know I can add properties and functions to the $rootScope and it will prototypically inherit, but I want isolated scopes in directives to also have access to these added things.
I had the same problem.
Just extend $rootScope prototype.
Then isolated scopes will also have this method.
This is my attempt to use lodash debounce function as native scope method:
angular.module('Test', [])
.config(function($provide) {
$provide.decorator('$rootScope', function ($delegate) {
$delegate.__proto__.$$busy = 0;
$delegate.__proto__.$watchDebounce = function (watchExpression, listener, objectEquality){
var _scope = this;
var debouncedListener = _.debounce(function (newValue, oldValue, scope){
listener(newValue, oldValue, scope);
_scope.$$busy = 0;
scope.$digest();
}, 1000);
var wrappedListener = function (newValue, oldValue, scope){
_scope.$$busy = 1;
debouncedListener(newValue, oldValue, scope);
}
return this.$watch(watchExpression, wrappedListener, objectEquality);
}
return $delegate;
})
})
Working example here http://jsfiddle.net/3ncct/
It doesn't seem possible, but I'd say that's the whole point of an isolate scope.
What you can do is access decorated stuff via scope.$root, however for many use cases it will defeat the purpose because the added functions will still only access the $rootScope instead of your isolated one.

Resources