Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 257
r3plica Avatar asked Mar 03 '16 10:03

r3plica


2 Answers

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.

like image 55
Andreas Jägle Avatar answered Nov 05 '22 13:11

Andreas Jägle


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.

like image 43
r3plica Avatar answered Nov 05 '22 14:11

r3plica