AngularJS service inheritance issues

I have a base service which looks like this:

.service('BaseImageService', ['$q', 'ApiHandler', 'UploadService', function ($q, api, uploadService) {

    // Get our api path
    var apiPath = 'logos';

    // Creates our logo
    var _createLogo = function (model) {

        // Handle our uploads
        return _handleUploads(model).then(function () {

            // Create our logo
            return api.post(apiPath, model);
        });
    };

    // Edit our logo
    var _editLogo = function (model) {

        // Handle our uploads
        return _handleUploads(model).then(function () {

            // Create our logo
            return api.put(apiPath, model);
        });
    };

    // Handles our files
    var _handleUploads = function (model) {

        // Create a promises array
        var promises = [];

        // Get our file
        var file = model.file,
            old = model.old;

        // If we have a file
        if (file) {

            // Try to upload the file
            promises.push(uploadService.upload(model.file).then(function (response) {

                // Update our model
                model.path = response.path;
                model.thumbnail = response.thumbnail;
            }));

            // If we have an old model
            if (old) {

                // Delete both our files
                promises.push(uploadService.delete(old.path));
                promises.push(uploadService.delete(old.thumbnail));
            }
        }

        // After all promises have completed
        return $q.all(promises);
    };

    // Create our service
    var service = {

        // Update our api path
        updateApiPath: function (path) {

            // Set the api path
            apiPath = path;
        },

        // Gets a list of logos
        list: function (t) {

            if (t) {
                console.log(apiPath);
            }

            // Get our logo
            return api.get(apiPath);
        },

        // Get a single logo
        get: function (id) {

            // Get our logo
            return api.get(apiPath, { id: id });
        },

        // Create our logo
        save: function (model) {

            // If we are editing
            if (model.id) {

                // Edit our logo
                return _editLogo(model);

                // If we are creating
            } else {

                // Create our logo
                return _createLogo(model);
            }
        },

        // Deletes our logo
        delete: function (id) {

            // Delete our logo
            return api.delete(apiPath, { id: id });
        },

        // Prepare for editing
        prepareForEditing: function (model) {

            // Create our old object
            model.old = {
                path: model.path,
                thumbnail: model.thumbnail
            };
        }
    };

    // Return our service
    return service;
}])

and then I have a few services which "inherit" this service, like this:

.service('ImageService', ['BaseImageService', function (baseService) {

    // Get our api path
    var apiPath = 'images';

    // Update the apiPath
    baseService.updateApiPath(apiPath);

    // Return our service
    return baseService;
}])

.service('LogoService', ['BaseImageService', function (baseService) {

    // Get our api path
    var apiPath = 'logos';

    // Update the apiPath
    baseService.updateApiPath(apiPath);

    // Return our service
    return baseService;
}])

.service('PlayerTextService', ['BaseImageService', function (baseService) {

    // Get our api path
    var apiPath = 'playerText';

    // Update the apiPath
    baseService.updateApiPath(apiPath);

    // Return our service
    return baseService;
}])

I thought that this was working fine. But I have this page that calls all 3 services (ImageService, LogoService and PlayerTextService) sequentially. On the first view of the page everything is fine, if I navigate away and then come back the images service actually pulls back thing from the player text service. Now I know this is because of services being singletons but I am not sure how to fix my issue.

Can anyone give me a hand?


I have added a codepen with an attempted solution:

http://codepen.io/r3plica/pen/ONVBJO


Attempt 2

http://codepen.io/r3plica/pen/jqPeMQ?editors=1010


The solution you tried doesn't work because the BaseService is a singleton. So you inject exactly the same instance into all three service registration functions and all of them configure the same object. So basically the last one wins.

Looks like you want to have separate services with different configurations. This is what providers are used for. They allow a two-step process of building a service instance. Please see this great Stackoverflow answer on the topic:

AngularJS: Service vs provider vs factory

For reference, Restangular is a library that needs to achieve exactly the same as you want. You could use this as a blueprint and see how Restangular handles this requirement:

https://github.com/mgonto/restangular#how-to-create-a-restangular-service-with-a-different-configuration-from-the-global-one

Please be aware that these concepts are based on AngularJS 1 and you need to handle this differently when you want to use AngularJS 2 later on.


After a lot of messing around; I finally found a solution adapting this bit of code

My base service looks like this:

.factory('BaseImageService', ['$q', 'ApiHandler', 'UploadService', 'vectorExtensions', function ($q, api, uploadService, vectorExtensions) {

    // Creates our logo
    var _createLogo = function (model) {

        // Handle our uploads
        return _handleUploads(model).then(function () {

            // Create our logo
            return api.post(BaseImageService.apiPath, model);
        });
    };

    // Edit our logo
    var _editLogo = function (model) {

        // Handle our uploads
        return _handleUploads(model).then(function () {

            // Create our logo
            return api.put(BaseImageService.apiPath, model);
        });
    };

    // Handles our files
    var _handleUploads = function (model) {

        // Create a promises array
        var promises = [];

        // Get our file
        var file = model.file,
            old = model.old;

        // If we have a file
        if (file) {

            // Try to upload the file
            promises.push(uploadService.upload(model.file).then(function (response) {

                // Update our model
                model.path = response.path;
                model.thumbnail = response.thumbnail;
                model.fileName = response.fileName;
            }));

            // If we have an old model
            if (old) {

                // Delete both our files
                promises.push(uploadService.delete(old.path));
                promises.push(uploadService.delete(old.thumbnail));
            }
        }

        // After all promises have completed
        return $q.all(promises);
    };

    // Addes a property to the image array to state if they are vector images or not
    var _addVectorProperties = function (images) {

        // Loop through our images
        for (var i = 0; i < images.length; i++) {

            // Get our current image
            var image = _addVectorProperty(images[i]);
        }

        // Return our images
        return images;
    };

    // Adds a property to the image to state if it is vector or not
    var _addVectorProperty = function (image) {

        // Vector flag
        var vector = false;

        // Get our file extension
        var parts = image.path.split('.');

        // If we have any parts
        if (parts.length) {

            // Get our last part
            var ext = parts[parts.length - 1],
                index = vectorExtensions.indexOf(ext);

            // If our extension exists in our vector array
            if (index > -1) {

                // Change our vector property
                vector = true;
            }
        }

        // Update our image with the new property
        image.vector = vector;

        // Return our image
        return image;
    };

    // Create our service
    var BaseImageService = function (path) {

        // Set our apiPath
        this.apiPath = path;

        // Update our api path
        this.updateApiPath = function (path) {

            // Set the api path
            apiPath = path;
        };

        // Gets a list of logos
        this.list = function () {

            // Get our logo
            return api.get(this.apiPath).then(_addVectorProperties);
        };

        // Get a single logo
        this.get = function (id) {

            // Get our logo
            return api.get(this.apiPath, { id: id }).then(_addVectorProperty);
        };

        // Create our logo
        this.save = function (model) {

            // If we are editing
            if (model.id) {

                // Edit our logo
                return _editLogo(model);

                // If we are creating
            } else {

                // Create our logo
                return _createLogo(model);
            }
        };

        // Deletes our logo
        this.delete = function (id) {

            // Delete our logo
            return api.delete(this.apiPath, { id: id });
        };

        // Set our active image
        this.setActive = function (images, image) {

            // Loop through our images
            for (var i = 0; i < images.length; i++) {

                // Get our current image
                var current = images[i];

                // Set whether we are active or not
                current.active = image.id === current.id ? true : false;
            }
        };

        // Prepare for editing
        this.prepareForEditing = function (model) {

            // Create our old object
            model.old = {
                path: model.path,
                thumbnail: model.thumbnail
            };
        };
    };

    // Return our service
    return BaseImageService;
}])

and the child services look like this:

.service('ImageService', ['BaseImageService', function (baseService) {

    // Create our base service
    var child = new baseService('images');

    // Return our new service
    return child;
}])

.service('LogoService', ['BaseImageService', function (baseService) {

    // Create our base service
    var child = new baseService('logos');

    // Return our new service
    return child;
}])

.service('PlayerTextService', ['BaseImageService', function (baseService) {

    // Create our base service
    var child = new baseService('playerText');

    // Return our new service
    return child;
}])

That works fine.


Yes it append because it's singleton. You have to perform inheritance if you want to do this.

Here is a code that i use and append to angular :

angular.baseResourceServiceMaker = function(service){
    return ['$injector', '$resource', 'TypeService', '$http', '_', 'BackEndpoint', 
        function($injector, $resource,TypeService, $http, _, BackEndpoint){

    //skipping not interesting code 
    // sample fields to inherits
    this.sample = "test";
    this.sampleFN = function(){[...]}
    // THE line that does the magic
    $injector.invoke(service, this);
}

Now time of usage

.service('MyService',angular.baseResourceServiceMaker(['$http',  function($http){
  // overriding fields
  this.sample="inherits";
 this.sampleFN = function(){[...]}
}]));

So basically what do we have here ? A function baseResourceServiceMaker which represent a generic particular service. The $injector that call the service we want to instantiate and set the scope to the generic service, so the this on the child class will bind to the same reference than the generic class. The generic service will be instantiated as many times you call it, no confict.

I personally use this code for the resource module of angular to define some basic methods having a custom serializer / deserializer than handle dates and some other stuffs. In your case the baseResourceServiceMaker will be your baseImageService with ending with the $injector.invoke(service, this).

EDIT : found a link with something probably cleaner : AngularJS service inheritance

链接地址: http://www.djcxy.com/p/90468.html

上一篇: 用apache commons cli定义位置参数

下一篇: AngularJS服务继承问题