16

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
  }
});
Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
Vinit Chouhan
  • 686
  • 1
  • 10
  • 23
  • 1
    [http://stackoverflow.com/questions/20621028/angularjs-calling-a-controller-function-from-a-service][1] [1]: http://stackoverflow.com/questions/20621028/angularjs-calling-a-controller-function-from-a-service – TechnoCrat Mar 05 '15 at 17:42

3 Answers3

37

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.

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • 1
    @PankajParkar: Its perfect solution for my issue as well. In my case the controllers were not nested and I had to send update from to the other. Its just like Ramban Ilaaj. – Rakesh Burbure Jun 27 '17 at 06:27
2

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
  }
}]);
grumpyhoser
  • 108
  • 3
  • 6
  • Why add the whole Controller? Just subscribe the specific function. Remember that if inside the function the $scope (or vm) is modified, notify the change to the view with [$scope.apply as a wrapper](http://jimhoskins.com/2012/12/17/angularjs-and-apply.html) – William Ardila Dec 30 '16 at 20:37
  • Good points, with a couple of caveats: a) when registering the specific function, be sure to indicate the controller it is bound to - 'SocketService.registerCtrlrFn(this.someFunction.bind(this)', and b) if the service logic already always runs as part of a digest cycle, the $scope.apply wrapper is not necessary. If the service logic is not part of a digest cycle, as in the original question, it is necessary. If it may or may not be within a digest cycle, use a $timeout wrapper instead. – grumpyhoser Jan 03 '17 at 22:35
  • More info on [$scope.apply vs $timeout](http://stackoverflow.com/questions/23070822/angular-scope-apply-vs-timeout-as-a-safe-apply) – grumpyhoser Jan 03 '17 at 22:43
  • 1
    Oops, missed a bracket in my register example - `SocketService.registerCtrlrFn(this.someFunction.bind(this))` – grumpyhoser Jan 03 '17 at 22:51
0

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.

Ian G
  • 498
  • 3
  • 16