2
\$\begingroup\$

I've written 3 factories as a part of a sign up process in AngularJS and Firebase. After completing them, I thought to myself, would any of these fit better as a service instead of a factory? From my research it's a bit fuzzy when it comes to choosing whether to create a service or a factory. If so, why would they fit better as a service? How can I tell when to create a service and when to create a factory?

I've documented the methods to explain what they do, and the ones that lack a documentation explains themselves in the key name.

forumApp.factory('fbRef', ['$firebase', '$firebaseAuth', function($firebase, $firebaseAuth) {

    return {
        getReference: function(path) {
            return path ? new Firebase('https://mydbname.firebaseio.com/' + path) : new Firebase('https://forum-app.firebaseio.com');  
        },
        getSyncedReference: function(path) {
            return $firebase(this.getReference(path));
        },
        getAuthObj: function(path) {
            return $firebaseAuth(this.getReference(path));
        }
    };
}]);

forumApp.factory('validation', [function() {

    /**
     * Validate user input before any database entries
     *
     * @param String $input
     *  Unvalidated input.
     *
     * @return Boolean
     *  True -> Input passed validation.
     *  False -> Input faulty, return error
     */
    return {
        validateSignup: function(user) {

            for (var info in user) {
                if (user[info] === '') {
                    return 'All fields must be filled in';
                }
            }

            if (user.age > 99) {
                return 'Sorry grandma, you are too old for this forum';
            }
            else if (isNaN(user.age)) {
                return 'Age only allows digits';
            }
            else if (user.email === undefined) {
                return 'Invalid email format';
            }
            else if (user.password != user.confirmedPassword) {
                return 'The passwords do not match';
            }
            else if (user.isTermsAccepted === false) {
                return 'You must accept the terms and service agreement';
            }
        }
    }
}]);

forumApp.factory('dbEntry', ['$firebase', 'fbRef', '$location', function($firebase, fbRef, $location) {
    return {
        /**
         * Insert user details into Firebase
         *
         * @param Object(s) user, userData 
         *  User details, user ID
         *
         * @return Boolean
         *  True -> Successful database entry
         *  False -> Unsuccessful database entry
         */
        insertUserDetails: function(user, userData) {

            return fbRef.getSyncedReference(userData.uid).$set({
                firstname: user.firstname,
                lastname: user.lastname,
                age: user.age,
                country: user.country,
                displayName: user.displayName,
                email: user.email
            }).then(function() {
                return true;
            }, function(error) {
                return false;
            });
        },
        /**
         * Create a new user account in Firebase
         *
         * @param Object user
         *  User details
         *
         * @return String
         *  If error -> error message
         *  If successful:
         *    -> store user details
         *    -> change location hash to homepage
         */
        createNewUser: function(user) {
            var self = this;

            return fbRef.getAuthObj().$createUser({
                email: user.email,
                password: user.password
            }).then(function(userData) {

                var isDetailsStored = self.insertUserDetails(user, userData);
                    isDetailsStored ? $location.path('/home') : this.removeUser(user);
            }).catch(function(error) {

                switch (error.code) {
                    case 'EMAIL_TAKEN': 
                        return 'Email already registered';

                    default: 
                        return 'Connection error - try again later';
                }
            });
        },
        /**
         * Remove the newly-created user if
         * details entry failed to force
         * the user to sign up again
         *
         * @param Object user
         *  User details
         *
         * @return Boolean
         *  True -> User removal successful
         *  False -> User removal unsuccessful
         */
        removeUser: function(user) {

            return fbRef.getAuthObj().$removeUser({
                email: user.email,
                password: user.password
            }).then(function() {
                return true;
            }).catch(function(error) {
                return false;
            });
        }
    };
}]);
\$\endgroup\$
3
\$\begingroup\$

I think you may be misinterpreting what a "factory" does in programming.

From Wikipedia

In class-based programming, the factory method pattern is a creational pattern which uses factory methods to deal with the problem of creating objects without specifying the exact class of object that will be created. This is done by creating objects via calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.

Basically, a "factory" class or method replaces this code:

var x = new Foo();

With:

var x = SomeFactory.getInstance(/* args */);

So the code using the x variable doesn't have to know its concrete type.

Your code is making web service calls implementing business logic rather than being a fancy wrapper for new Foo().

From the accepted answer for Difference between a service class and a Helper class at Programmers:

A Service class/interface provides a way of a client to interact with some functionality in the application. This is typically public, with some business meaning. For example, a TicketingService interface might allow you to buyTicket, sellTicket and so on.

Your code has "business meaning" -- it manages users in the system -- and therefore is a service. I would register it with AngularJS accordingly.

How AngularJS Defines (And Confuses) Services and Factories

The frustrating thing about the AngularJS documentation is the code examples in the AngularJS Services Docs keep using myModule.factory(...) to create a service. This creates some confusion on the roles of services and factories.

From the AngularJS docs:

myModule.factory('serviceId', function() {
    // ...
});

Two things are happening here.

  1. A factory function is registered with the framework that will return an object for a dependency named serviceId
  2. The object returned by the factory function is the service object.

Your use of forumApp.factory(...) is the correct way to create services in AngularJS. The end result of forumApp.factory(...) is a factory function that returns your service objects. Yes, you have created 3 factories, but those factories then create services --- and this is the correct pattern.

This StackOverflow question is a good read: angularjs - Service vs provider vs factory?

\$\endgroup\$
  • \$\begingroup\$ Hmm, I see. This was the best explanation I've seen so far on this, will change them accordingly! \$\endgroup\$ – Chrillewoodz Feb 20 '15 at 10:09
  • 1
    \$\begingroup\$ I added some additional info about services and factories in AngularJS. There is a lot of confusion about how they interact. \$\endgroup\$ – Greg Burghardt Feb 20 '15 at 13:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.