46

I'm trying to convert an Angular HTTP.get function in my controllers.js to a service in services.js.

The examples I have found all have conflicting ways to implement the service and their choice of names is confusing. Furthermore, the actual angular documentation for services uses yet a different syntax than all the examples. I know this is super simple, but please help me out here.

I have app.js, controllers.js, services.js, filters.js.

app.js

angular.module('MyApp', []).
    config(['$routeProvider', function($routeProvider)
        {
            $routeProvider.
                when('/bookslist', {templateUrl: 'partials/bookslist.html', controller:             BooksListCtrl}).
                otherwise({redirectTo: '/bookslist'});
        }
    ]);

controllers.js

function BooksListCtrl($scope,$http) {
    $http.get('books.php?action=query').success(function(data) {
        $scope.books = data;
    });

    $scope.orderProp = 'author';
}
Gianni Di Noia
  • 1,577
  • 10
  • 25
Brandon
  • 562
  • 1
  • 5
  • 6

2 Answers2

121

Think in terms of modules and dependency injection.

So, lets say you have these three files

<script src="controllers.js"></script>
<script src="services.js"></script>
<script src="app.js"></script>

You would need three modules

1. Main App Module

angular.module('MyApp', ['controllers', 'services'])
  .config(['$routeProvider', function($routeProvider){
    $routeProvider
      .when('/bookslist', {
        templateUrl: 'partials/bookslist.html', 
        controller:  "BooksListCtrl"
      })
      .otherwise({redirectTo: '/bookslist'});
  }]);

Notice that the other two modules are injected into the main module, so their components are available to the whole app.

2. Controllers Module

Currently your controller is a global function, you might want to add it into a controllers module

angular.module('controllers',[])
  .controller('BooksListCtrl', ['$scope', 'Books', function($scope, Books){
    Books.get(function(data){
      $scope.books = data;
    });

    $scope.orderProp = 'author';
  }]);

Books is passed to the controller function and is made available by the services module which was injected in the main app module.

3. Services Module

This is where you define your Books service.

angular.module('services', [])
  .factory('Books', ['$http', function($http){
    return{
      get: function(callback){
          $http.get('books.json').success(function(data) {
          // prepare data here
          callback(data);
        });
      }
    };
  }]);

There are multiple ways of registering services.

  • service: is passed a constructor function (we can call it a class) and returns an instance of the class.
  • provider: the most flexible and configurable since it gives you access to functions the injector calls when instantiating the service
  • factory: is passed a function the injector invokes when instantiating the service.

My preference in using the factory function and just have it return an object. In the example above we just return an object with a get function that is passed a success callback. You could change it to pass an error function too.


Edit Answering @yair's request in the comments, here's how you would inject a service into a directive.

4. Directives Module

I follow the usual pattern, first add the js file

<script src="directives.js"></script>

Then define a new module and register stuff, in this case a directive

angular.module('directives',[])
  .directive('dir', ['Books', function(Books){
    return{
      restrict: 'E',
      template: "dir directive, service: {{name}}",
      link:function(scope, element, attrs){
        scope.name = Books.name;
      }
    };
  }]);

Inject the directive module to the main module in app.js.

angular.module('MyApp', ['controllers', 'services', 'directives']) 

You might want to follow a different strategy and inject a module into the directives module

Notice that the syntax inline dependency annotation is the same for almost everything. The directive is injected the same Books service.

Updated Plunker: http://plnkr.co/edit/mveUM6YJNKIQTbIpJHco?p=preview

Gianni Di Noia
  • 1,577
  • 10
  • 25
jaime
  • 41,961
  • 10
  • 82
  • 52
  • Let me start with a HUGE thanks for the very detailed answer/explanation. In this line: .controller('BooksListCtrl', ['$scope', 'Books', function($scope, Books){ Can you explain the parameters? – Brandon Dec 18 '12 at 20:51
  • 3
    The first parameter is the name of your controller and the second is an array. The array contains a list of services including the `Book` service defined in the services module. The last item in the array is the function where those services will be injected. This style is called **inline** injection and is used to make your app work when code is minified. AngularJS uses function parameters to infer what to inject, the second arguments tells Angular's injector explicitly what to inject. – jaime Dec 18 '12 at 21:03
  • @jm- can you please elaborate your answer and show how to inject the service to a `directive`? thanks! – Yair Nevet Dec 19 '12 at 21:48
  • @YairNevet, I just added an example – jaime Dec 20 '12 at 00:09
  • @jm-: This was not was I was actually looking for, but very informative as well. Thanks for providing this great answer. – TheHippo Mar 18 '13 at 01:30
  • 2
    Great answer, this should be in a best practice section on angularjs.org – dynamo Apr 02 '13 at 02:25
3

Here is an implementation of service service instead of above mentioned factory service. Hope it helps.

app.js

angular.module('myApp', ['controllers', 'services'])
 .config(['$routeProvider', function($routeProvider){
    $routeProvider
      .when('/bookslist', {
        templateUrl: 'partials/bookslist.html', 
        controller:  "BooksListCtrl"
      })
      .otherwise({redirectTo: '/bookslist'});
  }]);

service.js

angular.module('services', [])
  .service('books', ['$http', function($http){
      this.getData = function(onSuccess,onError){
          $http.get('books.json').then(
              onSuccess,onError);
      }  
  }]);

controller.js

angular.module('controllers',[])
    .controller('BooksListCtrl', ['$scope', 'books', function($scope, books){
       $scope.submit = function(){

        $scope.books = books.getData($scope.onSuccess,$scope.onError);
       }
           $scope.onSuccess = function(data){
           console.log(data);
           $scope.bookName = data.data.bookname;
           }

           $scope.onError = function(error){
               console.log(error);
           }
  }]);