Angular中的非Singleton服务

Angular在文档中明确指出服务是单身人士:

Angular services are singletons 

反直觉地, module.factory也返回一个Singleton实例。

鉴于非单身服务有很多用例,实现工厂方法返回服务实例的最好方法是什么,以便每次声明一个ExampleService依赖时,它都会被一个不同的实例ExampleService

我不认为我们应该有一个工厂返回一个new有用的function,因为这开始打乱dependency injection和图书馆会performance不好,尤其是对第三方。 总之,我不确定是否有任何非单身服务的合法用例。

一个更好的方法来完成同样的事情是使用工厂作为一个API来返回一个对象的集合连接到他们的getter和setter方法。 这是一些伪代码,显示了如何使用这种服务可能工作:

 .controller( 'MainCtrl', function ( $scope, widgetService ) { $scope.onSearchFormSubmission = function () { widgetService.findById( $scope.searchById ).then(function ( widget ) { // this is a returned object, complete with all the getter/setters $scope.widget = widget; }); }; $scope.onWidgetSave = function () { // this method persists the widget object $scope.widget.$save(); }; }); 

这只是通过ID查找小部件的伪代码,然后能够保存对logging所做的更改。

以下是该服务的一些伪代码:

 .factory( 'widgetService', function ( $http ) { function Widget( json ) { angular.extend( this, json ); } Widget.prototype = { $save: function () { // TODO: strip irrelevant fields var scrubbedObject = //... return $http.put( '/widgets/'+this.id, scrubbedObject ); } }; function getWidgetById ( id ) { return $http( '/widgets/'+id ).then(function ( json ) { return new Widget( json ); }); } // the public widget API return { // ... findById: getWidgetById // ... }; }); 

虽然没有包括在这个例子中,但是这些灵活的服务也可以很容易地pipe理状态。


我现在没有时间,但是如果这会有帮助的话,我可以稍后再组织一个简单的普伦克来演示。

我不完全确定你想要满足什么用例。 但有可能有一个工厂返回一个对象的实例。 你应该能够修改这个来适应你的需求。

 var ExampleApplication = angular.module('ExampleApplication', []); ExampleApplication.factory('InstancedService', function(){ function Instance(name, type){ this.name = name; this.type = type; } return { Instance: Instance } }); ExampleApplication.controller('InstanceController', function($scope, InstancedService){ var instanceA = new InstancedService.Instance('A','string'), instanceB = new InstancedService.Instance('B','object'); console.log(angular.equals(instanceA, instanceB)); }); 

的jsfiddle

更新

考虑以下请求非单身服务 。 布莱恩·福特在其中指出:

所有服务都是单例的想法并不能阻止你编写可以实例化新对象的单例工厂。

和他从工厂返回实例的例子:

 myApp.factory('myService', function () { var MyThing = function () {}; MyThing.prototype.foo = function () {}; return { getInstance: function () { return new MyThing(); } }; }); 

我也会认为他的例子是优越的,因为你不必在你的控制器中使用new关键字。 它被封装在服务的getInstance方法中。

另一种方法是用angular.extend()复制服务对象。

 app.factory('Person', function(){ return { greet: function() { return "Hello, I'm " + this.name; }, copy: function(name) { return angular.extend({name: name}, this); } }; }); 

然后,例如,在你的控制器

 app.controller('MainCtrl', function ($scope, Person) { michael = Person.copy('Michael'); peter = Person.copy('Peter'); michael.greet(); // Hello I'm Michael peter.greet(); // Hello I'm Peter }); 

这是一个庞然大物 。

我知道这个职位已经被回答,但我仍然认为会有一些合法的情况下,你需要有非单身服务。 假设有一些可重用的业务逻辑可以在多个控制器之间共享。 在这种情况下,放置逻辑的最佳位置是服务,但是如果我们需要在我们的可重用逻辑中保留某些状态呢? 那么我们需要非单身服务,所以可以在应用程序中的不同控制器之间共享。 这是我将如何实施这些服务:

 angular.module('app', []) .factory('nonSingletonService', function(){ var instance = function (name, type){ this.name = name; this.type = type; return this; } return instance; }) .controller('myController', ['$scope', 'nonSingletonService', function($scope, nonSingletonService){ var instanceA = new nonSingletonService('A','string'); var instanceB = new nonSingletonService('B','object'); console.log(angular.equals(instanceA, instanceB)); }]); 

这是我的一个非单身服务的例子,它来自一个ORM即时工作。 在这个例子中,我展示了一个我想要服务('用户','文档')inheritance和潜在扩展的基本模型(ModelFactory)。

在我的ORM中,ModelFactory注入其他服务以提供使用模块系统进行沙箱化的额外function(查询,持久性,模式映射)。

在该示例中,用户和文档服务具有相同的function,但具有自己的独立范围。

 /* A class which which we want to have multiple instances of, it has two attrs schema, and classname */ var ModelFactory; ModelFactory = function($injector) { this.schema = {}; this.className = ""; }; Model.prototype.klass = function() { return { className: this.className, schema: this.schema }; }; Model.prototype.register = function(className, schema) { this.className = className; this.schema = schema; }; angular.module('model', []).factory('ModelFactory', [ '$injector', function($injector) { return function() { return $injector.instantiate(ModelFactory); }; } ]); /* Creating multiple instances of ModelFactory */ angular.module('models', []).service('userService', [ 'ModelFactory', function(modelFactory) { var instance; instance = new modelFactory(); instance.register("User", { name: 'String', username: 'String', password: 'String', email: 'String' }); return instance; } ]).service('documentService', [ 'ModelFactory', function(modelFactory) { var instance; instance = new modelFactory(); instance.register("Document", { name: 'String', format: 'String', fileSize: 'String' }); return instance; } ]); /* Example Usage */ angular.module('controllers', []).controller('exampleController', [ '$scope', 'userService', 'documentService', function($scope, userService, documentService) { userService.klass(); /* returns { className: "User" schema: { name : 'String' username : 'String' password: 'String' email: 'String' } } */ return documentService.klass(); /* returns { className: "User" schema: { name : 'String' format : 'String' formatileSize: 'String' } } */ } ]); 

angular度只给出一个单一的服务/工厂选项。 一个方法是有一个工厂服务,将在您的控制器或其他消费者实例内为您build立一个新的实例。 唯一注入的是创build新实例的类。 这是注入其他依赖关系或将新对象初始化为用户规范(添加服务或configuration)的好地方,

 namespace admin.factories { 'use strict'; export interface IModelFactory { build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel; } class ModelFactory implements IModelFactory { // any injection of services can happen here on the factory constructor... // I didnt implement a constructor but you can have it contain a $log for example and save the injection from the build funtion. build($log: ng.ILogService, connection: string, collection: string, service: admin.services.ICollectionService): IModel { return new Model($log, connection, collection, service); } } export interface IModel { // query(connection: string, collection: string): ng.IPromise<any>; } class Model implements IModel { constructor( private $log: ng.ILogService, private connection: string, private collection: string, service: admin.services.ICollectionService) { }; } angular.module('admin') .service('admin.services.ModelFactory', ModelFactory); } 

那么在消费者实例中,需要工厂服务,并在工厂调用生成方法以在需要时获取新实例

  class CollectionController { public model: admin.factories.IModel; static $inject = ['$log', '$routeParams', 'admin.services.Collection', 'admin.services.ModelFactory']; constructor( private $log: ng.ILogService, $routeParams: ICollectionParams, private service: admin.services.ICollectionService, factory: admin.factories.IModelFactory) { this.connection = $routeParams.connection; this.collection = $routeParams.collection; this.model = factory.build(this.$log, this.connection, this.collection, this.service); } } 

你可以看到它提供opperatiunity注入一些特定的服务,在工厂步骤不可用。 您可以始终在工厂实例上进行注入,以供所有Model实例使用。

注意我不得不删除一些代码,所以我可能会犯一些上下文错误…如果你需要一个代码示例,让我知道。

我相信NG2可以select在你的DOM的正确位置注入一个新的服务实例,所以你不需要build立你自己的工厂实现。 将不得不等待,看看:)

我相信有一个很好的理由在服务中创build一个对象的新实例。 我们应该保持开放的态度,而不是说我们不应该做这样的事情,但单身人士是这样做的 。 控制器经常在应用程序的生命周期内被创build和销毁,但是这些服务必须是持久的。

我可以考虑一个用例,你有一个工作stream程,比如接受一个付款,你有多个属性设置,但现在必须改变他们的付款types,因为客户的信用卡失败,他们需要提供不同forms的付款。 当然,这与你创build应用程序的方式有很大的关系。 您可以重置付款对象的所有属性,也可以在服务中创build对象的新实例 。 但是,你不会想要一个新的服务实例,也不想刷新页面。

我相信一个解决scheme是在服务中提供一个对象,你可以创build一个新的实例并设置。 但是,要明确一点,服务的单一实例是重要的,因为控制器可能会被创build和销毁很多次,但是服务需要持久化。 你正在寻找的可能不是Angular中的直接方法,而是你可以在你的服务中pipe理的对象模式。

作为一个例子,我有一个重置button。 (这没有经过testing,它实际上只是一个用于在服务中创build新对象的用例的快速概念。

 app.controller("PaymentController", ['$scope','PaymentService',function($scope, PaymentService) { $scope.utility = { reset: PaymentService.payment.reset() }; }]); app.factory("PaymentService", ['$http', function ($http) { var paymentURL = "https://www.paymentserviceprovider.com/servicename/token/" function PaymentObject(){ // this.user = new User(); /** Credit Card*/ // this.paymentMethod = ""; //... } var payment = { options: ["Cash", "Check", "Existing Credit Card", "New Credit Card"], paymentMethod: new PaymentObject(), getService: function(success, fail){ var request = $http({ method: "get", url: paymentURL } ); return ( request.then(success, fail) ); } //... } return { payment: { reset: function(){ payment.paymentMethod = new PaymentObject(); }, request: function(success, fail){ return payment.getService(success, fail) } } } }]); 

下面是另一个我非常满意的问题的方法,特别是与Closure Compiler配合使用,并且启用了高级优化:

 var MyFactory = function(arg1, arg2) { this.arg1 = arg1; this.arg2 = arg2; }; MyFactory.prototype.foo = function() { console.log(this.arg1, this.arg2); // You have static access to other injected services/factories. console.log(MyFactory.OtherService1.foo()); console.log(MyFactory.OtherService2.foo()); }; MyFactory.factory = function(OtherService1, OtherService2) { MyFactory.OtherService1_ = OtherService1; MyFactory.OtherService2_ = OtherService2; return MyFactory; }; MyFactory.create = function(arg1, arg2) { return new MyFactory(arg1, arg2); }; // Using MyFactory. MyCtrl = function(MyFactory) { var instance = MyFactory.create('bar1', 'bar2'); instance.foo(); // Outputs "bar1", "bar2" to console, plus whatever static services do. }; angular.module('app', []) .factory('MyFactory', MyFactory) .controller('MyCtrl', MyCtrl);