0

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

Community
  • 1
  • 1
Marty Cochrane
  • 679
  • 2
  • 13
  • 26
  • and why you dont creating the factory or service for this calls ? – Narek Mamikonyan Jul 25 '14 at 07:25
  • well thats kind of my point. I would like to create a factory or service to do this for me but not so sure how to do it the "correct way" also if I set up the db connection in the factory will it be a singleton and only connect once or will it continually create connections to firebase? – Marty Cochrane Jul 25 '14 at 07:27
  • yes , let me explain differences between them in answer – Narek Mamikonyan Jul 25 '14 at 07:28

3 Answers3

2

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

Narek Mamikonyan
  • 4,601
  • 2
  • 24
  • 30
  • Thanks for the great detail. Can you give me an example of when you would want to instantiate something and when you wouldn't? – Marty Cochrane Jul 25 '14 at 08:25
  • for example in your case you need singleton object you need it instantiate once, well you should use factory . its answer on your question or not ? – Narek Mamikonyan Jul 25 '14 at 08:42
0

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.

Mohammad Sepahvand
  • 17,364
  • 22
  • 81
  • 122
  • The connection will be created as many times as the 'getMessage' function is called. If you create the $firebase binding out side of the function it will then only be created once. – David East Jul 25 '14 at 13:51
  • @David, I've not used Firebase before so am not sure about the syntax, but my point was basically that, as you said, the code outside the function `var db= new Firebase("https://**.firebaseio.com/");` gets executed only once. – Mohammad Sepahvand Jul 25 '14 at 14:15
  • Yes, but calling "new Firebase" doesn't create a connection. A connection is not established until a read or write is attempted. When the $firebase binding is created it will load all of the data at that location, each and every time it is called. – David East Jul 25 '14 at 14:43
  • Ok. As as I said once, my point was that the OP should move the code responsible for creating the connection to firebase outside the public methods of the service. Do you have any suggestions on how I could edit my answer? – Mohammad Sepahvand Jul 25 '14 at 14:50
  • Yes, move the $firebase(db) binding outside of the returned object. Take a look at this example Plunker: http://plnkr.co/edit/i0fOLy?p=preview – David East Jul 25 '14 at 14:54
  • What is $getMessages? That's not implemented anywhere. – David East Jul 26 '14 at 05:16
  • How about now, if you see anything wrong I'd be grateful if you edit it. – Mohammad Sepahvand Jul 26 '14 at 05:26
  • Why are you wrapping the $firebase binding again? I can edit your answer, but then it wouldn't be your answer anymore. This currently won't even work. You should try to run these type of answers through JSFiddle before proposing them as correct solutions. – David East Jul 27 '14 at 05:58
  • Sorry but I don't know why I bothered to entertain your requests in the first place. this question was never about firebase. I gave an answer to the OP, which was completely right from a technical perspective, and then you come and change the subject matter as if it were question on how to use firebase properly, which it never was. What I told the OP in my answer was to move whatever code he wanted to executed only once to outside of the public function of his service. – Mohammad Sepahvand Jul 27 '14 at 06:05
  • Using Firebase inside of a factory is more complicated than what you would usually do. Firebase constantly pushes out data to the client, so you have to be able to keep this open channel. I'm not saying any of your previous examples wouldn't have worked at a generic level. However, they wouldn't have worked at all with Firebase, which is a requirement from the OP. – David East Jul 27 '14 at 06:08
0

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);

});
Marty Cochrane
  • 679
  • 2
  • 13
  • 26
  • I wouldn't return the $firebase binding in the function. It will cause your data to be loaded every time that method is called. – David East Jul 25 '14 at 14:46
  • but do I not want it to be loaded every time I call that function? If I just store $firebase(db) in a variable outside of the function and then return that variable instead would I get the latest values from firebase? – Marty Cochrane Jul 28 '14 at 10:07
  • The $firebase binding will always stay in sync. By including it outside of your function it just becomes private and only loaded once. It still updates for every change. – David East Jul 28 '14 at 16:10