AngularJS dengan jelas menyatakan dalam dokumentasinya bahwa Services is Singletons:
AngularJS services are singletons
Berlawanan dengan intuisi, module.factory
juga mengembalikan instance Singleton.
Mengingat bahwa ada banyak kasus penggunaan untuk layanan non-tunggal, apa cara terbaik untuk mengimplementasikan metode pabrik untuk mengembalikan contoh Layanan, sehingga setiap kali sebuah ExampleService
ketergantungan dideklarasikan, itu dipenuhi oleh contoh yang berbeda ExampleService
?
Jawaban:
Saya tidak berpikir kita harus memiliki
new
fungsi yang dapat dikembalikan pabrik karena ini mulai merusak injeksi ketergantungan dan perpustakaan akan berperilaku canggung, terutama untuk pihak ketiga. Singkatnya, saya tidak yakin ada kasus penggunaan yang sah untuk layanan non-tunggal.Cara yang lebih baik untuk mencapai hal yang sama adalah dengan menggunakan pabrik sebagai API untuk mengembalikan kumpulan objek dengan metode pengambil dan penyetel yang dilampirkan padanya. Berikut ini beberapa pseudo-code yang menunjukkan bagaimana penggunaan layanan semacam itu dapat bekerja:
.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(); }; });
Ini hanyalah pseudo-code untuk mencari widget berdasarkan ID dan kemudian dapat menyimpan perubahan yang dibuat pada record.
Berikut beberapa pseudo-code untuk layanan tersebut:
.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 // ... }; });
Meskipun tidak termasuk dalam contoh ini, jenis layanan fleksibel ini juga dapat dengan mudah mengelola status.
Saya tidak punya waktu sekarang, tetapi jika itu akan membantu saya bisa mengumpulkan Plunker sederhana nanti untuk didemonstrasikan.
sumber
$resource
.Saya tidak sepenuhnya yakin kasus penggunaan apa yang Anda coba penuhi. Tetapi dimungkinkan untuk memiliki contoh pengembalian pabrik dari suatu objek. Anda harus dapat memodifikasi ini agar sesuai dengan kebutuhan Anda.
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
Diperbarui
Pertimbangkan permintaan berikut untuk layanan non-tunggal . Di mana Brian Ford mencatat:
dan contoh pengembalian contoh dari pabrik:
myApp.factory('myService', function () { var MyThing = function () {}; MyThing.prototype.foo = function () {}; return { getInstance: function () { return new MyThing(); } }; });
Saya juga berpendapat bahwa contohnya lebih unggul karena fakta bahwa Anda tidak harus menggunakan
new
kata kunci dalam pengontrol Anda. Itu dikemas dalamgetInstance
metode layanan.sumber
ngInfiniteScroll
dan layanan pencarian khusus sehingga saya dapat menunda inisialisasi hingga beberapa peristiwa klik. JSFiddle jawaban pertama diperbarui dengan solusi kedua: jsfiddle.net/gavinfoley/G5ku5new
deklaratif dan mudah untuk langsung mengetahui layanan apa yang lajang dan apa yang tidak. Berdasarkan apakah suatu objek sedang diperbarui.Cara lain adalah dengan menyalin objek layanan dengan
angular.extend()
.app.factory('Person', function(){ return { greet: function() { return "Hello, I'm " + this.name; }, copy: function(name) { return angular.extend({name: name}, this); } }; });
dan kemudian, misalnya, di pengontrol Anda
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 });
Berikut adalah memetik .
sumber
Saya tahu posting ini telah dijawab tetapi saya masih berpikir akan ada beberapa skenario yang sah bahwa Anda perlu memiliki layanan non-tunggal. Katakanlah ada beberapa logika bisnis yang dapat digunakan kembali yang dapat dibagi antara beberapa pengontrol. Dalam skenario ini, tempat terbaik untuk meletakkan logika adalah layanan, tetapi bagaimana jika kita perlu mempertahankan beberapa status dalam logika yang dapat digunakan kembali? Kemudian kita membutuhkan layanan non-tunggal sehingga dapat dibagikan di berbagai pengontrol dalam aplikasi. Beginilah cara saya menerapkan layanan ini:
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)); }]);
sumber
Inilah contoh saya dari layanan non tunggal, Ini dari ORM yang sedang saya kerjakan. Dalam contoh saya menunjukkan Model Dasar (ModelFactory) yang saya ingin layanan ('pengguna', 'dokumen') untuk mewarisi dan potensi untuk diperluas.
Dalam ORM ModelFactory saya menyuntikkan layanan lain untuk menyediakan fungsionalitas tambahan (kueri, ketekunan, pemetaan skema) yang di-sandbox menggunakan sistem modul.
Dalam contoh, baik pengguna dan layanan dokumen memiliki fungsi yang sama tetapi memiliki cakupannya sendiri-sendiri.
/* 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' } } */ } ]);
sumber
angular hanya memberikan opsi layanan / pabrik tunggal . salah satu cara untuk mengatasinya adalah dengan memiliki layanan pabrik yang akan membuat instans baru untuk Anda di dalam pengontrol atau instans konsumen lainnya. satu-satunya hal yang dimasukkan adalah kelas yang membuat instance baru. ini adalah tempat yang baik untuk memasukkan dependensi lain atau untuk menginisialisasi objek baru Anda ke spesifikasi pengguna (menambahkan layanan atau konfigurasi)
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); }
maka dalam contoh konsumen Anda, Anda memerlukan layanan pabrik dan memanggil metode pembangunan di pabrik untuk mendapatkan contoh baru saat Anda membutuhkannya
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); } }
Anda dapat melihatnya memberikan kesempatan untuk menyuntikkan beberapa layanan tertentu yang tidak tersedia di langkah pabrik. Anda selalu dapat mengalami injeksi pada instans pabrik untuk digunakan oleh semua instans Model.
Catatan Saya harus melepas beberapa kode jadi saya mungkin membuat beberapa kesalahan konteks ... jika Anda memerlukan sampel kode yang berfungsi, beri tahu saya.
Saya percaya bahwa NG2 akan memiliki opsi untuk memasukkan contoh baru dari layanan Anda di tempat yang tepat di DOM Anda sehingga Anda tidak perlu membangun implementasi pabrik Anda sendiri. harus menunggu dan melihat :)
sumber
Saya yakin ada alasan bagus untuk membuat instance baru dari sebuah objek dalam layanan. Kita juga harus tetap berpikiran terbuka daripada hanya mengatakan kita tidak boleh melakukan hal seperti itu, tetapi singleton dibuat seperti itu karena suatu alasan . Pengontrol sering dibuat dan dihancurkan dalam siklus aplikasi, tetapi layanan harus tetap ada.
Saya dapat memikirkan kasus penggunaan di mana Anda memiliki beberapa jenis alur kerja, seperti menerima pembayaran dan Anda memiliki beberapa properti yang ditetapkan, tetapi sekarang harus mengubah jenis pembayaran mereka karena kartu kredit pelanggan gagal dan mereka perlu memberikan bentuk lain dari pembayaran. Tentu saja, ini banyak hubungannya dengan cara Anda membuat aplikasi. Anda dapat mengatur ulang semua properti untuk objek pembayaran, atau Anda dapat membuat contoh baru dari objek dalam layanan . Namun, Anda tidak ingin layanan baru, atau menyegarkan halaman.
Saya yakin solusi menyediakan objek dalam layanan yang dapat Anda buat dan setel instance baru. Namun, untuk memperjelas, satu contoh layanan penting karena pengontrol dapat dibuat dan dimusnahkan berkali-kali, tetapi layanan tersebut membutuhkan ketekunan. Apa yang Anda cari mungkin bukan metode langsung dalam Angular, tetapi pola objek yang dapat Anda kelola di dalam layanan Anda.
Sebagai contoh, saya telah membuat tombol reset . (Ini belum diuji, ini benar-benar hanya gagasan singkat tentang kasus penggunaan untuk membuat objek baru dalam layanan.
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) } } } }]);
sumber
Berikut pendekatan lain untuk masalah yang saya cukup puas, khususnya saat digunakan dalam kombinasi dengan Closure Compiler dengan pengoptimalan lanjutan diaktifkan:
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);
sumber