Inheritance of Custom Services

in Angular 1.x

Pavel Bosin: pavel@bosin.net

How to better implement 

common functionality in multiple AngularJS services

Multiple pages need similar, but different services:

Invoices

Orders

Tickets

Use in Controllers

angular.module('demoApp').controller('InvoiceCtrl',
            ['$scope', 'Invoices', function ($scope, Invoices) {

    Invoices.getFilteredList().then(function (response) {
        // set invoices in the table data source
    });

}]);

Invoices Controller loads filtered list of invoices

Tickets Controller loads filtered list of tickets

Orders Controller loads filtered list of orders

... and each has some specific differences from others

Common service functionality

{
    getObject: function (id) {...},
    getList: function () {...},
    getFilteredList: function () {...},

    getFilters: function () {...},
    getFilter: function () {...},

    getColumns: function () {...},
    getPagination: function () {...}
}

tl;dr

Create a function that returns a simple object with the common methods. Call this function while instantiating a service.

Extend the result with object.create().

Add more specific methods.

Wrap the result as Angular service using factory pattern.

Shall we look at details?

  • Why not just have independent services?
    • Keep your code DRY. 
  • Why not just use composition?
    • Because composition with services as singletons is tricky (we will have an example)
  • Why not use inheritance of Controllers instead?
    • Controllers should be lean; They usually rely on scope hierarchy and not on inheritance

Any alternatives to services inheritance?

Base Service with Dependency


angular.module('demoApp').factory('BaseService', 
            ['Restangular', function (Restangular) {
    var BaseService = {

        init: function (config) {...}

        getObject: function (id) {...},
        getList: function () {...},
        getFilteredList: function () {...},
        // ... see the slide with common methods  
    };

    return BaseService;
}]);

Child Services

angular.module('demoApp').factory('Invoices',
            ['Restangular', 'BaseService', function (Restangular, BaseService) {

    // specific config: column definitions, filters, api url, etc.
    var config = {...},     

    BaseService.init(config);

    var thisService = {
        // parent methods
        getObject: function (id) { 
            return BaseService.getObject(id); 
        },
        getFilteredList: function () { 
            return BaseService.getFilteredList(); 
        },
        
        // additional invoice specific functions
        getSubList: function () {...}
    };    
    return thisService;
}]);

Tickets and Orders services are very similar.

Using composition here, we have to implement parent methods as call-through.

Need to improve:

  • repeated call-through code in child services
  • init data stays in the singleton base service and is broken if more than one child is used at the same time
    • use stateless base service, but a lot of data is passed in for every method call :(
    • make base service non-signleton!

Non-singleton Base Service

angular.module('demoApp').BaseService = function (Restangular) {
    return {
        // initialize this base service with data
        init: function (config) { 
            //... 
        },

        // get configuration data
        getColumns: function () { return config.columns; },
        getFilters: function () { return config.listFilters; },

        // get single object by id; returns a promise
        getObject: function (id) { 
            //... 
        },

        // get collection of objects; returns a promise
        getFilteredList: function (filter) { 
            //... 
        }
    };
};

Simple Base Service Usage

angular.module('demoApp').factory('Invoices',
            ['Restangular', function (Restangular) {

    var config = {...}; // specific data

    var baseService = angular.module('demoApp').BaseService(Restangular);
    var thisService = Object.create(baseService);
    thisService.init(config);

    
    // additional invoice specific functions
    thisService.getSubList = function () {...};

    return thisService;
}]);

Questions?

These slides: bit.ly/1zKvecS

Inheritance of Angular Services

By pbosin

Inheritance of Angular Services

  • 1,048