AngularJS четко заявляет в своей документации, что службы являются одиночными:
AngularJS services are singletons
Как ни странно, module.factory
также возвращает экземпляр Singleton.
Учитывая, что существует множество вариантов использования для не-одноэлементных служб, как лучше всего реализовать фабричный метод для возврата экземпляров службы, чтобы каждый раз, когда ExampleService
объявляется зависимость, она удовлетворялась другим экземпляром ExampleService
?
Ответы:
Я не думаю, что у нас когда-либо должна быть фабрика, возвращающая
new
работоспособную функцию, поскольку это начинает разрушать внедрение зависимостей, и библиотека будет вести себя неуклюже, особенно для третьих сторон. Короче говоря, я не уверен, что существуют какие-либо законные варианты использования не-одноэлементных служб.Лучший способ добиться того же - использовать фабрику в качестве API для возврата коллекции объектов с прикрепленными к ним методами получения и установки. Вот какой-то псевдокод, показывающий, как может работать такой сервис:
.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(); }; });
Это просто псевдокод для поиска виджета по идентификатору и возможности сохранения изменений, внесенных в запись.
Вот какой-то псевдокод сервиса:
.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 // ... }; });
Хотя эти виды гибких сервисов не включены в этот пример, они также могут легко управлять состоянием.
У меня сейчас нет времени, но если это будет полезно, я могу позже собрать простой Plunker для демонстрации.
источник
$resource
.Я не совсем уверен, какой вариант использования вы пытаетесь удовлетворить. Но возможно, чтобы фабрика возвращала экземпляры объекта. Вы должны иметь возможность изменить это в соответствии со своими потребностями.
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
методе службы.источник
ngInfiniteScroll
службой настраиваемого поиска, поэтому я могу отложить инициализацию до какого-либо события щелчка. JSFiddle 1-го ответа обновлен вторым решением: jsfiddle.net/gavinfoley/G5ku5new
будет декларативным, и сразу легко сказать, какие службы являются синглтонами, а какие нет. В зависимости от того, обновляется ли объект.Другой способ - скопировать служебный объект с помощью
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)); }]);
источник
Вот мой пример службы, отличной от singleton, это из ORM, над которым я работаю. В этом примере я показываю базовую модель (ModelFactory), которую я хочу, чтобы службы («пользователи», «документы») наследовали и потенциально расширяли.
В моем ORM ModelFactory внедряет другие службы для обеспечения дополнительной функциональности (запрос, постоянство, отображение схемы), которая изолирована с помощью модульной системы.
В этом примере и пользователь, и служба документов имеют одинаковую функциональность, но имеют свои собственные независимые области действия.
/* 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 предоставляет только одноэлементный сервис / заводскую опцию. один из способов - создать заводскую службу, которая создаст для вас новый экземпляр внутри вашего контроллера или других экземпляров-потребителей. единственное, что вводится, - это класс, создающий новые экземпляры. это хорошее место для внедрения других зависимостей или для инициализации вашего нового объекта в соответствии со спецификацией пользователя (добавление служб или конфигурации)
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); } }
вы можете видеть, что он предоставляет возможность внедрить некоторые определенные службы, которые недоступны на заводском этапе. вы всегда можете выполнить инъекцию в экземпляр фабрики, который будет использоваться всеми экземплярами модели.
Обратите внимание, мне пришлось вырезать часть кода, чтобы я мог сделать некоторые контекстные ошибки ... если вам нужен образец кода, который работает, дайте мне знать.
Я считаю, что NG2 будет иметь возможность внедрить новый экземпляр вашего сервиса в нужное место в вашей DOM, чтобы вам не нужно было создавать собственную реализацию фабрики. придется подождать и посмотреть :)
источник
Я считаю, что есть веская причина для создания нового экземпляра объекта в службе. Мы также должны быть непредвзятыми, а не просто говорить, что мы никогда не должны делать такие вещи, но синглтон был создан таким по какой-то причине . Контроллеры часто создаются и уничтожаются в течение жизненного цикла приложения, но службы должны быть постоянными.
Я могу представить себе вариант использования, в котором у вас есть какой-то рабочий процесс, например, принятие платежа, и у вас есть несколько установленных свойств, но теперь необходимо изменить их тип платежа, потому что кредитная карта клиента не работает, и им нужно предоставить другую форму оплата. Конечно, это во многом связано с тем, как вы создаете свое приложение. Вы можете сбросить все свойства объекта платежа или создать новый экземпляр объекта внутри службы . Но вам не нужен новый экземпляр службы и вы не захотите обновлять страницу.
Я считаю, что решение предоставляет объект внутри службы, который вы можете создать и установить. Но для ясности: один экземпляр службы важен, потому что контроллер может создаваться и уничтожаться много раз, но службы нуждаются в постоянстве. То, что вы ищете, может быть не прямым методом в Angular, а шаблоном объекта, которым вы можете управлять внутри своей службы.
В качестве примера я сделал кнопку сброса . (Это не проверено, это просто краткое представление о сценарии использования для создания нового объекта в службе.
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);
источник