AngularJS ‘service’ goes from being the red headed step child to the golden child

I have a confession to make, I did not have a single service that was created and registered using service in my massive AngularJS app before migrating to ES6. They were mostly created using factory and where appropriate, created using provider – but not a single one with service. I just preferred how factory felt and looked. Providers, though verbose, were the only way to make a service configurable. Surprisingly, I found that I wasn’t the only one in this conspiracy against services. In fact, I have read multiple blog posts where people have outright dissed services and recommended using factory.

Through my journey migrating this same app to ES6, my feelings to this often neglected style have changed. It has become my go to approach now. Why you say this change of heart? Well, it is because ES6 classes lend themselves very well to services. They plug right in. This is also how I create controllers. I like this idea of being able to follow the same pattern to create controllers and services. I wish I could create directives the same way but unfortunately it needs be a function that returns a directive definition object.

export class Security {
  constructor() {
    this._permissions = ['deleteUsers'];
  }

  get username() {
    return "Don Joe";
  }

  isAdmin() {
    return true;
  }

  checkAccess(permission) {
    return this._permissions.find(perm => perm === permission);
  }
}

This class can be simply plugged into the service registration function:

import {Security} from "security";

var app = angular.module('plunker', [])
            ....
            .service('security', Security)
            ......

With all the news post ng-europe about AngularJS 2.0 and how everything is going to be built around ES6 classes and modules, I am hoping that this approach would leave me in a better place when it is time to migrate over to AngularJS 2.0. I am not going to be naive and expect it to be smooth migration, but I am hoping that these tiny steps today will alleviate some pain in the future. Hopefully my classes will be directly pluggable into whatever the equivivalent or replacement for services will be (if any) or maybe it will be as simple as just adding the appropriate annotations to my existing classes so that they act like controllers or services. Fingers crossed.

Here is a working plunk

Advertisements

7 thoughts on “AngularJS ‘service’ goes from being the red headed step child to the golden child

  1. Nice, I’m on the same heading as you, but I’m stuck at exactly this place…. How to inject stuff here, like for example $http in your service…

    Best I could came up with:

    – ApiService.js
    export default [‘$http’, ($http) => {
    return class ApiService {
    get() {
    return $http…….
    }
    }
    }]

    – app.js
    import ApiService from ‘ApiService’;

    var app = angular.module(‘testApp’, []);
    app.service(‘ApiService’, ApiService);

    Downside: all classes are now scoped, and not exportable/extendable 😦

  2. @mithundaa

    The problem I have with these methods is minification, because you can’t use the [‘value_name’, function(value_name){}] syntax. Any thoughts on that?

  3. Ok, I found something for that as well… Doesn’t look nice, but works:

    export default class CatService extends Animal {
    constructor($rootScope) {
    this.$rootScope = $rootScope;
    }

    say() {
    this.$rootScope.testCat = true;
    return super.say(“Miauw”);
    }
    }
    CatService.$inject = [‘$rootScope’];

    One remaining issue… Factories, I see you like them as well.

    Consider the same Cat, but than as factory. Registering the Cat as a factory, would be something like this:

    import Cat from ‘./factory/cat.factory’;
    var app = angular.module(‘testApp’, []);
    app.factory(‘Cat’, () => {return Cat; });

    Which would result in the DI on the Factory initialization step: app.factory(‘Cat’, () => {return Cat; });

    So to solve that, we can move that part to the class:

    export default [‘$rootScope’, ($rootScope) => {
    return class Cat extends Animal {
    constructor($rootScope) {
    this.$rootScope = $rootScope;
    }

    say() {
    this.$rootScope.testCat = true;
    return super.say(“Miauw”);
    }
    };
    }];

    But than, the Cat is not exportable/extendable…

  4. Error in the last code sample:

    export default [‘$rootScope’, ($rootScope) => {
    return class Cat extends Animal {
    say() {
    $rootScope.testCat = true;
    return super.say(“Miauw”);
    }
    };
    }];

  5. @daanoz Use ng-annotate for minification. You dont have to worry about injecting your dependencies then and you get a cleaner definition too.
    You can definitely return a function and use it while registering a factory but I wanted to build my app around classes. From what I am seeing around 2.0 will be built around classes.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s