I have been working with the excelent ngStorage plugin for angular.
When setting it up you can declare a $scope-node connected to the localstorage like this:
$scope.$store = $localStorage;
$scope.$store is now accessible in all controllers etc.
I want to remove some stuff from localstorage and access it using broadcast instead.
In my init I performed:
$scope.taskarr = [];
$rootScope.$broadcast('taskarrbroad',$scope.taskarr);
What is required in order to add, remove and $watch this array, none of the mentioned seem to work.
Here, nothing happens
controller('textController', function($scope,$routeParams){
$scope.$watch('taskarrbroad.length', function(){
console.log($scope.taskarr.map(function(task){
return task.content;
}).join('\n'));
})
})
Here I can access $scope.taskarr and update it, but the view isn't updated. $scope.$apply() didn't help either (the timeout is because it's already within a digest.
controller('stateSwitchController', function($scope, $routeParams, $timeout){
$scope.taskarr = $scope.$store[$routeParams.state].taskarr || [];
console.log($scope.taskarr);
$timeout(function() {
$scope.$apply();
})
}).
$broadcast is a way to send events to other parts of your application. When you broadcast an event, someone else has to listen to that even with $on(). Something like:
// Some controller
$rootScope.$broadcast('my-event', eventData);
// Some other controller
$scope.$on('my-event', function() {
console.log('my-event fired!')
});
$watch is something else, it's not an event listener per se, it's a way to attach a function that gets called when that value changes, and that value has to be on the scope. So your watch should look like this:
$scope.$watch('taskarr.length', function(){
});
Since you've named the array taskarr on the scope.
Related
So I have this situation where one controller is emitting the event and the other controller has the listener. Here is the code:
In controller A, I have this method:
$scope.process = function () {
var taskName = 'process';
$scope.$emit('process', taskName);
}
In controller B, I have this:
$rootScope.$on('process', function (event, taskName) {
//Do something here
});
Now whenever I visit other pages on application and comeback to this, the process listener gets created twice. I cannot use controller scope as the event is getting emitted from other controller. How can I destroy listener once it has completed its task? I have also tried $scope.$destroy(). Doesn't really work. What is the correct way of doing this?
I am on Angularjs 1.4.7.
Usually you do it in different way:
$rootScope.$broadcast(...)
...
$scope.$on(...)
Then you do not need to unsubscribe.
If you really need for some reason to subscribe to $rootScope, then:
var deregister = $scope.$on(...);
...
deregister(); // destory that listener
I'm working on a pretty big application in AngularJS and to avoid memory leaks we're implementing the memory release in the $onDestroy method, the problem is that there are variables that become undefined however, ng-change events keep coming from HTML and I have some errors. Is there any way to disconnect all the HTML from the controller? or at least to stop all the events coming from the frontend? I'm working in AngularJS 1.6.
This is an example of how I have defined the components:
function requestListController($uibModal, urlRest, $stateParams, $state, uiGridConstants, $filter, httpService) {
var ctrl = this;
ctrl.$onInit= function() {
// ALL DATA INITIALIZATION
ctrl.requestListGridOptions.data = [];
// GETTING EXTERNAL DATA
httpService.get(url, true)
.then(function(response){
console.log("initRequestList - data.RequestListTO : " , response.RequestListTO);
angular.copy(response.RequestListTO.requests, ctrl.requestListGridOptions.data);
}) .catch(function onError(response) {
// Handle error
var status = response.status;
console.log("initRequestList - error : " + status);
});
};
//////////////////////////////
// //
// on$Destroy method //
// //
//////////////////////////////
ctrl.$onDestroy = function() {
ctrl.status=undefined;
ctrl.requestListGridOptions=undefined;
};
// OTHER METHODS
};
//Inject dependencies
requestListController.$inject = [ '$uibModal', 'urlRest', '$stateParams', '$state', 'uiGridConstants', '$filter', 'httpService'];
pomeApp.component('requestList', {
templateUrl: 'request/requestList/requestList.template.html',
controller: requestListController
});
This is more less the structure of my components.
I guess you misinterpret the onDestroy event. It's mainly to remove timeouts or intervals or events for $rootScope.$on(...).
The ng-change event is bind to the scope. This means it will automatically destroyed if the scope is removed. Therefore the whole scope won't be destroyed and you have another problem.
If you have one big application with one scope or something similar you should use ng-if to remove the parts that should not be shown. This will remove the DOM element and with it all the watchers if the variable for ng-if is false.
Without any proper code from your side no one can really help you and just make some guesses what your problem could be.
You first need to see how many events have been subscribed. Then in the destroy, you can unsubscribe all those events. Sometimes, we also use directives which have to be destroyed. Or there is some logic inside those directives which needs cleanup. Also, if you have subscribed to any events on the root scope, it will live even after a local scope has been destroyed.
In a project I'm working on we've got a variable on the $rootScope called events. I can access this in my controllers using $rootScope.events after injecting it to my controller.
It can take some time before the service sets the events on the $rootScope variable. Now am I adding a new functionality that needs the ID from the first event of the variable. The problem is, it's getting called before $rootScope.events is set. I can't figure out how to call the method in my controller after the $rootscope.events is set. I have used $watch before, how ever, it doesn't seem to work on this variable. The code I tried:
$scope.$watch('$rootScope.events', function() {
if ($rootScope.events.length > 0) {
getDetails(); // function I want to call after $rootscope.events is set
$log.debug($rootScope.events); // debugging line
}
});
I added the $rootScope.events.length > 0 to avoid it's getting in a infinite loop. Not sure if this is necessary. Is there a solution for what I need to add this functionality? Something like this watch? Or have I done something wrong?
I don't think you need more code then I've added to this post as I just inject $scope and $rootScope in my controller, and then $log.debug() should get called with the set variable. Currently it returns an empty variable. If I'm wrong just let me know in the comments.
It's been awhile, but I think you want this:
$rootScope.$watch('events', function() {
if ($rootScope.events.length > 0) {
getDetails(); // function I want to call after $rootscope.events is set
$log.debug($rootScope.events); // debugging line
}
});
events is a value on $rootscope but $rootscope.events is not a value on $scope.
To avoid cluttering $rootscope with watches, however, you should probably use:
$scope.$watch('$root.events', function() {
var events = $scope.$root.events;
if (events.length > 0) {
getDetails(); // function I want to call after events is set
$log.debug(events); // debugging line
}
});
Or simply:
$scope.$watch('$root.events', function(newValue, oldValue) {
if (newValue.length > 0) {
getDetails(); // function I want to call after events is set
$log.debug(newValue); // debugging line
}
});
RootScope documentation
Watch is on a string (scope variable) or a function.
$scope.$watch(function() {
return $rootScope.events;
}, function() {
console.log($rootScope.events);
}, true);
Instead of putting a watch for something that happen once, you can use a promise which your events service will resolve when the events will be ready.
Event Service : ($rootScope & $q injected)
// in constructor :
this.deferred = $q.defer();
$rootScope.eventPromise = deferred.promise;
// in a setup fonction or even within the constructor
setupEvent : function(){
.. doing some stuff ..
.. somewhere in a asynchronous call back :
$rootScope.events =... //setup events
this.deferred.resolve();// or me.deferred using var me=this if some closure trouble
.. somewhere else if it fails ..
this.deferred.reject();
}
Now let's be sure this will run before any controller will be loaded :
angular.run(['EventService', function(EventService){
// if you do everything in the constructor let the angular.run and don't run any code,
// this will make sure your events will start loading before angular will resolve the current routes.
EventService.setupEvent();
}]);
Now let's use it :
$rootScope.eventPromise.then(function(){
$rootScope.events // we're safe here.
});
I have one controller.
app.controller('first',['$scope','scopeService', function ($scope,scopeService){
$scope.initialize = function()
{
scopeService.store('value', $scope);
}
}]);
My second controller is
app.controller('second',['$scope','scopeService', function ($scope,scopeService){
$scope.initialize = function()
{
scopeService.get('value', $scope);
}
}]);
But my second controller is loaded before first so i am getting value as undefined..
You can pass data between the controller in two different ways. One way is to use a service to get and store data. Then both controllers can get the data from the service itself. Services are singleton so if it stores data once in its variable then another controller can get is as well.
Another way is to use Angular events. You can emit an event from your second controller and have the first controller listen for the event.
Example code, emit event:
$scope.$emit('event-name', {data: someDate});
Then receive the event using $rootScope:
$rootScope.$on('event-name', function (event, data) {
//do something with data
});
In your case, you should emit the event when your controller receives the data. Then the first controller listening to this event will get the data as well.
I am playing with Angular and SignalR, I have tried to create a service which will act as a manager.
dashboard.factory('notificationsHub', function ($scope) {
var connection;
var proxy;
var initialize = function () {
connection = $.hubConnection();
proxy = connection.createHubProxy('notification');
proxy.on('numberOfIncidents', function (numOfIncident) {
console.log(numOfIncident);
$scope.$emit('numberOfIncidents', numOfIncident);
});
connection.start()
.done(function() {
console.log('Connected');
})
.fail(function() { console.log('Failed to connect Connected'); });
};
return {
initialize: initialize
};
});
however I get the error Error: Unknown provider: $scopeProvider <- $scope <- notificationsHub.
How can I use pubsub to pass all the notifications to the controllers? jQuery maybe?
$scope does not exist in this context as that's something injected when a controller is created and a new child scope is made. However, $rootScope is available at the time you need.
Also, be aware $emit() goes upward and your controller scopes wont see it. You would either need to switch to $broadcast() so the event goes downwards or inject $rootScope as well to the controllers you want to be able to subscribe to 'numberOfIncidents'
Check out the angular docs and a useful wiki on scopes.
Here is a great example showing how to wrap the proxy in a service and use $rootScope for event pub/sub.
http://sravi-kiran.blogspot.com/2013/09/ABetterWayOfUsingAspNetSignalRWithAngularJs.html
As already noted in johlrich's answer, $scope is not avaliable inside proxy.on. However, just switching to $rootScope will most likely not work. The reason for this is because the event handlers regisrered with proxy.on are called by code outside the angular framework, and thus angular will not detect changes to variables. The same applies to $rootScope.$on event handlers that are triggered by events broadcasted from the SignalR event handlers. See https://docs.angularjs.org/error/$rootScope/inprog for some more details.
Thus you want to call $rootScope.$apply() from the SignalR event handler, either explicitly
proxy.on('numberOfIncidents', function (numOfIncident) {
console.log(numOfIncident);
$scope.$apply(function () {
$rootScope.$emit('numberOfIncidents', numOfIncident);
});
});
or possibly implicitly through $timeout
proxy.on('numberOfIncidents', function (numOfIncident) {
console.log(numOfIncident);
$timeout(function () {
$rootScope.$emit('numberOfIncidents', numOfIncident);
}, 0);
});
I tried to use $apply() after changing value, i tried to use $apply(functuin() {value = 3}), and also i tried to use $emit and $broadcast for changing value and it doesn't help.
But i found solution we need in html after in controller you can use
var scope2 = angular.element("#test").scope();
scope2.point.WarmData.push(result);
$scope.$apply();
P.s. I understand that it is very old question, but may by smb, as i, need this solution.