AngularJS服务继承问题

时间:2016-03-03 10:49:43

标签: javascript angularjs

我有一个基本服务,如下所示:

.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;
}])

然后我有一些“继承”此服务的服务,如下所示:

.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;
}])

我认为这很好用。但我有这个页面顺序调用所有3个服务(ImageService,LogoService和PlayerTextService)。在页面的第一个视图上一切都很好,如果我离开然后回来图像服务实际上从播放器文本服务拉回东西。现在我知道这是因为服务是单身人士,但我不知道如何解决我的问题。

任何人都可以帮我一把吗?

我添加了一个尝试解决方案的codepen:

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

尝试2

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

5 个答案:

答案 0 :(得分:2)

您尝试的解决方案不起作用,因为BaseService是一个单例。因此,您将完全相同的实例注入所有三个服务注册函数,并且所有这些函数都配置相同的对象。所以基本上最后一个赢了。

看起来您希望使用不同配置的单独服务。这就是供应商的用途。它们允许构建服务实例的两步过程。请参阅以下主题的Stackoverflow答案:

AngularJS: Service vs provider vs factory

作为参考,Restangular是一个需要完全相同的库。您可以将其用作蓝图,并查看Restangular如何处理此要求:

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

请注意,这些概念基于AngularJS 1,如果您希望稍后使用AngularJS 2,则需要以不同方式处理。

答案 1 :(得分:2)

经过大量的捣乱;我终于找到了解决方案adapting this bit of code

我的基本服务如下:

.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;
}])

并且子服务看起来像这样:

.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;
}])

工作正常。

答案 2 :(得分:0)

是的,它会附加,因为它是单身人士。如果要执行此操作,则必须执行继承。

这是我使用的代码并附加到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);
}

现在使用时间

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

基本上我们在这里有什么?函数baseResourceServiceMaker,表示通用特定服务。调用我们想要实例化的服务的$ injector,并将范围设置为泛型服务,因此子类上的this将绑定到与泛型类相同的引用。通用服务将被实例化为您调用它的次数,没有任何混淆。

我个人使用此代码作为angular的资源模块来定义一些具有自定义序列化器/解串器的基本方法,而不是处理日期和其他一些东西。在您的情况下,baseResourceServiceMaker将是您的baseImageService,以$ injector.invoke(service,this)结尾。

编辑:找到了一些可能更干净的链接:AngularJS service inheritance

答案 3 :(得分:0)

如果您使用(或切换到)ES6或TypeScript,这将变得非常容易。

export class Base {
    // . . .
}

然后:

import {app} from '../app';
import {Base} from './base';

export class Child extends Base {
    // . . .
}

app.service('child', Child);

答案 4 :(得分:0)

从你的尝试1:

  1. 您的BaseService在全球范围内寻找apiPath
  2. 而角度准备依赖关系你的最后一个依赖是ImageService,
  3. 首先它将准备依赖,然后它将执行list()方法,
  4. 您的apiPath将引用全球级声明值的所有方式,即来自第2步的apiPath = 'images';
  5. 解决方案:在thisapiPath下的BaseService前面使用list()运算符。

    工作Plunker