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

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.

Related

Call controller function from service in angularjs

I am using socket.io to enable chat in my app and i am using a service SocketService to perform all the socket stuff. When a message came then i want to trigger a function of a controller from the service SocketService to make some changes in the UI.
So i want to know that how can i access the function of a controller from the service.
Sample Code:
.service('SocketService', function ($http,$rootScope,$q) {
this.connect = function(){
var socket = io();
socket.on('connect',function(){
// Call a function named 'someFunction' in controller 'ChatController'
});
}
});
This is the sample code for service.
Now the code for controller
.controller('ChatController',function('SocketService',$scope){
$scope.someFunction = function(){
// Some Code Here
}
});
You could achieve this by using angular events $broadcast or $emit.
In your case $broadcast would be helpful,
You need to broadcast your event in $rootscope that can be listen by all the child scopes which has $on with same event name.
CODE
.service('SocketService', function($http, $rootScope, $q) {
this.connect = function() {
var socket = io();
socket.on('connect', function() {
// Call a function named 'someFunction' in controller 'ChatController'
$rootScope.$broadcast('eventFired', {
data: 'something'
});
});
}
});
.controller('ChatController', function('SocketService', $scope) {
$scope.someFunction = function() {
// Some Code Here
}
$scope.$on('eventFired', function(event, data) {
$scope.someFunction();
})
});
Hope this could help you, Thanks.
I know this is an old question, but I have another option. I have a personal bias against $broadcast - it just doesn't feel very 'angularish', I prefer making explicit calls in my code.
So instead of broadcasting to the controller and triggering another digest cycle, I prefer to have the controller register itself to the service, as below. Just be careful not to introduce any circular dependencies if the controller makes use of the same service. This works best with the controllerAs syntax, so that the calling service does not need to care about $scope.
Yes, this is more code than $broadcast, but it does give the service total access to the entire controller - all of it's methods and properties.
.service('SocketService', function ($http,$rootScope,$q) {
var _this = this;
this.chatController = null;
this.registerCtrlr = function (ctrlr) {
_this.chatController = ctrlr;
};
this.unRegisterCtrlr = function () {
_this.chatController = null;
};
this.connect = function(){
var socket = io();
socket.on('connect',function(){
// Call chatController.someFunction if chatController exists
if (_this.chatController) {
_this.chatController.someFunction();
}
});
};
});
.controller('ChatController',['SocketService', '$scope', function(SocketService, $scope){
SocketService.registerCtrlr(this);
//-- make sure controller unregisters itself when destroyed - need $scope for this
$scope.$on('$destroy', function () {
SocketService.unRegisterCtrlr();
});
this.someFunction = function(){
// Some Code Here
}
}]);
I realize this post is old but I'd like to give my two cents after dealing with Angular JS for several years. I personally would reconsider this approach. Ideally with AngularJS you'd modify your controller/directive to facilitate transferring data to the view model and ultimately bind an HTML template to what I call "the user friendly" view model. This view model should simply reflect what you want the user to see and when in general. Using this method the moment connect event happens your view model which should be bound to the service's data will reflect changes to the data the moment the data arrives.

Cannot listen for events on Controller $scope

I am facing a race condition. One of the service is changing the view using ui-router and then broadcasting event from $rootscope. The scope of the controller(tied to the changed view) is listening on this event, but whats happening is when the view is changed my controller scope is reinitialized and till the time it is ready the event has already been dispatched.
So the new controller scope never listens for the event.
Please provide some way to handle this situation.
What you can do is, Instead of listening for event in controller, listen for the event in a service. Since, service are bound to have only a single instance, you won't face such issue in service.
And, as for as the controller, you can first check for the presence of a flag in the service, then retrieve the updated value.
Consider this sample code taken from here.
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;
}]);

How to call controller method from service in Angular?

I wonder if I can call controller method from service.
I know that Service is singleton and I can't inject $scope to the service.
In my case I manage Google Maps in service and want to open modal Dialog when user right clicks on Polygon.
As I know, to open/create new instance of dialog, somehow Service must notify controller to do that.
This is a template with controller + method and service: Template
var myApp = angular.module('myApp', []);
function MyCtrl($scope, gridService, $timeout) {
// how to call "foo" method from service?
$scope.foo = function(){
alert('called from service');
};
}
myApp.service('gridService', ['$timeout', function ( $timeout) {
var grid = {
fetching: false,
pristine: true,
pageType: 'Edit'
}
return {
gridSetup: function () {
return grid;
},
setGridSetup: function (newGrid) {
}
}
}]);
Thanks,
The answer is simple: you don't.
The service exists to manipulate data, nothing else. And it really shouldn't care "why" it's doing what it's doing. You ask the service to do something and wait for a response.
Personally I prefer using promises to resolve async operations (i.e. to notify the controller about a state change) since it's heavily supported by many angular services like $http.
But feel free to use callbacks of you wish.
Usually you do not need to call controller from the service - in general the single service could be used by any controller so service shall know nothing about them. In most cases controller calls to the service in react to some user's action and then you need to update view from controller somehow when service did its work (get response from server, etc.). I see following general ways how to do it.
1. Use callbacks.
//controller
$scope.onButtonClick = function() {
$scope.label = "wait, loading...";
function onSuccess(result) {
$scope.label = "done! result is " + result;
}
myService.doSomeWork(param1, param2, onSuccess);
}
//service
doSomeWork: function(param1, param2, onSuccess) {
$.get({...}, onSuccess);
}
So you provide a callback for each action.
2. Subscribe on events
You may use jQuery for events subscribing/triggering
//controller
$(myService).on('update', function() {
$scope.lastUpdateTime = new Date();
});
$scope.onButtonClick = function() {
myService.doUpdate();
}
//service
doUpdate: function() {
$.get({...}, function onOk() {
$(this).trigger('update');
});
}
3. Use promises
A lot of built-in angular services return promise objects, you may use them too:
//controller
myService.doSomething(param1).then(function(result) {
$scope.result = result;
});
//service
doSomething: function(param1) {
return $http.get({...});
}
4. Share some data
An example is $resource service - for example when you call query method it returns empty array-like object that could be safely put to scope and then fills it with values when http request is done.
//controller
$scope.options = myService.options;
$scope.onClick = function() { myService.update() }
//service
options: [],
update: function() {
var self = this;
$http.get({...}).success(function(res) {
self.options.splice(0, self.options.length); //to keep same array
self.options.push.apply(self.options, res.data.options);
});
}
In all these cases services and controllers are separated, services could be used with any controller and you may easily write unit-tests on services that will not break if you change your controller/view part somehow.
A possible solution would be to have a dialog service which you can inject into the grid service. So when the user right clicks on the polygon the handler would call open on the dialog service.
Take a look at the modal service on angular ui as an example.

How to access the value of one controller from another controller in angularjs

I have two controller in an angularApp with following codes
Controller:Devicectr
function Devicectr($scope, $http) {
$scope.Devices = [{ Id: 1, Devicename: "MasterDeviceA" },
{ Id: 1, Devicename: "MasterDeviceB" },
{ Id: 1, Devicename: "MasterDeviceC" }];
$scope.ActiveDevice = $scope.Devices[0] //
};
Controller:PreviewCtr
function Devicectr($scope, $http) {
$scope.ViewDevice=null; // Here i want to read the $scope.ActiveDevice from **Devicectr** Controller
};
I want read the $scope.ActiveDevice value of Devicectr controller from PreviewCtr controller. How i can do it
Usually you use services when you want to share data between controllers.
Like:
yourModule.factory('yourService', function() {
var sharedData = ...
var getData = function() {
return sharedData;
}
return {
getData : getData
}
}
and you then use this in both your controllers:
function Devicectr($scope, $http, yourService) {...}
function Previewctr($scope, $http, yourService) {...}
You could also nest the controllers to include the same scope, but I would say that is a worse idea.
Create a DeviceService which keeps track of the currently selected device. It should expose setActive(device) and getActive() method.
In your PreviewCtr inject the service to determine the active device. You need to watch for the Device assignment or change
If you assign the service to the scope like
$scope.deviceService=deviceService; you can do something like
$scope.$watch(function(){ return $scope.deviceService.getActive();},function(newValue,oldValue) {
//This gets called when the device change
});
You and also use scope $broadcast method (using $rootScope) in the service to raise an event when the Device changes in setDevice(device) function. You can catch that event any where using $scope.$on handler. See documentation here.

How to make sure $http.get in service completes before directive is initialized with data?

How do I make it such that all the data "MyService" needs to retrieve is retrieved before my 'control' directive is created or some function to actually add those items is called? If not, is there some other recommended way involving controllers, etc. Have yet to see a basic example of similar sequence. Note that data may have more values added to it via other functions, in which a method would have to be called explicitly for those new data elements.
Service:
// var app = angular.module...
app.service('MyService', function($http) {
this.data = [];
// Called once to first initalize the data
$http.get('data.json').then(function data(response) {
this.data.push(response);
}
})
Directive (with its own isolate controller? or point to the main app controller)?:
myDirective.directive('control', ['Params', 'MyService', function(Params, MyService) {
// Call on a method "addData(MyService.data) when the data is actually loaded
}]);
I don't think there is any way to stop directive form initiating and wait for a async function call of a service. However, several alternatives here:
1. Use ng-route and set up a resolve with $routeProvider.
API: http://docs.angularjs.org/api/ngRoute.$routeProvider
Basically, you return a promise in a route's resolver, the route's view will wait for that promise to resolve. After resolve, it will load the view and initiate whatever controller or directive within the view.
2. $watch the data property of MyService.
When data changes, do whatever you want
$scope.$watch(function() {
return MyService.data;
}, function(newVal, oldVal) {
// do something
}, true);
3. Trigger a ready event on MyService, listen to MyService in your directive.
This requires you to include some event library such as 'EventEmitter`, and mix it into MyService.
4. $broadcast ready event on $rootScope from MyService, and listen to it in your directive.
app.service('MyService', function($http, $rootScope) {
this.data = [];
var _this = this;
// Called once to first initalize the data
$http.get('data.json').then(function data(response) {
_this.data.push(response);
$rootScope.$broadcast('MyServiceReady');
}
})
myDirective.directive('control', ['Params', 'MyService', function(Params, MyService) {
return function(scope) {
scope.$on('MyServiceReady', function() {
// do something
});
};
}]);

Resources