2

I have an Angular app with a controller and a service. I call the service from the controller and the service makes a back end call and should call another function in the controller. I was able to call the service from the controller but was not able to call a function in the controller.

Here is the code. The controller:

(function(){
  angular.module("addressModule")

    // The controller for the standardized Address
    .controller('myController', ['$resource','myService', myControllerFunction]);

    myControllerFunction.$inject = ['addressConfig', 'myService', '$resource'];

    function myControllerFunction (addressConfig, myService, $resource, $scope) {
      var vm = this;

      // cannot show what the model is
      vm.model = "some model";
      myService.callGISFromService(vm.model);

      this.callFunctionInController = function(){
        console.log("The controller function is called");
      }
    }
})();

The code for the service is

(function(){

    angular.module('addressModule')
        .service('myService', ['$resource',  myServiceFunction]);

    myServiceFunction.$inject = ['$resource'];

    function myServiceFunction ($resource) {

        this.callGISFromService = function (model) {
            var urlForTheGISCall = "some URL";
            var resource = $resource(urlForTheGISCall);
            console.log("control is in the service");

            resource.get().$promise
            .then(function successCallback(response) {

                this.callFunctionInController();

            }), function errorCallback(response) {
                console.log("the backend call did not work");

            }
        };
    }
})();

I was able to make the back end call and get the data required but not able to call the function in controller.

The error on the console:

TypeError: this.callFunctionInController is not a function
koox00
  • 2,691
  • 2
  • 15
  • 25
kasey
  • 185
  • 1
  • 12
  • 2
    doesn't the `$inject` array have to be in the same order as the parameters in your controller? You have the ordering mixed up. – Daniel Lizik Mar 14 '16 at 16:08
  • 1
    you are also injecting twice, one with the bracket syntax and one with the $inject – koox00 Mar 14 '16 at 16:17
  • @Daniel_L I have changed the order, but did not make any difference. – kasey Mar 14 '16 at 16:33
  • @koox00 the application is not working, if I do not inject them twice. – kasey Mar 14 '16 at 16:35
  • 1
    remove the array notation in the controller and just inject with the inject service also inject $scope also and mind the order. The application should play even without inject at all if you are not using minification. – koox00 Mar 14 '16 at 16:44
  • @koox00 Yeah it worked, my bad, sorry. – kasey Mar 14 '16 at 16:47

2 Answers2

3

You might want to take a look at this post, it explains how to use a $broadcast, to establish communication between service - controller.

You could also transform the function in your service into a asynchronously function, so the controller knows when the function has completed successfully or with errors. It would look like this:

(function(){

angular.module('addressModule')

.service('myService', ['$resource', '$q',  myServiceFunction]);

        myServiceFunction.$inject = ['$resource', '$q'];

        function myServiceFunction ($resource) {

        this.callGISFromService = function (model) {

            var deferred = $q.defer();

            var urlForTheGISCall = "some URL";

            var resource = $resource(urlForTheGISCall);
            console.log("control is in the service");

            resource.get().$promise
            .then(function successCallback(response) {

                deferred.resolve('success');

            }), function errorCallback(response) {
                deferred.reject('error');
            }

              return deferred.promise;
        };
    }
})();

This makes it that you can call your function in the controller like this:

(function(){
    angular.module("addressModule")
    // The controller for the standardized Address

        .controller('myController', ['$resource','myService', myControllerFunction]);

        myControllerFunction.$inject = ['addressConfig', 'myService', '$resource'];

        function myControllerFunction (addressConfig, myService, $resource, $scope) {

            var vm = this;
            // cannot show what the model is
            vm.model = "some model";
            myService.callGISFromService(vm.model).then(function(success){
                function(){
                    console.log("The controller function is called");
            }
            });

}
})();

I personally would opt for the second option, as it is more elegant and lightweight than using broadcast. Having allot of broadcasts in your app could make it really messy and difficult to read. For more info on asynchronously functions, check out the angularjs documentation

Community
  • 1
  • 1
Chris
  • 3,329
  • 1
  • 30
  • 44
  • Using events for communication between classes is a bad practice. There are better patterns like the Observer, Visitor and Strategy patterns. – Martin Mar 14 '16 at 16:36
  • What is $q, Do i have to make any dependencies or inject anything to use this. I got an error : ReferenceError: $q is not defined – kasey Mar 14 '16 at 16:36
  • No it doesn't require any specific dependencies, check out the documentation, it's well explained. If this error persist, post it here, so we can look at it together. – Chris Mar 15 '16 at 07:57
1

This is the JavaScript callback pattern. You can also use the Observer pattern.

We can create a Observable service with a subscribe method. This method will accept a function from the subscribing controller(s).

Observable.$inject = ['$http'];
function Observable($http) {
    this.$http = $http;
    this.subscribers = [];
}

Observable.prototype.subscribe = function(fn) {
    this.subscribers.push(fn);
}

Observable.prototype.getSomething = function() {

    var self = this;
    this.$http.get('/something').then(function() {
        self.subscribers.forEach(function(fn) { fn() })));
}

app.service('observable', Observable);

Then in your controller you can subscribe to the updates:

ObserverController = ['observale'];
function ObserverController(observable) {
    observable.subscribe(this.callback);
}

ObserverController.prototype.callback = function() {
    // this is called from the Observable service
}

app.controller('ObserverController', ObserverController);

In your controller you are explicitly subscribing to get updates from the Observable service. Every time the getSomething method in the service is called, the functions that have previously been passed to the subscribe method will be called.

Typically an Observable will pass an object/value to the Observer.

Martin
  • 15,820
  • 4
  • 47
  • 56