JavaEar 专注于收集分享传播有价值的技术资料

How to use an angular factory correctly?

I've been doing quite a lot of reading about angular dependency injection and factories vs services etc like in this post here - angular.service vs angular.factory

I'm struggling putting it into practise and wonder if you can give me suggestions on how you would do it.

My current code looks like this

var app = angular.module("martysCoolApp", ['firebase', 'ngRoute'])

function mainController($scope, $firebase) {
   var db = new Firebase("https://**.firebaseio.com/");
   $scope.messages = $firebase(db);

   $scope.addItem = function(error) {
       if (error.keyCode != 13) return;

       $scope.messages.$add({ name: $scope.name, price: $scope.price });

       $scope.name = "";
       $scope.price = "";
   };
}

I decided I wanted to use angular routes and split this basic function up into two different controllers that I would use for my test app. the MainController would just display everything in the firebase db and the AdminController would be able to add messages to it

var app = angular.module("martysCoolApp", ['firebase', 'ngRoute'])

.factory('fireBaseConnectionService', $firebase)
 //code in here to connect to firebase and add messages

.controller('MainController', function(fireBaseConnectionService, $scope, $route, $routeParams, $location) {
    $scope.$route = $route;
    $scope.$location = $location;
    $scope.$routeParams = $routeParams;

    //code here to retrieve everything from firebase db
})
.controller('AdminController', function(fireBaseConnectionService, $scope, $routeParams) {
    $scope.name = "AdminController";
    $scope.params = $routeParams;

    //code here to add a row to the db

})
.config(function($routeProvider, $locationProvider) {
    $routeProvider.when('/', {
        redirectTo: '/menu'
    })
        .when('/menu', {
            path: '/menu',
            templateUrl: 'partials/menu.html',
            controller: 'MainController'
        })
        .when('/admin', {
            templateUrl: 'partials/admin.html',
            controller: 'AdminController'
        })
        .otherwise({
            redirectTo: '/'
        });

$locationProvider.html5Mode(false);

});

My problem is I don't want to have to connect to the firebase db in each controller. I would like to have a factory that handles this for me and has maybe functions within that that I can call from my controllers to view everything in db and to add something to the db

3个回答

    最佳答案
  1. factory()

    As we’ve seen, the factory() method is a quick way to create and configure a service. The factory() function takes two arguments:

    • name (string)

    This argument takes the name of the service we want to register.

    • getFn (function)

    This function runs when Angular creates the service.

    angular.module('myApp')
      .factory('myService', function() {
        return {
         'username': 'auser'
        }
      });
    

    The getFn will be invoked once for the duration of the app lifecycle, as the service is a singleton object. As with other Angular services, when we define our service, getFn can take an array or a function that will take other injectable objects.

    The getFn function can return anything from a primitive value to a function to an object (similar to the value() function).

    angular.module('myApp')
       .factory('githubService', [
        '$http', function($http) {
          return {
            getUserEvents: function(username) {
          // ...
         }
        }
    }]);
    

    service()

    If we want to register an instance of a service using a constructor function, we can use service(), which enables us to register a constructor function for our service object. The service() method takes two arguments:

    • name (string)

    This argument takes the name of the service instance we want to register.

    • constructor (function)

    Here is the constructor function that we’ll call to instantiate the instance. The service() function will instantiate the instance using the new keyword when creating the instance.

    var Person = function($http) {
       this.getName = function() {
        return $http({
         method: 'GET',
         url: '/api/user'
        });
       };
    };
    angular.service('personService', Person);
    

    provider

    These factories are all created through the $provide service, which is responsible for instantiating these providers at run time.

    angular.module('myApp')
        .factory('myService', function() {
          return {
           'username': 'auser'
        }
       })
    // This is equivalent to the
    // above use of factory
    .provider('myService', {
        $get: function() {
        return {
          'username': 'auser'
        }
       }
    });
    

    Why would we ever need to use the .provider() method when we can just use the .factory() method?

    The answer lies in whether we need the ability to externally configure a service returned by the .provider() method using the Angular .config() function. Unlike the other methods of service creation, we can inject a special attribute into the config() method.

    from ng-book

  2. 参考答案2
  3. All you have to do is just move the firebase connection into the service, and inject that service wherever you want . The connection line will execute the first time your app runs, given that you front load the service when your app runs, as you seem to be doing now:

    .factory('fireBaseConnectionService', function($firebase){
      var db = $firebase(new Firebase("https://**.firebaseio.com/"));//creating 
      //the firebase connection this line executes only once when the service is loaded
      return{
              getMessage:function(){
                return  db.whatever;
              }
      }
    })
    

    If you load the service script dynamically, on route where you need it, it will only connect to the database when it reaches that route. The code above will create one connection to the database, as the connection line is executed only once.

  4. 参考答案3
  5. Just for anyone interested with the help of the answers above and this link - Firebase _ AngularJS this is what I ended up doing

    var app = angular.module("martysCoolApp", ['firebase', 'ngRoute'])
    
    .factory('fireBaseConnectionService', ["$firebase", function($firebase) {
        var db = new Firebase("https://***.firebaseio.com/");
        return {
            getMessages: function() {
                return $firebase(db);
            },
            addMessage: function(message) {
                var messages = $firebase(db);
                messages.$add(message);
            }
        }
    }])
    
    .controller('MainController', ["fireBaseConnectionService", "$scope", function (fireBaseConnectionService, $scope, $route, $routeParams, $location) {
        $scope.$route = $route;
        $scope.$location = $location;
        $scope.$routeParams = $routeParams;
    
        $scope.messages = fireBaseConnectionService.getMessages();
    }])
    
    .controller('AdminController', ["fireBaseConnectionService", "$scope",  function(fireBaseConnectionService, $scope, $routeParams) {
        $scope.name = "AdminController";
        $scope.params = $routeParams;
    
        $scope.addItem = function(error) {
            if (error.keyCode != 13) return;
    
            fireBaseConnectionService.addMessage({ name: $scope.name, price: $scope.price });
    
            $scope.name = "";
            $scope.price = "";
        }
    
    }])
    .config(function($routeProvider, $locationProvider) {
        $routeProvider.when('/', {
            redirectTo: '/menu'
        })
            .when('/menu', {
                path: '/menu',
                templateUrl: 'partials/menu.html',
                controller: 'MainController'
            })
            .when('/admin', {
                templateUrl: 'partials/admin.html',
                controller: 'AdminController'
            })
            .otherwise({
                redirectTo: '/'
            });
    
    $locationProvider.html5Mode(false);
    
    });