Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - Calling a controller function from a service

I'm so green at Angular, I'm not even sure I've been structuring a search for this correctly. The whole directive and service terminology is still confusing me some, but that isn't my question.

I've read this excellent article series front to back: http://www.ng-newsletter.com/posts/beginner2expert-how_to_start.html

Which is why I am at this point in my application. And why I know my question relates more to the relationship between services and controllers. Rather than syntax-related.

So here is an overview of the app:

I have one controller. This goes off and gets a bunch of farm data for the user using an AJAX call to a PHP file, and displays it on screen using it's own $scope.

var masterApp = angular.module('masterApp', ['myFilters','commonControls']);

masterApp.controller('MasterCtrl', ['$scope','$http', '$filter', 'commonFarmSelector', 
    function($scope, $http, $filter, commonFarmSelector){

        ...

        $scope.masterCtrl.loadFarmData = function(farmId) {
            var postdata = {
               "farmId":farmId
            };

            $http.post('/service/farmproduction', postdata).success(function (data) {
                // Do stuff with the $scope using data
            }
        }

        $scope.masterCtrl.loadFarms();
}

You will see I am injecting something called "commonControls". This was a module I created to hold controls that will be reused by multiple controllers. In this case, a dropdown field that contains a list of farms the user has access to (also obtained by an AJAX call):

var commonControlsApp = angular.module('commonControls', []);

commonControlsApp.controller('farmSelectorCtrl', ['$scope', '$http',function($scope, $http) {

    $scope.farmSelectorCtrl ={}

    // Change entire farm view when a different farm is selected
    $scope.farmSelectorCtrl.switchUserFarm = function() {
        var farmId = $scope.farmSelectorCtrl.selectedUserFarm;
        $scope.masterCtrl.loadFarms(farmId); // !!! Direct link to masterCtrl
    };

    // Get a list of the user's farms
    $http.post('/service/userfarms').success(function (data) {
        $scope.farmSelectorCtrl.userFarms = data.getFarmsPerUserResult.farmIds;
    });

}]);

This works fine. But as you can see, the farmSelector is directly linked to masterCtrl. And the behavior of that loadFarmData function is specific to that controller. In other words, it will only do things that apply to that page.

The thing is, this farmSelector will be used on other pages. And the precise behavior of a change event will be different for each page. So I am struggling to work out where this behavior should sit. And how it would be called dependent on the controller using the farmSelector.

The article I have linked above suggests this farmSelector should be in a service so it can be reused elsewhere. But I am still confused over how you could give a generic service a specific action to take when an event is triggered.

like image 757
Aaryn Avatar asked Dec 16 '13 21:12

Aaryn


People also ask

Can a service call a controller?

Sure, that's better. Controller functions are (by design) application-specific. Services should be business-focused, and application-agnostic. If you can separate out the functionality of your app into services in an application-agnostic way, you should.

How do you call a function in AngularJS?

You can call your angularJS function from javascript or jquery with the help of angular. element().

What is Controlleras in AngularJS?

In AngularJS, a Controller is defined by a JavaScript constructor function that is used to augment the AngularJS Scope. Controllers can be attached to the DOM in different ways.


1 Answers

I highly recommend a service as well, for the same reason the article suggests. It also has a great answer to your problem.

The technical term for what you want is a calback function. It is, precisely, a specific action to take when an event is triggered, and the Services section of the article provides a good example of how to do this.

Take a look at this section of the Services article (which I've trimmed down to the important parts)

angular.module('myApp.services', [])
  .factory('githubService', ['$http', function($http) {

    var doRequest = function(username) {
      return $http({
        url: 'https://MySuperURL.com/getTheData'
      });
   }

    return {
      events: doRequest
    };

}]);

So we've got a service now, called githubService, which has one method: events (which is really just a different name for doRequest; I kept the rename so that it would match with the article's code.)

Hidden here behind the scenes is the $q API, which is sometimes referred to as the 'promise' API. The function $http returns a 'promise' object, which is really just a way for the code to keep track of what should happen when the 'promise' is done. For example, let's look at this next code (again, modified from the article's version):

app.controller('ServiceController', ['$scope', 'githubService',
function($scope, githubService) {

  // uses the $http service to call the GitHub API
  // and returns the resulting promise
  githubService.events(newUsername)
    .success(function(data, status, headers) {
         // do magic stuff with the result
         // (which is in the data param)
         $scope.events = data.data;
    })
});

}]);

This is where the 'magic' is happening. Look at the call to success(), and you'll see that they are actually passing a function that should be run when the request works. The function still has access to all the variables in the ServiceController due to closure, so it's allowed to use $scope and other variables. However, it's very possible to write a different success() method in every controller by passing a different function each time, which lets multiple controllers take different actions. When the request finishes, it will call every success function that it was given.

You could follow this code example and get a working model, but I also suggest that you take a look at $q in angular, and also take a look at this article about callback functions. You need to understand both to really get what's going on, but the good news is that they are both used quite often in angular, so it will be worth your time.

like image 122
Hylianpuffball Avatar answered Sep 22 '22 14:09

Hylianpuffball