Search code examples
angularjsfluent

Build fluent service in AngularJS


I have a simple factory in AngularJS:

(function(){
'use strict';

    angular
        .module('myModule', [])
        .factory('myService', service);

    function service(){
        var products= function(p1, p2, p3, ..., pn) {
            var url = "http://something.url/api/action";
            var data = {
                'p1': p1,
                'p2': p2,
                ...
                'pn': pn,
            }

            // return data           
            return $http
                .post(url, data)
                .then(function (response) {
                    return response.data;
                });
        }

        return {
            Products : products
        };
    }
})();

I use this service inside a controller like this:

myInjectedService
    .Products(vm.p1, vm.p1, ... , vm.pn)
    .then(successCallbackFn)
    .catch(failureCallbackFn);

Each parameter (p1, ..., pn) are used to filter the final result. This works like a charm! But with a little drawback: there are to many accepted arguments for Products and is really difficult to know if I'm sending the right parameters and this sounds a little error prone. What I would is a fluent API for service that make everything more human readable, this would be great:

myInjectedService
    .Products()
    .FilterById(p1)
    .WhereCategoryIs(p2)
    ...
    .WhereSomethingElseIs(pn)
    .Send()
    .then(successCallbackFn)
    .catch(failureCallbackFn);

Previously the task of HTTP call was handled by Products call. Right now Products(), only make an empty query (i.e. {}). Each subsequent FilterByX will enrich the query (i.e. {'productId': 'xxx-yyy-1111'}). Calling Send() will make the real HTTP POST call. This call will use the data builded through various filter applied. How can I do that? I'm playing with prototype but without success.


Solution

  • You can archieve what you want by define a new class and use prototype like this.

    In a fluent method, remember to return the object itself.

       function service(){
            var products = function(url) {
              
                // Define a new Product class
                var Products = function() {
                  this.url = url;
                  this.data = {};
                };
                
                // Add the function
                Products.prototype.FilterById = function(id) {
                    this.data.id = id;
                    // To make it fluent, return the object itself
                    return this;
                };
                  
                Products.prototype.FilterByCategory = function(category) {
                    this.data.category = category;
                    return this;
                };
                  
                Products.prototype.send = function() {
                   console.log(this.data);
                };
                  
                // Return an instance of the Products class
                return new Products();
              };
          
            return {
                Products : products
            };
       };
       
     service().Products().FilterById(1).FilterByCategory("Item").send();

    You can read more about it here: https://www.sitepoint.com/javascript-like-boss-understanding-fluent-apis/