AngularJS: Service gegen Anbieter gegen Fabrik


3319

Was sind die Unterschiede zwischen einem Service, Providerund Factoryin AngularJS?


244
Ich fand, dass alle Angular-Begriffe für Anfänger einschüchternd waren. Wir haben mit diesem Cheatsheet begonnen, das für unsere Programmierer beim Erlernen von Angular demisx.github.io/angularjs/2014/09/14/… etwas einfacher zu verstehen war . Hoffe das hilft auch deinem Team.
Demisx

7
Meiner Meinung nach ist der beste Weg, den Unterschied zu verstehen, die Verwendung von Angulars eigener Dokumentation: docs.angularjs.org/guide/providers. Sie ist sehr gut erklärt und verwendet ein eigenartiges Beispiel, um Ihnen das Verständnis zu erleichtern .
Rafael Merlin

3
@Blaise Danke! Gemäß meinem Kommentar im Beitrag habe ich ihn absichtlich weggelassen, da 99% der Anwendungsfälle aus meiner Erfahrung über erfolgreich bearbeitet werden können service.factory. Ich wollte dieses Thema nicht weiter komplizieren.
Demisx

3
Ich finde diese Diskussion auch sehr nützlich stackoverflow.com/questions/18939709/…
Anand Gupta

3
Hier sind einige gute Antworten darüberwieservices,factoriesundprovidersfunktioniert.
Mistalis

Antworten:


2866

Von der AngularJS-Mailingliste habe ich einen erstaunlichen Thread erhalten , der Service gegen Fabrik gegen Anbieter und deren Injektionsnutzung erklärt. Zusammenstellung der Antworten:

Dienstleistungen

Syntax: module.service( 'serviceName', function );
Ergebnis: Wenn Sie serviceName als injizierbares Argument deklarieren, erhalten Sie eine Instanz der Funktion. Mit anderen Worten new FunctionYouPassedToService() .

Fabriken

Syntax: module.factory( 'factoryName', function );
Ergebnis: Wenn Sie factoryName als injizierbares Argument deklarieren, erhalten Sie den Wert, der durch Aufrufen der an module.factory übergebenen Funktionsreferenz zurückgegeben wird .

Anbieter

Syntax: module.provider( 'providerName', function );
Ergebnis: Beim Deklarieren von providerName als injizierbares Argument Sie (new ProviderFunction()).$get() . Die Konstruktorfunktion wird instanziiert, bevor die $ get-Methode aufgerufen wird - ProviderFunctionist die an module.provider übergebene Funktionsreferenz.

Anbieter haben den Vorteil, dass sie während der Modulkonfigurationsphase konfiguriert werden können.

Sehen Sie hier für den bereitgestellten Code.

Hier ist eine großartige weitere Erklärung von Misko:

provide.value('a', 123);

function Controller(a) {
  expect(a).toEqual(123);
}

In diesem Fall gibt der Injektor den Wert einfach so zurück, wie er ist. Aber was ist, wenn Sie den Wert berechnen möchten? Dann benutzen Sie eine Fabrik

provide.factory('b', function(a) {
  return a*2;
});

function Controller(b) {
  expect(b).toEqual(246);
}

So factoryist eine Funktion, die für die Wertschöpfung verantwortlich ist. Beachten Sie, dass die Factory-Funktion nach anderen Abhängigkeiten fragen kann.

Aber was ist, wenn Sie mehr OO sein und eine Klasse namens Greeter haben möchten?

function Greeter(a) {
  this.greet = function() {
    return 'Hello ' + a;
  }
}

Dann müssten Sie schreiben, um zu instanziieren

provide.factory('greeter', function(a) {
  return new Greeter(a);
});

Dann könnten wir in einem solchen Controller nach einem Begrüßer fragen

function Controller(greeter) {
  expect(greeter instanceof Greeter).toBe(true);
  expect(greeter.greet()).toEqual('Hello 123');
}

Aber das ist viel zu wortreich. Ein kürzerer Weg, dies zu schreiben, wäreprovider.service('greeter', Greeter);

Aber was ist, wenn wir die GreeterKlasse vor der Injektion konfigurieren wollten ? Dann könnten wir schreiben

provide.provider('greeter2', function() {
  var salutation = 'Hello';
  this.setSalutation = function(s) {
    salutation = s;
  }

  function Greeter(a) {
    this.greet = function() {
      return salutation + ' ' + a;
    }
  }

  this.$get = function(a) {
    return new Greeter(a);
  };
});

Dann können wir das machen:

angular.module('abc', []).config(function(greeter2Provider) {
  greeter2Provider.setSalutation('Halo');
});

function Controller(greeter2) {
  expect(greeter2.greet()).toEqual('Halo 123');
}

Als Randbemerkung, service, factory, und valuesind alle von Anbietern abgeleitet.

provider.service = function(name, Class) {
  provider.provide(name, function() {
    this.$get = function($injector) {
      return $injector.instantiate(Class);
    };
  });
}

provider.factory = function(name, factory) {
  provider.provide(name, function() {
    this.$get = function($injector) {
      return $injector.invoke(factory);
    };
  });
}

provider.value = function(name, value) {
  provider.factory(name, function() {
    return value;
  });
};

58
Siehe auch stackoverflow.com/a/13763886/215945, in dem die Unterschiede zwischen Service und Werk erläutert werden.
Mark Rajcok

3
In Bearbeitung 611 habe ich die Verwendung von Winkelkonstanten und -werten hinzugefügt. Um die Unterschiede der bereits gezeigten zu demonstrieren. jsbin.com/ohamub/611/edit
Nick

17
Obwohl ein Dienst durch Erstellen einer Instanz der Funktion aufgerufen wird. Es wird tatsächlich nur einmal pro Injektor erstellt, wodurch es wie ein Singleton wirkt. docs.angularjs.org/guide/dev_guide.services.creating_services
Angelokh

33
Dieses Beispiel könnte unglaublich sein, wenn es ein klares praktisches Beispiel verwendet. Ich verliere mich beim Versuch herauszufinden, was der Sinn der Dinge ist toEqualund greeter.Greetist. Warum nicht etwas Realeres und Relatierbares verwenden?
Kyle Pennell

5
Die Verwendung der Funktion require () ist eine schlechte Wahl, um etwas zu erklären. Verwenden Sie beim nächsten Mal den Code der realen Welt.
Craig

812

JS Fiddle Demo

Beispiel "Hallo Welt" mit factory/ service/ provider:

var myApp = angular.module('myApp', []);

//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
    this.sayHello = function() {
        return "Hello, World!";
    };
});

//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
    return {
        sayHello: function() {
            return "Hello, World!";
        }
    };
});
    
//provider style, full blown, configurable version     
myApp.provider('helloWorld', function() {

    this.name = 'Default';

    this.$get = function() {
        var name = this.name;
        return {
            sayHello: function() {
                return "Hello, " + name + "!";
            }
        }
    };

    this.setName = function(name) {
        this.name = name;
    };
});

//hey, we can configure a provider!            
myApp.config(function(helloWorldProvider){
    helloWorldProvider.setName('World');
});
        

function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {
    
    $scope.hellos = [
        helloWorld.sayHello(),
        helloWorldFromFactory.sayHello(),
        helloWorldFromService.sayHello()];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
    {{hellos}}
</div>
</body>


2
Ändert der thisKontext in der $getFunktion nicht? - Sie verweisen in dieser Funktion nicht mehr auf den instanziierten Anbieter.
Nate-Wilkins

12
@Nate: Ändert den thisKontext nicht, da aufgerufen wird new Provider(). $ Get (), an Providerden die Funktion übergeben wird app.provider. Das heißt , dass $get()als ein Verfahren auf dem konstruierten aufgerufen wird Provider, so thisbeziehen sich auf Providerwie das Beispiel zeigt.
Brandon

1
@Brandon Ohh ok das ist dann irgendwie ordentlich. Verwirrend auf den ersten Blick - danke für die Klarstellung!
Nate-Wilkins

3
Warum bekomme ich, Unknown provider: helloWorldProvider <- helloWorldwenn ich das lokal ausführe? Auskommentieren, gleicher Fehler für die anderen 2 Beispiele. Gibt es eine versteckte Anbieterkonfiguration? (Angular 1.0.8) - Gefunden: stackoverflow.com/questions/12339272/…
Antoine

4
Ist der Grund, warum @Antoine den Fehler "Unbekanntes Angebot: helloWorldProvider" erhält, weil Sie in Ihrem .config-Code 'helloWorldProvider' verwenden, aber wenn Sie den Anbieter in myApp.provider definieren ('helloWorld', function ()), verwenden Sie 'Hallo Welt'? Mit anderen Worten, woher weiß Angular in Ihrem Konfigurationscode, dass Sie sich auf den helloWorld-Anbieter beziehen? Danke
jmtoung

645

TL; DR

1) Wenn Sie eine Factory verwenden , erstellen Sie ein Objekt, fügen Eigenschaften hinzu und geben dasselbe Objekt zurück. Wenn Sie diese Factory an Ihren Controller übergeben, sind diese Eigenschaften für das Objekt jetzt in diesem Controller über Ihre Factory verfügbar.

app.controller(‘myFactoryCtrl’, function($scope, myFactory){
  $scope.artist = myFactory.getArtist();
});

app.factory(‘myFactory’, function(){
  var _artist = Shakira’;
  var service = {};

  service.getArtist = function(){
    return _artist;
  }

  return service;
});


2) Wenn Sie Service verwenden , instanziiert AngularJS ihn hinter den Kulissen mit dem Schlüsselwort 'new'. Aus diesem Grund fügen Sie 'this' Eigenschaften hinzu, und der Dienst gibt 'this' zurück. Wenn Sie den Dienst an Ihren Controller übergeben, sind diese Eigenschaften für "this" jetzt über Ihren Dienst auf diesem Controller verfügbar.

app.controller(‘myServiceCtrl’, function($scope, myService){
  $scope.artist = myService.getArtist();
});

app.service(‘myService’, function(){
  var _artist = Nelly’;
  this.getArtist = function(){
    return _artist;
  }
});



3) Anbieter sind der einzige Dienst, den Sie an Ihre .config () - Funktion übergeben können. Verwenden Sie einen Anbieter, wenn Sie eine modulweite Konfiguration für Ihr Serviceobjekt bereitstellen möchten, bevor Sie es verfügbar machen.

app.controller(‘myProvider’, function($scope, myProvider){
  $scope.artist = myProvider.getArtist();
  $scope.data.thingFromConfig = myProvider.thingOnConfig;
});

app.provider(‘myProvider’, function(){
 //Only the next two lines are available in the app.config()
 this._artist = ‘’;
 this.thingFromConfig = ‘’;
  this.$get = function(){
    var that = this;
    return {
      getArtist: function(){
        return that._artist;
      },
      thingOnConfig: that.thingFromConfig
    }
  }
});

app.config(function(myProviderProvider){
  myProviderProvider.thingFromConfig = This was set in config’;
});



Nicht TL; DR

1) Factory
Factories sind die beliebteste Methode zum Erstellen und Konfigurieren eines Dienstes. Es gibt wirklich nicht viel mehr als das, was die TL gesagt hat; DR. Sie erstellen einfach ein Objekt, fügen ihm Eigenschaften hinzu und geben dasselbe Objekt zurück. Wenn Sie dann die Factory an Ihren Controller übergeben, sind diese Eigenschaften für das Objekt jetzt in diesem Controller über Ihre Factory verfügbar. Ein ausführlicheres Beispiel finden Sie unten.

app.factory(‘myFactory’, function(){
  var service = {};
  return service;
});

Jetzt stehen uns alle Eigenschaften zur Verfügung, die wir an 'service' anhängen, wenn wir 'myFactory' an unseren Controller übergeben.

Fügen wir nun unserer Rückruffunktion einige 'private' Variablen hinzu. Auf diese kann vom Controller aus nicht direkt zugegriffen werden, aber wir werden eventuell einige Getter / Setter-Methoden für 'service' einrichten, um diese 'privaten' Variablen bei Bedarf ändern zu können.

app.factory(‘myFactory’, function($http, $q){
  var service = {};
  var baseUrl = https://itunes.apple.com/search?term=’;
  var _artist = ‘’;
  var _finalUrl = ‘’;

  var makeUrl = function(){
   _artist = _artist.split(‘ ‘).join(‘+’);
    _finalUrl = baseUrl + _artist + ‘&callback=JSON_CALLBACK’;
    return _finalUrl
  }

  return service;
});

Hier werden Sie feststellen, dass wir diese Variablen / Funktionen nicht an 'service' anhängen. Wir erstellen sie einfach, um sie später entweder zu verwenden oder zu ändern.

  • baseUrl ist die Basis-URL, die die iTunes-API benötigt
  • _artist ist der Künstler, nach dem wir suchen möchten
  • _finalUrl ist die endgültige und vollständig erstellte URL, unter der wir iTunes anrufen
  • makeUrl ist eine Funktion, mit der unsere iTunes-freundliche URL erstellt und zurückgegeben wird.

Nachdem unsere Hilfs- / privaten Variablen und Funktionen vorhanden sind, fügen wir dem 'service'-Objekt einige Eigenschaften hinzu. Was auch immer wir auf "Service" setzen, kann direkt in jedem Controller verwendet werden, an den wir "myFactory" übergeben.

Wir werden setArtist- und getArtist-Methoden erstellen, die den Künstler einfach zurückgeben oder festlegen. Wir werden auch eine Methode erstellen, die die iTunes-API mit unserer erstellten URL aufruft. Diese Methode wird ein Versprechen zurückgeben, das erfüllt wird, sobald die Daten von der iTunes-API zurückgegeben wurden. Wenn Sie nicht viel Erfahrung mit Versprechungen in AngularJS haben, empfehle ich Ihnen dringend, sich eingehend mit ihnen zu befassen.

Unten akzeptiert setArtist einen Künstler und ermöglicht es Ihnen, den Künstler festzulegen . getArtist gibt den Künstler zurück. callItunes ruft zuerst makeUrl () auf, um die URL zu erstellen, die wir für unsere $ http-Anfrage verwenden. Anschließend wird ein Versprechungsobjekt eingerichtet, eine $ http-Anfrage mit unserer endgültigen URL gestellt. Da $ http ein Versprechen zurückgibt, können wir nach unserer Anfrage .success oder .error aufrufen. Wir lösen dann unser Versprechen mit den iTunes-Daten oder lehnen es mit der Meldung "Es ist ein Fehler aufgetreten" ab.

app.factory('myFactory', function($http, $q){
  var service = {};
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  service.setArtist = function(artist){
    _artist = artist;
  }

  service.getArtist = function(){
    return _artist;
  }

  service.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

  return service;
});

Jetzt ist unsere Fabrik fertig. Wir können jetzt 'myFactory' in jeden Controller einfügen und dann unsere Methoden aufrufen, die wir an unser Serviceobjekt angehängt haben (setArtist, getArtist und callItunes).

app.controller('myFactoryCtrl', function($scope, myFactory){
  $scope.data = {};
  $scope.updateArtist = function(){
    myFactory.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myFactory.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

Im obigen Controller injizieren wir den 'myFactory'-Dienst. Anschließend legen wir Eigenschaften für unser $ scope-Objekt mit Daten aus 'myFactory' fest. Der einzige schwierige Code oben ist, wenn Sie sich noch nie mit Versprechungen befasst haben. Da callItunes ein Versprechen zurückgibt, können wir die .then () -Methode verwenden und $ scope.data.artistData erst festlegen, wenn unser Versprechen mit den iTunes-Daten erfüllt ist. Sie werden feststellen, dass unser Controller sehr dünn ist (dies ist eine gute Codierungspraxis). Alle unsere logischen und persistenten Daten befinden sich in unserem Service, nicht in unserem Controller.

2) Service
Das vielleicht Wichtigste, was Sie beim Erstellen eines Dienstes wissen müssen, ist, dass er mit dem Schlüsselwort "Neu" instanziiert wird. Für Ihre JavaScript-Gurus sollte dies einen großen Hinweis auf die Art des Codes geben. Für diejenigen unter Ihnen mit eingeschränktem Hintergrund in JavaScript oder für diejenigen, die nicht genau wissen, was das 'neue' Schlüsselwort tatsächlich bewirkt, lassen Sie uns einige JavaScript-Grundlagen überprüfen, die uns letztendlich helfen, die Natur eines Dienstes zu verstehen.

Um die Änderungen wirklich zu sehen, die auftreten, wenn Sie eine Funktion mit dem Schlüsselwort 'new' aufrufen, erstellen wir eine Funktion und rufen sie mit dem Schlüsselwort 'new' auf. Zeigen wir dann, was der Interpreter tut, wenn er das Schlüsselwort 'new' sieht. Die Endergebnisse sind beide gleich.

Lassen Sie uns zuerst unseren Konstruktor erstellen.

var Person = function(name, age){
  this.name = name;
  this.age = age;
}

Dies ist eine typische JavaScript-Konstruktorfunktion. Wenn wir nun die Person-Funktion mit dem Schlüsselwort 'new' aufrufen, wird 'this' an das neu erstellte Objekt gebunden.

Fügen wir nun dem Prototyp unserer Person eine Methode hinzu, damit sie für jede Instanz unserer Personenklasse verfügbar ist.

Person.prototype.sayName = function(){
  alert(‘My name is  + this.name);
}

Da wir nun die Funktion sayName auf den Prototyp setzen, kann jede Instanz von Person die Funktion sayName aufrufen, um den Namen dieser Instanz zu warnen.

Nachdem wir nun unsere Person-Konstruktorfunktion und unsere sayName-Funktion in ihrem Prototyp haben, erstellen wir tatsächlich eine Instanz von Person und rufen dann die sayName-Funktion auf.

var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’

Insgesamt sieht der Code zum Erstellen eines Personenkonstruktors, zum Hinzufügen einer Funktion zum Prototyp, zum Erstellen einer Personeninstanz und zum anschließenden Aufrufen der Funktion für den Prototyp folgendermaßen aus.

var Person = function(name, age){
  this.name = name;
  this.age = age;
}
Person.prototype.sayName = function(){
  alert(‘My name is  + this.name);
}
var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’

Schauen wir uns nun an, was tatsächlich passiert, wenn Sie das Schlüsselwort 'new' in JavaScript verwenden. Das erste, was Sie beachten sollten, ist, dass wir nach der Verwendung von 'new' in unserem Beispiel eine Methode (sayName) für 'tyler' aufrufen können, als wäre es ein Objekt - das liegt daran, dass es so ist. Zuerst wissen wir also, dass unser Personenkonstruktor ein Objekt zurückgibt, ob wir das im Code sehen können oder nicht. Zweitens wissen wir, dass das Objekt, das die Person-Funktion zurückgibt, bei fehlgeschlagenen Suchvorgängen an ihren Prototyp delegieren muss, da sich unsere Funktion sayName auf dem Prototyp und nicht direkt auf der Person-Instanz befindet. Einfacher ausgedrückt, wenn wir tyler.sayName () aufrufen, sagt der Interpreter: „OK, ich werde das soeben erstellte 'tyler'-Objekt betrachten, die Funktion sayName suchen und es dann aufrufen. Moment mal, ich sehe es hier nicht - alles was ich sehe ist Name und Alter, Lassen Sie mich den Prototyp überprüfen. Ja, es sieht so aus, als wäre es auf dem Prototyp, lass es mich nennen. “

Unten finden Sie Code, wie Sie darüber nachdenken können, was das 'neue' Schlüsselwort in JavaScript tatsächlich tut. Es ist im Grunde ein Codebeispiel für den obigen Absatz. Ich habe die 'Interpreter-Ansicht' oder die Art und Weise, wie der Interpreter den Code sieht, in Notizen eingefügt.

var Person = function(name, age){
  //The below line creates an object(obj) that will delegate to the person’s prototype on failed lookups.
  //var obj = Object.create(Person.prototype);

  //The line directly below this sets ‘this’ to the newly created object
  //this = obj;

  this.name = name;
  this.age = age;

  //return this;
}

Wenn Sie nun wissen, was das 'neue' Schlüsselwort in JavaScript wirklich bewirkt, sollte es einfacher sein, einen Dienst in AngularJS zu erstellen.

Das Wichtigste, was Sie beim Erstellen eines Dienstes verstehen müssen, ist zu wissen, dass Dienste mit dem Schlüsselwort "Neu" instanziiert werden. Wenn Sie dieses Wissen mit unseren obigen Beispielen kombinieren, sollten Sie jetzt erkennen, dass Sie Ihre Eigenschaften und Methoden direkt an 'this' anhängen, das dann vom Service selbst zurückgegeben wird. Schauen wir uns das in Aktion an.

Im Gegensatz zu dem, was wir ursprünglich mit dem Factory-Beispiel gemacht haben, müssen wir kein Objekt erstellen und dann dieses Objekt zurückgeben, da wir, wie bereits mehrfach erwähnt, das Schlüsselwort 'new' verwendet haben, damit der Interpreter dieses Objekt erstellt und es delegieren lässt Es ist ein Prototyp und wird dann für uns zurückgegeben, ohne dass wir die Arbeit erledigen müssen.

Lassen Sie uns zuerst unsere 'private' und Hilfsfunktion erstellen. Dies sollte sehr vertraut aussehen, da wir mit unserer Fabrik genau dasselbe gemacht haben. Ich werde hier nicht erklären, was jede Zeile tut, da ich das im Fabrikbeispiel getan habe. Wenn Sie verwirrt sind, lesen Sie das Fabrikbeispiel erneut.

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
});

Jetzt werden wir alle unsere Methoden, die in unserem Controller verfügbar sein werden, an 'this' anhängen.

app.service('myService', function($http, $q){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.setArtist = function(artist){
    _artist = artist;
  }

  this.getArtist = function(){
    return _artist;
  }

  this.callItunes = function(){
    makeUrl();
    var deferred = $q.defer();
    $http({
      method: 'JSONP',
      url: _finalUrl
    }).success(function(data){
      deferred.resolve(data);
    }).error(function(){
      deferred.reject('There was an error')
    })
    return deferred.promise;
  }

});

Genau wie in unserer Factory sind setArtist, getArtist und callItunes jetzt in jedem Controller verfügbar, an den wir myService übergeben. Hier ist der myService-Controller (der fast genau dem unseres werkseitigen Controllers entspricht).

app.controller('myServiceCtrl', function($scope, myService){
  $scope.data = {};
  $scope.updateArtist = function(){
    myService.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myService.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }
});

Wie ich bereits erwähnt habe, sind Services, sobald Sie wirklich verstanden haben, was "neu" macht, fast identisch mit Fabriken in AngularJS.

3) Anbieter

Das Wichtigste an Anbietern ist, dass sie der einzige Dienst sind, den Sie an den Teil app.config Ihrer Anwendung übergeben können. Dies ist von großer Bedeutung, wenn Sie einen Teil Ihres Serviceobjekts ändern müssen, bevor es überall in Ihrer Anwendung verfügbar ist. Obwohl Services / Fabriken sehr ähnlich sind, gibt es einige Unterschiede, die wir diskutieren werden.

Zuerst haben wir unseren Provider auf ähnliche Weise eingerichtet wie bei unserem Service und unserer Fabrik. Die folgenden Variablen sind unsere 'private' und Hilfsfunktion.

app.provider('myProvider', function(){
   var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  //Going to set this property on the config function below.
  this.thingFromConfig = ‘’;

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
}

* Wenn ein Teil des obigen Codes verwirrend ist, lesen Sie den Abschnitt Factory, in dem ich erkläre, was das alles bewirkt.

Sie können sich Anbieter mit drei Abschnitten vorstellen. Der erste Abschnitt enthält die 'privaten' Variablen / Funktionen, die später geändert / festgelegt werden (siehe oben). Der zweite Abschnitt enthält die Variablen / Funktionen, die in Ihrer app.config-Funktion verfügbar sind und daher geändert werden können, bevor sie an anderer Stelle verfügbar sind (siehe auch oben). Es ist wichtig zu beachten, dass diese Variablen an das Schlüsselwort 'this' angehängt werden müssen. In unserem Beispiel kann nur 'thingFromConfig' in der app.config geändert werden. Der dritte Abschnitt (siehe unten) enthält alle Variablen / Funktionen, die in Ihrem Controller verfügbar sind, wenn Sie den Dienst 'myProvider' an diesen bestimmten Controller übergeben.

Wenn Sie einen Dienst mit Provider erstellen, sind die einzigen Eigenschaften / Methoden, die in Ihrem Controller verfügbar sind, die Eigenschaften / Methoden, die von der Funktion $ get () zurückgegeben werden. Der folgende Code setzt $ get auf 'this' (von dem wir wissen, dass es irgendwann von dieser Funktion zurückgegeben wird). Diese $ get-Funktion gibt nun alle Methoden / Eigenschaften zurück, die im Controller verfügbar sein sollen. Hier ist ein Codebeispiel.

this.$get = function($http, $q){
    return {
      callItunes: function(){
        makeUrl();
        var deferred = $q.defer();
        $http({
          method: 'JSONP',
          url: _finalUrl
        }).success(function(data){
          deferred.resolve(data);
        }).error(function(){
          deferred.reject('There was an error')
        })
        return deferred.promise;
      },
      setArtist: function(artist){
        _artist = artist;
      },
      getArtist: function(){
        return _artist;
      },
      thingOnConfig: this.thingFromConfig
    }
  }

Jetzt sieht der vollständige Provider-Code so aus

app.provider('myProvider', function(){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  //Going to set this property on the config function below
  this.thingFromConfig = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.$get = function($http, $q){
    return {
      callItunes: function(){
        makeUrl();
        var deferred = $q.defer();
        $http({
          method: 'JSONP',
          url: _finalUrl
        }).success(function(data){
          deferred.resolve(data);
        }).error(function(){
          deferred.reject('There was an error')
        })
        return deferred.promise;
      },
      setArtist: function(artist){
        _artist = artist;
      },
      getArtist: function(){
        return _artist;
      },
      thingOnConfig: this.thingFromConfig
    }
  }
});

Jetzt sind setArtist, getArtist und callItunes genau wie in unserer Factory und in unserem Service in jedem Controller verfügbar, an den wir myProvider übergeben. Hier ist der myProvider-Controller (der fast genau mit unserem Werks- / Service-Controller identisch ist).

app.controller('myProviderCtrl', function($scope, myProvider){
  $scope.data = {};
  $scope.updateArtist = function(){
    myProvider.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myProvider.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }

  $scope.data.thingFromConfig = myProvider.thingOnConfig;
});

Wie bereits erwähnt, besteht der Sinn des Erstellens eines Dienstes mit Provider darin, einige Variablen über die Funktion app.config ändern zu können, bevor das endgültige Objekt an den Rest der Anwendung übergeben wird. Sehen wir uns ein Beispiel dafür an.

app.config(function(myProviderProvider){
  //Providers are the only service you can pass into app.config
  myProviderProvider.thingFromConfig = 'This sentence was set in app.config. Providers are the only service that can be passed into config. Check out the code to see how it works';
});

Jetzt können Sie sehen, wie 'thingFromConfig' in unserem Provider als leere Zeichenfolge ist, aber wenn dies im DOM angezeigt wird, lautet es 'Dieser Satz wurde gesetzt ...'.


11
Der einzige Teil, der in dieser hervorragenden Beschreibung fehlt, sind die relativen Vorteile der Nutzung des Dienstes gegenüber einer Fabrik. Dies wird in der akzeptierten Antwort von Lior
Unendlichkeit

2
FWIW (vielleicht nicht viel), hier ist ein Blogger, der Probleme
Barlop

3
Die Pointe der 'JavaScript-Gurus' war schlau. : DI denke, diese Antwort klärt die Dinge sehr. Hervorragend geschrieben.
Amarmishra

4
Ihr TLDR benötigt einen TLDR.
JensB

3
@JensB tl; dr - Learn React.
Tyler McGinnis

512

Alle Dienste sind Singletons ; Sie werden einmal pro App instanziiert. Sie können von jedem Typ sein , sei es ein Grundelement, ein Objektliteral, eine Funktion oder sogar eine Instanz eines benutzerdefinierten Typs.

Die value, factory, service, constant, und providerMethoden sind alle Anbieter. Sie bringen dem Injektor bei, wie die Dienste instanziiert werden.

Das ausführlichste, aber auch umfassendste ist ein Anbieterrezept. Die verbleibenden vier Rezepttypen - Wert, Fabrik, Service und Konstante - sind nur syntaktischer Zucker über einem Anbieterrezept .

  • Das Wertrezept ist der einfachste Fall, in dem Sie den Service selbst instanziieren und dem Injektor den instanziierten Wert bereitstellen .
  • Das Factory-Rezept gibt dem Injector eine Factory-Funktion, die er aufruft, wenn er den Service instanziieren muss. Beim Aufruf erstellt die Factory-Funktion die Dienstinstanz und gibt sie zurück. Die Abhängigkeiten des Dienstes werden als Argumente der Funktionen eingefügt. Die Verwendung dieses Rezepts fügt also die folgenden Fähigkeiten hinzu:
    • Fähigkeit, andere Dienste zu nutzen (Abhängigkeiten haben)
    • Service-Initialisierung
    • Verzögerte / verzögerte Initialisierung
  • Das Service-Rezept ist fast das gleiche wie das Factory-Rezept, aber hier ruft der Injector anstelle einer Factory-Funktion einen Konstruktor mit dem neuen Operator auf.
  • Das Provider-Rezept ist normalerweise übertrieben . Es fügt eine weitere Indirektionsebene hinzu, indem Sie die Erstellung der Factory konfigurieren können.

    Sie sollten das Provider-Rezept nur verwenden, wenn Sie eine API für die anwendungsweite Konfiguration verfügbar machen möchten, die vor dem Start der Anwendung vorgenommen werden muss. Dies ist normalerweise nur für wiederverwendbare Dienste interessant, deren Verhalten zwischen den Anwendungen möglicherweise geringfügig variieren muss.

  • Das Konstantenrezept ist genau wie das Wertrezept, außer dass Sie Dienste definieren können, die in der Konfigurationsphase verfügbar sind . Früher als Services, die mit dem Value-Rezept erstellt wurden. Im Gegensatz zu Werten können sie nicht mit dekoriert werdendecorator .
Siehe die Dokumentation des Anbieters .


2
Service und Fabrik sind also im Wesentlichen gleich? Die Verwendung einer der anderen bietet nichts anderes als alternative Syntax?
Matt

2
@Matt, ja, Service ist eine prägnante Methode, wenn Sie bereits über eine eigene Funktion verfügen, die Sie als Service verfügbar machen möchten. Aus Dokumenten: myApp.factory ('unicornLauncher', ["apiToken", Funktion (apiToken) {neuen UnicornLauncher (apiToken) zurückgeben;}]); vs: myApp.service ('unicornLauncher', ["apiToken", UnicornLauncher]);
Janek

5
@joshperry Als Neuling habe ich eine Weile den Unterschied zwischen Service und Fabrik gegoogelt. Ich bin damit einverstanden, dass dies die beste Antwort aller Zeiten ist! Ich würde Service als eine Serviceklasse (z. B. Encoder / Decoder-Klasse) verstehen, die möglicherweise einige private Eigenschaften hat. Die Fabrik bietet eine Reihe zustandsloser Hilfsmethoden.
Stanleyxu2005

3
Yaa-Beispiele in anderen Antworten oben erklären den Kernunterschied zwischen s / w-Diensten und -Anbietern nicht sehr deutlich. Dies wird zum Zeitpunkt der Instanziierung dieser Rezepte injiziert.
Ashish Singh

223

Grundlegendes zu AngularJS Factory, Service und Provider

All dies wird verwendet, um wiederverwendbare Singleton-Objekte gemeinsam zu nutzen. Es hilft, wiederverwendbaren Code für Ihre App / verschiedene Komponenten / Module freizugeben.

Vom Docs Service / Factory :

  • Faul instanziiert - Angular instanziiert einen Service / eine Factory nur dann, wenn eine Anwendungskomponente davon abhängt.
  • Singletons - Jede von einem Service abhängige Komponente erhält einen Verweis auf die einzelne Instanz, die von der Service Factory generiert wurde.

Fabrik

Eine Factory ist eine Funktion, bei der Sie Logik bearbeiten / hinzufügen können, bevor Sie ein Objekt erstellen. Anschließend wird das neu erstellte Objekt zurückgegeben.

app.factory('MyFactory', function() {
    var serviceObj = {};
    //creating an object with methods/functions or variables
    serviceObj.myFunction = function() {
        //TO DO:
    };
    //return that object
    return serviceObj;
});

Verwendungszweck

Es kann nur eine Sammlung von Funktionen wie eine Klasse sein. Daher kann es in verschiedenen Controllern instanziiert werden, wenn Sie es in Ihre Controller- / Factory- / Direktivenfunktionen einspeisen. Es wird nur einmal pro App instanziiert.

Bedienung

Denken Sie beim Betrachten der Dienste einfach an den Array-Prototyp. Ein Dienst ist eine Funktion, die ein neues Objekt mit dem Schlüsselwort 'new' instanziiert. Mit dem thisSchlüsselwort können Sie einem Serviceobjekt Eigenschaften und Funktionen hinzufügen . Im Gegensatz zu einer Factory wird nichts zurückgegeben (es wird ein Objekt zurückgegeben, das Methoden / Eigenschaften enthält).

app.service('MyService', function() {
    //directly binding events to this context
    this.myServiceFunction = function() {
        //TO DO:
    };
});

Verwendungszweck

Verwenden Sie diese Option, wenn Sie ein einzelnes Objekt in der gesamten Anwendung freigeben müssen. Zum Beispiel authentifizierte Benutzerdetails, gemeinsam nutzbare Methoden / Daten, Dienstprogrammfunktionen usw.

Anbieter

Ein Anbieter wird verwendet, um ein konfigurierbares Dienstobjekt zu erstellen. Sie können die Serviceeinstellung über die Konfigurationsfunktion konfigurieren. Mit der $get()Funktion wird ein Wert zurückgegeben . Die $getFunktion wird in der Laufphase im Winkel ausgeführt.

app.provider('configurableService', function() {
    var name = '';
    //this method can be be available at configuration time inside app.config.
    this.setName = function(newName) {
        name = newName;
    };
    this.$get = function() {
        var getName = function() {
             return name;
        };
        return {
            getName: getName //exposed object to where it gets injected.
        };
    };
});

Verwendungszweck

Wenn Sie eine modulweise Konfiguration für Ihr Serviceobjekt bereitstellen müssen, bevor Sie es verfügbar machen, z. Angenommen , Sie wie Ihre API URL auf Grundlage Ihrer Umgebung einstellen möchten dev, stageoderprod

HINWEIS

In der Konfigurationsphase von Angular ist nur ein Anbieter verfügbar, Service & Factory jedoch nicht.

Ich hoffe, dies hat Ihr Verständnis für Fabrik, Service und Anbieter geklärt .


1
Was würde ich tun, wenn ich einen Dienst mit einer bestimmten Schnittstelle, aber zwei unterschiedlichen Implementierungen haben und jede in einen Controller einspeisen möchte, der jedoch mithilfe eines UI-Routers an unterschiedliche Zustände gebunden ist? zB Fernanrufe in einem Zustand tätigen, aber stattdessen in einem anderen in den lokalen Speicher schreiben. Die Provider-Dokumente sagen zu verwenden only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications, klingt also nicht möglich, oder?
Qix

191

Für mich kam die Offenbarung, als mir klar wurde, dass sie alle auf die gleiche Weise funktionieren: indem sie etwas einmal ausführen, den Wert speichern, den sie erhalten, und dann denselben gespeicherten Wert abhusten, wenn sie durch Abhängigkeitsinjektion referenziert werden .

Sagen wir, wir haben:

app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);

Der Unterschied zwischen den drei ist:

  1. aDer gespeicherte Wert stammt aus dem Betrieb fn.
  2. bDer gespeicherte Wert stammt von newing fn.
  3. c‚s gespeicherten Wert kommt aus dem ersten Instanz immer durch newing fn, und dann eine laufende $getMethode der Instanz.

Das heißt, es gibt so etwas wie ein Cache-Objekt in AngularJS, dessen Wert jeder Injektion nur einmal zugewiesen wird, wenn sie das erste Mal injiziert wurden und wo:

cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()

Aus diesem Grund verwenden wir thisin Diensten und definieren einen this.$getin Anbietern.


2
Diese Antwort gefällt mir auch am besten. Der Zweck von allen besteht darin, bei Bedarf über DI Zugriff auf ein Objekt zu gewähren. Normalerweise geht es dir gut mit factorys. Der einzige Grund dafür servicesind Sprachen wie CoffeeScript, TypeScript, ES6 usw., sodass Sie deren Klassensyntax verwenden können. Sie benötigen providers nur, wenn Ihr Modul in mehreren Anwendungen mit unterschiedlichen Einstellungen verwendet wird app.config(). Ob Ihr Dienst ein reiner Singleton ist oder Instanzen von etwas erstellen kann, hängt nur von Ihrer Implementierung ab.
Andreas Linnert

137

Service gegen Anbieter gegen Fabrik:

Ich versuche es einfach zu halten. Es geht um das grundlegende JavaScript-Konzept.

Lassen Sie uns zunächst über Dienste in AngularJS sprechen !

Was ist Service: In AngularJS, Serviceist nichts anderes als ein Singleton-JavaScript-Objekt, in dem einige nützliche Methoden oder Eigenschaften gespeichert werden können. Dieses Singleton-Objekt wird auf ngApp-Basis (Angular App) erstellt und von allen Controllern in der aktuellen App gemeinsam genutzt. Wenn Angularjs ein Dienstobjekt instanziiert, registriert es dieses Dienstobjekt mit einem eindeutigen Dienstnamen. Jedes Mal, wenn wir eine Dienstinstanz benötigen, durchsucht Angular die Registrierung nach diesem Dienstnamen und gibt den Verweis auf das Dienstobjekt zurück. So können wir Methoden aufrufen, auf Eigenschaften usw. für das Serviceobjekt zugreifen. Möglicherweise haben Sie die Frage, ob Sie auch Eigenschaften und Methoden für das Bereichsobjekt von Controllern festlegen können! Warum brauchen Sie ein Serviceobjekt? Die Antwort lautet: Dienste werden von mehreren Controllern gemeinsam genutzt. Wenn Sie einige Eigenschaften / Methoden in das Bereichsobjekt eines Controllers einfügen, ist es nur für den aktuellen Bereich verfügbar.

Wenn es also drei Controller-Bereiche gibt, seien es ControllerA, ControllerB und ControllerC, die alle dieselbe Dienstinstanz verwenden.

<div ng-controller='controllerA'>
    <!-- controllerA scope -->
</div>
<div ng-controller='controllerB'>
    <!-- controllerB scope -->
</div>
<div ng-controller='controllerC'>
    <!-- controllerC scope -->
</div>

Wie erstelle ich einen Service?

AngularJS bietet verschiedene Methoden zum Registrieren eines Dienstes. Hier konzentrieren wir uns auf drei Methoden Fabrik (..), Service (..), Anbieter (..);

Verwenden Sie diesen Link als Code-Referenz

Werksfunktion:

Wir können eine Factory-Funktion wie folgt definieren.

factory('serviceName',function fnFactory(){ return serviceInstance;})

AngularJS bietet die Methode 'factory (' serviceName ', fnFactory)' , die zwei Parameter verwendet, serviceName und eine JavaScript-Funktion. Angular erstellt eine Dienstinstanz durch Aufrufen der Funktion fnFactory () wie unten.

var serviceInstace = fnFactory();

Die übergebene Funktion kann ein Objekt definieren und dieses Objekt zurückgeben. AngularJS speichert diese Objektreferenz einfach in einer Variablen, die als erstes Argument übergeben wird. Alles, was von fnFactory zurückgegeben wird, ist an serviceInstance gebunden. Anstatt ein Objekt zurückzugeben, können wir auch Funktionen, Werte usw. zurückgeben. Was auch immer wir zurückgeben, es steht der Dienstinstanz zur Verfügung.

Beispiel:

var app= angular.module('myApp', []);
//creating service using factory method
app.factory('factoryPattern',function(){
  var data={
    'firstName':'Tom',
    'lastName':' Cruise',
    greet: function(){
      console.log('hello!' + this.firstName + this.lastName);
    }
  };

  //Now all the properties and methods of data object will be available in our service object
  return data;
});

Servicefunktion:

service('serviceName',function fnServiceConstructor(){})

Auf die andere Weise können wir einen Dienst registrieren. Der einzige Unterschied besteht darin, wie AngularJS versucht, das Serviceobjekt zu instanziieren. Dieses Mal verwendet angle das Schlüsselwort 'new' und ruft die Konstruktorfunktion wie folgt auf.

var serviceInstance = new fnServiceConstructor();

In der Konstruktorfunktion können wir das Schlüsselwort 'this' verwenden, um dem Serviceobjekt Eigenschaften / Methoden hinzuzufügen. Beispiel:

//Creating a service using the service method
var app= angular.module('myApp', []);
app.service('servicePattern',function(){
  this.firstName ='James';
  this.lastName =' Bond';
  this.greet = function(){
    console.log('My Name is '+ this.firstName + this.lastName);
  };
});

Anbieterfunktion:

Die Funktion Provider () ist eine weitere Möglichkeit zum Erstellen von Diensten. Lassen Sie uns daran interessiert sein, einen Dienst zu erstellen, der dem Benutzer nur eine Begrüßungsnachricht anzeigt. Wir möchten aber auch eine Funktionalität bereitstellen, mit der der Benutzer seine eigene Begrüßungsnachricht festlegen kann. In technischer Hinsicht wollen wir konfigurierbare Dienste erstellen. Wie können wir das machen? Es muss einen Weg geben, damit die App ihre benutzerdefinierten Begrüßungsnachrichten weitergeben kann und Angularjs sie der Factory- / Konstruktorfunktion zur Verfügung stellt, die unsere Service-Instanz erstellt. In einem solchen Fall erledigt die Funktion provider () den Job. Mit der Funktion provider () können wir konfigurierbare Dienste erstellen.

Wir können konfigurierbare Dienste mithilfe der unten angegebenen Anbietersyntax erstellen.

/*step1:define a service */
app.provider('service',function serviceProviderConstructor(){});

/*step2:configure the service */
app.config(function configureService(serviceProvider){});

Wie funktioniert die Anbietersyntax intern?

1. Das Provider-Objekt wird mit der Konstruktorfunktion erstellt, die wir in unserer Provider-Funktion definiert haben.

var serviceProvider = new serviceProviderConstructor();

2.Die Funktion, die wir in app.config () übergeben haben, wird ausgeführt. Dies wird als Konfigurationsphase bezeichnet, und hier haben wir die Möglichkeit, unseren Service anzupassen.

configureService(serviceProvider);

3.Die endgültige Service-Instanz wird durch Aufrufen der $ get-Methode von serviceProvider erstellt.

serviceInstance = serviceProvider.$get()

Beispielcode zum Erstellen eines Dienstes mithilfe der Bereitstellungssyntax:

var app= angular.module('myApp', []);
app.provider('providerPattern',function providerConstructor(){
  //this function works as constructor function for provider
  this.firstName = 'Arnold ';
  this.lastName = ' Schwarzenegger' ;
  this.greetMessage = ' Welcome, This is default Greeting Message' ;
  //adding some method which we can call in app.config() function
  this.setGreetMsg = function(msg){
    if(msg){
      this.greetMessage =  msg ;
    }
  };

  //We can also add a method which can change firstName and lastName
  this.$get = function(){
    var firstName = this.firstName;
    var lastName = this.lastName ;
    var greetMessage = this.greetMessage;
    var data={
       greet: function(){
         console.log('hello, ' + firstName + lastName+'! '+ greetMessage);
       }
    };
    return data ;
  };
});

app.config(
  function(providerPatternProvider){
    providerPatternProvider.setGreetMsg(' How do you do ?');
  }
);

Arbeitsdemo

Zusammenfassung:


Factory verwendet eine Factory-Funktion, die eine Service-Instanz zurückgibt. serviceInstance = fnFactory ();

Der Dienst verwendet eine Konstruktorfunktion, und Angular ruft diese Konstruktorfunktion mit dem Schlüsselwort 'new' zum Erstellen der Dienstinstanz auf. serviceInstance = new fnServiceConstructor ();

Provider definiert eine providerConstructor-Funktion. Diese providerConstructor-Funktion definiert eine Factory-Funktion $ get . Angular ruft $ get () auf, um das Serviceobjekt zu erstellen. Die Anbietersyntax bietet den zusätzlichen Vorteil, dass das Serviceobjekt konfiguriert wird, bevor es instanziiert wird. serviceInstance = $ get ();



63

Fabrik

Wenn Sie AngularJS eine Funktion geben, wird AngularJS den Rückgabewert zwischenspeichern und einfügen, wenn die Factory angefordert wird.

Beispiel:

app.factory('factory', function() {
    var name = '';
    // Return value **is** the object that will be injected
    return {
        name: name;
    }
})

Verwendungszweck:

app.controller('ctrl', function($scope, factory) {
     $scope.name = factory.name;
});

Bedienung

Sie geben AngularJS eine Funktion, AngularJS ruft new auf, um sie zu instanziieren. Es ist die von AngularJS erstellte Instanz, die zwischengespeichert und injiziert wird, wenn der Dienst angefordert wird. Da new zum Instanziieren des Dienstes verwendet wurde, ist das Schlüsselwort this gültig und bezieht sich auf die Instanz.

Beispiel:

app.service('service', function() {
     var name = '';
     this.setName = function(newName) {
         name = newName;
     }
     this.getName = function() {
         return name;
     }
});

Verwendungszweck:

app.controller('ctrl', function($scope, service) {
   $scope.name = service.getName();
});

Anbieter

Sie geben AngularJS eine Funktion, und AngularJS ruft seine $getFunktion auf. Dies ist der Rückgabewert der $getFunktion, der zwischengespeichert und eingefügt wird, wenn der Dienst angefordert wird.

Mit Providern können Sie den Provider konfigurieren, bevor AngularJS die $getMethode zum Abrufen des Injectible aufruft.

Beispiel:

app.provider('provider', function() {
     var name = '';
     this.setName = function(newName) {
          name = newName;
     }
     this.$get = function() {
         return {
            name: name
         }
     }
})

Verwendung (als Injektionslösung in einer Steuerung)

app.controller('ctrl', function($scope, provider) {
    $scope.name = provider.name;
});

Verwendung (vorheriges Konfigurieren des Providers $getwird aufgerufen, um das Injectable zu erstellen)

app.config(function(providerProvider) {
    providerProvider.setName('John');
});

56

Beim Herumspielen mit Anbietern ist mir etwas Interessantes aufgefallen.

Die Sichtbarkeit von injizierbaren Substanzen ist für Anbieter anders als für Dienstleistungen und Fabriken. Wenn Sie eine AngularJS-Konstante deklarieren (z. B. myApp.constant('a', 'Robert');), können Sie sie in Services, Fabriken und Anbieter einfügen.

Wenn Sie jedoch einen AngularJS-Wert deklarieren (z. B. myApp.value('b', {name: 'Jones'});), können Sie ihn in Services und Fabriken einfügen, NICHT jedoch in die Funktion zum Erstellen von Anbietern. Sie können es jedoch in die $getFunktion einfügen, die Sie für Ihren Anbieter definieren. Dies wird in der AngularJS-Dokumentation erwähnt, ist aber leicht zu übersehen. Sie finden es auf der Seite% bereitstellen in den Abschnitten über die Wert- und Konstantenmethoden.

http://jsfiddle.net/R2Frv/1/

<div ng-app="MyAppName">
    <div ng-controller="MyCtrl">
        <p>from Service: {{servGreet}}</p>
        <p>from Provider: {{provGreet}}</p>
    </div>
</div>
<script>
    var myApp = angular.module('MyAppName', []);

    myApp.constant('a', 'Robert');
    myApp.value('b', {name: 'Jones'});

    myApp.service('greetService', function(a,b) {
        this.greeter = 'Hi there, ' + a + ' ' + b.name;
    });

    myApp.provider('greetProvider', function(a) {
        this.firstName = a;
        this.$get = function(b) {
            this.lastName = b.name;
            this.fullName = this.firstName + ' ' + this.lastName;
            return this;
        };
    });

    function MyCtrl($scope, greetService, greetProvider) {
        $scope.servGreet = greetService.greeter;
        $scope.provGreet = greetProvider.fullName;
    }
</script>

45

Dies ist ein sehr verwirrender Teil für Neulinge und ich habe versucht, ihn in einfachen Worten zu klären

AngularJS-Dienst: Wird zum Teilen von Dienstprogrammfunktionen mit der Dienstreferenz in der Steuerung verwendet. Der Dienst ist von Natur aus Singleton, sodass für einen Dienst nur eine Instanz im Browser erstellt wird und auf der gesamten Seite dieselbe Referenz verwendet wird.

Im Service erstellen wir mit diesem Objekt Funktionsnamen als Eigenschaft .

AngularJS Factory: Der Zweck von Factory ist der gleiche wie Service. In diesem Fall erstellen wir jedoch ein neues Objekt und fügen Funktionen als Eigenschaften dieses Objekts hinzu. Am Ende geben wir dieses Objekt zurück.

AngularJS-Provider: Der Zweck ist wieder der gleiche, jedoch gibt der Provider die Ausgabe seiner $ get-Funktion an.

Das Definieren und Verwenden von Service, Factory und Provider wird unter http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider erläutert


2
Fabrik und Anbieter sind auch Singleton-Objekt? Gibt es ein Scanrio, in dem Fabriken gegenüber Dienstleistungen empfohlen werden?
Sunil Garg

34

Für mich ist der beste und einfachste Weg, den Unterschied zu verstehen:

var service, factory;
service = factory = function(injection) {}

Wie AngularJS bestimmte Komponenten instanziiert (vereinfacht):

// service
var angularService = new service(injection);

// factory
var angularFactory = factory(injection);

Für den Dienst wird die AngularJS-Komponente die Objektinstanz der Klasse, die durch die Dienstdeklarationsfunktion dargestellt wird. Für die Fabrik ist es das Ergebnis, das von der Fabrikdeklarationsfunktion zurückgegeben wird. Die Fabrik verhält sich möglicherweise genauso wie der Service:

var factoryAsService = function(injection) {
  return new function(injection) {
    // Service content
  }
}

Die einfachste Denkweise ist die folgende:

  • Service ist eine Singleton-Objektinstanz. Verwenden Sie Dienste, wenn Sie ein Singleton-Objekt für Ihren Code bereitstellen möchten.
  • Fabrik ist eine Klasse. Verwenden Sie Fabriken, wenn Sie benutzerdefinierte Klassen für Ihren Code bereitstellen möchten (dies ist mit Diensten nicht möglich, da diese bereits instanziiert sind).

Das Beispiel für die Factory-Klasse sowie die Unterschiede zwischen den Anbietern finden Sie in den Kommentaren.


Wie kann ein Dienst ein Singleton sein, wenn er bei jeder Verwendung instanziiert wird? Ich kann mich
darum

Der Service wird während der Auflösung von Abhängigkeiten nur einmal instanziiert. Wenn Sie dann vom Injektor nach einem Service fragen, erhalten Sie immer dieselbe Instanz. Es kann hier leicht überprüft werden: jsfiddle.net/l0co/sovtu55t/1 , bitte führen Sie es mit der Konsole aus. Die Konsole zeigt an, dass der Dienst nur einmal instanziiert wird.
Lukasz Frankowski

Oh, ich verstehe. Ich hatte erwartet, buchstäblich in der Lage zu sein new MyService()oder so :)
Joe

33

Meine Klarstellung zu diesem Thema:

Grundsätzlich erstellen und konfigurieren alle genannten Typen (Service, Factory, Provider usw.) nur globale Variablen (die natürlich für die gesamte Anwendung global sind), so wie es altmodische globale Variablen waren.

Obwohl globale Variablen nicht empfohlen werden, besteht die tatsächliche Verwendung dieser globalen Variablen darin, eine Abhängigkeitsinjektion bereitzustellen , indem die Variable an den entsprechenden Controller übergeben wird.

Es gibt viele Komplikationen bei der Erstellung der Werte für die "globalen Variablen":

  1. Konstante
    Dies definiert eine tatsächliche Konstante, die während der gesamten Anwendung nicht geändert werden sollte, genau wie Konstanten in anderen Sprachen (etwas, das JavaScript fehlt).
  2. Wert
    Dies ist ein veränderbarer Wert oder ein Objekt, das als globale Variable dient und sogar beim Erstellen anderer Dienste oder Fabriken eingefügt werden kann (siehe weiter unten). Es muss jedoch ein " Literalwert " sein, was bedeutet, dass man den tatsächlichen Wert ausschreiben muss und keine Berechnungs- oder Programmierlogik verwenden kann (mit anderen Worten, 39 oder myText oder {prop: "value"} sind OK, aber 2 +2 ist nicht).
  3. Fabrik
    Ein allgemeinerer Wert, der sofort berechnet werden kann. Es funktioniert, indem eine Funktion mit der zur Berechnung des Werts erforderlichen Logik an AngularJS übergeben wird und AngularJS sie ausführt und den Rückgabewert in der benannten Variablen speichert.
    Beachten Sie, dass es möglich ist, ein Objekt (in diesem Fall funktioniert es ähnlich wie ein Dienst ) oder eine Funktion (die in der Variablen als Rückruffunktion gespeichert wird) zurückzugeben.
  4. Service
    Ein Service ist eine abgespeckte Version der Factory, die nur gültig ist, wenn der Wert ein Objekt ist. Sie ermöglicht das Schreiben einer beliebigen Logik direkt in die Funktion (als wäre es ein Konstruktor) sowie das Deklarieren und Zugreifen die Objekteigenschaften mit dem Schlüsselwort this .
  5. Anbieter
    Im Gegensatz zu einem Dienst, bei dem es sich um eine vereinfachte Version der Factory handelt , ist ein Anbieter eine komplexere, aber flexiblere Methode zum Initialisieren der "globalen" Variablen, wobei die größte Flexibilität darin besteht, Werte aus der app.config festzulegen.
    Es funktioniert wie die Verwendung einer Kombination aus Dienst und Anbieter , indem eine Funktion an den Anbieter übergeben wird, deren Eigenschaften mit dem Schlüsselwort this deklariert wurden, das über das verwendet werden kann app.config.
    Dann muss es eine separate $ .get- Funktion geben, die von AngularJS ausgeführt wird, nachdem die obigen Eigenschaften über die app.configDatei festgelegt wurden, und diese $ .get- Funktion verhält sich genauso wie die Factory oben, indem sein Rückgabewert verwendet wird, um die "globalen" Variablen zu initialisieren.

26

Mein Verständnis ist unten sehr einfach.

Fabrik: Sie erstellen einfach ein Objekt innerhalb der Fabrik und geben es zurück.

Bedienung:

Sie haben nur eine Standardfunktion, die dieses Schlüsselwort verwendet, um eine Funktion zu definieren.

Anbieter:

Es gibt ein $getObjekt, das Sie definieren, und es kann verwendet werden, um das Objekt abzurufen, das Daten zurückgibt.


Haben Sie die Fabrik und den Service nicht verwechselt? Services schafft, wo die Fabrik zurückkehrt.
Flavien Volken

Wenn Sie den Dienstnamen als injizierbares Argument deklarieren, erhalten Sie eine Instanz der Funktion. Mit anderen Worten, neuer FunctionYouPassedToService (). Diese Objektinstanz wird zum Serviceobjekt, das AngularJS registriert und bei Bedarf später an andere Services / Controller weiterleitet. // factory Wenn Sie factoryname als injizierbares Argument deklarieren, erhalten Sie den Wert, der durch Aufrufen der an module.factory übergebenen Funktionsreferenz zurückgegeben wird.
Sajan

Okay, also ... im Winkel ist die Fabrik ein Singleton, in dem der "Service" tatsächlich eine Fabrik ist (in gängigen Designmustern)
Flavien Volken

25

Zusammenfassung aus Angular-Dokumenten :

  • Es gibt fünf Rezepttypen, die definieren, wie Objekte erstellt werden: Wert , Fabrik , Dienst , Anbieter und Konstante .
  • Fabrik und Service sind die am häufigsten verwendeten Rezepte. Der einzige Unterschied besteht darin, dass das Service- Rezept für Objekte eines benutzerdefinierten Typs besser funktioniert, während die Factory JavaScript-Grundelemente und -Funktionen erstellen kann.
  • Das Provider- Rezept ist der Kernrezepttyp und alle anderen sind nur syntaktischer Zucker.
  • Anbieter ist der komplexeste Rezepttyp. Sie benötigen es nur, wenn Sie einen wiederverwendbaren Code erstellen, der global konfiguriert werden muss.

Geben Sie hier die Bildbeschreibung ein


Beste Antworten von SO:

https://stackoverflow.com/a/26924234/165673 (<- GUT) https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673


20

Alle guten Antworten schon. Ich möchte noch ein paar Punkte zu Service und Fabrik hinzufügen . Zusammen mit dem Unterschied zwischen Service / Fabrik. Und man kann auch Fragen haben wie:

  1. Soll ich Service oder Fabrik benutzen? Was ist der Unterschied?
  2. Tun sie dasselbe oder haben sie dasselbe Verhalten?

Beginnen wir mit dem Unterschied zwischen Service und Werk:

  1. Beide sind Singletons : Immer wenn Angular diese zum ersten Mal als Abhängigkeit findet, wird eine einzelne Instanz von Service / Factory erstellt. Sobald die Instanz erstellt wurde, wird dieselbe Instanz für immer verwendet.

  2. Kann verwendet werden, um ein Objekt mit Verhalten zu modellieren : Beide können Methoden, interne Statusvariablen usw. haben. Die Art und Weise, wie Sie diesen Code schreiben, ist jedoch unterschiedlich.

Dienstleistungen:

Ein Service ist eine Konstruktorfunktion, die Angular durch Aufrufen von new instanziiert yourServiceName(). Dies bedeutet ein paar Dinge.

  1. Funktionen und Instanzvariablen sind Eigenschaften von this.
  2. Sie müssen keinen Wert zurückgeben. Wenn Angular aufruft new yourServiceName(, erhält es das thisObjekt mit allen Eigenschaften, die Sie darauf setzen.

Beispielbeispiel:

angular.service('MyService', function() {
  this.aServiceVariable = "Ved Prakash"
  this.aServiceMethod = function() {
    return //code
  };
});

Wenn Angular diesen MyServiceDienst in einen davon abhängigen Controller MyServiceeinfügt, erhält dieser Controller einen , auf dem er Funktionen aufrufen kann, z. B. MyService.aServiceMethod ().

Seien Sie vorsichtig mitthis :

Da der erstellte Dienst ein Objekt ist, können die darin enthaltenen Methoden beim Aufruf auf Folgendes verweisen:

angular.service('ScoreKeeper', function($http) {
  this.score = 0;

  this.getScore = function() {
    return this.score;
  };

  this.setScore = function(newScore) {
    this.score = newScore;
  };

  this.addOne = function() {
    this.score++;
  };
});

Sie könnten versucht sein, ScoreKeeper.setScoreeine Versprechenskette aufzurufen , wenn Sie beispielsweise die Punktzahl initialisiert haben, indem Sie sie vom Server abgerufen haben : $http.get('/score').then(ScoreKeeper.setScore).Das Problem dabei ist, dass ScoreKeeper.setScoresie mit thisgebunden an aufgerufen wird nullund Sie Fehler erhalten. Der bessere Weg wäre $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper)). Ob Sie dies für Ihre Servicemethoden verwenden oder nicht, achten Sie darauf, wie Sie sie aufrufen.

Rückgabe eines Wertes von aService :

Aufgrund der Funktionsweise von JavaScript-Konstruktoren erhält der Aufrufer dieses Objekt anstelle dieser Instanz , wenn Sie einen komplexen Wert (i.e., an Object)von einer constructorFunktion zurückgeben.

Dies bedeutet , dass Sie im Grunde das Werk Beispiel von unten copy-paste, ersetzen factorymit service, und es wird funktionieren:

angular.service('MyService', function($http) {
  var api = {};

  api.aServiceMethod= function() {
    return $http.get('/users');
  };
  return api;
});

Wenn Angular Ihren Dienst mit neuem MyService () erstellt, erhält es dieses API-Objekt anstelle der MyService-Instanz.

Dies ist das Verhalten für komplexe Werte (Objekte, Funktionen), jedoch nicht für primitive Typen.

Fabriken:

Eine Factory ist eine einfache alte Funktion, die einen Wert zurückgibt. Der Rückgabewert ist das, was in Dinge injiziert wird, die von der Fabrik abhängen. Ein typisches Factory-Muster in Angular besteht darin, ein Objekt mit Funktionen als Eigenschaften wie folgt zurückzugeben:

angular.factory('MyFactory', function($http) {
  var api = {};

  api.aFactoryMethod= function() {
    return $http.get('/users');
  };

  return api;
});

Der injizierte Wert für eine Factory-Abhängigkeit ist der Rückgabewert der Factory und muss kein Objekt sein. Es könnte eine Funktion sein

Antworten auf die obigen Fragen 1 und 2:

Meistens bleiben Sie einfach bei der Verwendung von Fabriken für alles. Ihr Verhalten ist leichter zu verstehen. Es gibt keine Wahl, ob ein Wert zurückgegeben werden soll oder nicht, und außerdem müssen keine Fehler eingeführt werden, wenn Sie das Falsche tun.

Ich bezeichne sie immer noch als "Dienste", wenn ich davon spreche, sie als Abhängigkeiten zu injizieren.

Das Service- / Fabrikverhalten ist sehr ähnlich, und einige Leute werden sagen, dass beide in Ordnung sind. Das ist etwas wahr, aber ich finde es einfacher, den Ratschlägen von John Papas Styleguide zu folgen und einfach bei den Fabriken zu bleiben. **


16

Eine zusätzliche Klarstellung ist, dass Fabriken Funktionen / Grundelemente erstellen können, während Services dies nicht können. Schau dir das an jsFiddle an, die auf Epokks basiert: http://jsfiddle.net/skeller88/PxdSP/1351/ .

Die Factory gibt eine Funktion zurück, die aufgerufen werden kann:

myApp.factory('helloWorldFromFactory', function() {
  return function() {
    return "Hello, World!";
  };
});

Die Factory kann auch ein Objekt mit einer Methode zurückgeben, die aufgerufen werden kann:

myApp.factory('helloWorldFromFactory', function() {
  return {
    sayHello: function() {
      return "Hello, World!";
    }
  };
});

Der Dienst gibt ein Objekt mit einer Methode zurück, die aufgerufen werden kann:

myApp.service('helloWorldFromService', function() {
  this.sayHello = function() {
     return "Hello, World!";
  };
});

Weitere Informationen finden Sie in einem Beitrag, den ich über den Unterschied geschrieben habe: http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/


16

Es gibt bereits gute Antworten, aber ich möchte diese nur teilen.

Zuallererst: Provider ist der Weg / das Rezept, um ein zu erstellenservice (Singleton-Objekt) zu erstellen, das von $ injizier injiziert werden soll (wie AngulaJS mit dem IoC-Muster umgeht).

Und Wert, Fabrik, Service und Konstante (4 Wege) - der syntaktische Zucker über Provider Weg / Recepie.

Es wurde ein Service vs FactoryTeil behandelt: https://www.youtube.com/watch?v=BLzNCkPn3ao

Beim Servicenew dreht sich eigentlich alles um Schlüsselwörter, die bekanntlich vier Dinge bewirken:

  1. erstellt ein brandneues Objekt
  2. verknüpft es mit seinem prototypeObjekt
  3. verbindet sich contextmitthis
  4. und kehrt zurück this

Und Factory dreht sich alles um Factory Pattern - enthält Funktionen, die Objekte wie diesen Service zurückgeben.

  1. Fähigkeit, andere Dienste zu nutzen (Abhängigkeiten haben)
  2. Service-Initialisierung
  3. verzögerte / verzögerte Initialisierung

Und dieses einfache / kurze Video: deckt auch den Anbieter ab : https://www.youtube.com/watch?v=HvTZbQ_hUZY (dort sehen Sie, wie sie von der Fabrik zum Anbieter gehen)

Das Anbieterrezept wird hauptsächlich in der App-Konfiguration verwendet, bevor die App vollständig gestartet / initialisiert wurde.


14

Nachdem ich all diese Beiträge gelesen hatte, war das für mich verwirrender. Aber dennoch sind alle Informationen wertvoll. Schließlich fand ich die folgende Tabelle, die Informationen mit einfachem Vergleich liefert

  • Der Injektor verwendet Rezepte, um zwei Arten von Objekten zu erstellen: Dienste und Spezialobjekte
  • Es gibt fünf Rezepttypen, die definieren, wie Objekte erstellt werden: Wert, Fabrik, Dienst, Anbieter und Konstante.
  • Fabrik und Service sind die am häufigsten verwendeten Rezepte. Der einzige Unterschied besteht darin, dass das Service-Rezept für Objekte eines benutzerdefinierten Typs besser funktioniert, während die Factory JavaScript-Grundelemente und -Funktionen erstellen kann.
  • Das Provider-Rezept ist der Kernrezepttyp und alle anderen sind nur syntaktischer Zucker.
  • Anbieter ist der komplexeste Rezepttyp. Sie benötigen es nur, wenn Sie einen wiederverwendbaren Code erstellen, der global konfiguriert werden muss.
  • Alle Spezialobjekte mit Ausnahme des Controllers werden über Werksrezepte definiert.

Geben Sie hier die Bildbeschreibung ein

Und für Anfänger verstehen: - Dies kann den Anwendungsfall nicht korrigieren, aber auf hoher Ebene ist dies der Anwendungsfall für diese drei.

  1. Wenn Sie in Winkelmodul verwenden möchten, sollte die Konfigurationsfunktion als Provider erstellt werden

angular.module('myApp').config(function($testProvider){
$testProvider.someFunction();
})

  1. Ajax-Anruf- oder Drittanbieter-Integrationen müssen Service sein .
  2. Für Datenmanipulationen erstellen Sie es als Factory

Für grundlegende Szenarien verhält sich Factory & Service gleich.


13

Hier ist ein Broilerplate-Code, den ich als Code-Vorlage für die Objektfabrik in AngularjS entwickelt habe. Ich habe ein Auto / eine CarFactory als Beispiel verwendet, um dies zu veranschaulichen. Ermöglicht einfachen Implementierungscode in der Steuerung.

     <script>
        angular.module('app', [])
            .factory('CarFactory', function() {

                /**
                 * BroilerPlate Object Instance Factory Definition / Example
                 */
                this.Car = function() {

                    // initialize instance properties
                    angular.extend(this, {
                        color           : null,
                        numberOfDoors   : null,
                        hasFancyRadio   : null,
                        hasLeatherSeats : null
                    });

                    // generic setter (with optional default value)
                    this.set = function(key, value, defaultValue, allowUndefined) {

                        // by default,
                        if (typeof allowUndefined === 'undefined') {
                            // we don't allow setter to accept "undefined" as a value
                            allowUndefined = false;
                        }
                        // if we do not allow undefined values, and..
                        if (!allowUndefined) {
                            // if an undefined value was passed in
                            if (value === undefined) {
                                // and a default value was specified
                                if (defaultValue !== undefined) {
                                    // use the specified default value
                                    value = defaultValue;
                                } else {
                                    // otherwise use the class.prototype.defaults value
                                    value = this.defaults[key];
                                } // end if/else
                            } // end if
                        } // end if

                        // update 
                        this[key] = value;

                        // return reference to this object (fluent)
                        return this;

                    }; // end this.set()

                }; // end this.Car class definition

                // instance properties default values
                this.Car.prototype.defaults = {
                    color: 'yellow',
                    numberOfDoors: 2,
                    hasLeatherSeats: null,
                    hasFancyRadio: false
                };

                // instance factory method / constructor
                this.Car.prototype.instance = function(params) {
                    return new 
                        this.constructor()
                                .set('color',           params.color)
                                .set('numberOfDoors',   params.numberOfDoors)
                                .set('hasFancyRadio',   params.hasFancyRadio)
                                .set('hasLeatherSeats', params.hasLeatherSeats)
                    ;
                };

                return new this.Car();

            }) // end Factory Definition
            .controller('testCtrl', function($scope, CarFactory) {

                window.testCtrl = $scope;

                // first car, is red, uses class default for:
                // numberOfDoors, and hasLeatherSeats
                $scope.car1     = CarFactory
                                    .instance({
                                        color: 'red'
                                    })
                                ;

                // second car, is blue, has 3 doors, 
                // uses class default for hasLeatherSeats
                $scope.car2     = CarFactory
                                    .instance({
                                        color: 'blue',
                                        numberOfDoors: 3
                                    })
                                ;
                // third car, has 4 doors, uses class default for 
                // color and hasLeatherSeats
                $scope.car3     = CarFactory
                                    .instance({
                                        numberOfDoors: 4
                                    })
                                ;
                // sets an undefined variable for 'hasFancyRadio',
                // explicitly defines "true" as default when value is undefined
                $scope.hasFancyRadio = undefined;
                $scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);

                // fourth car, purple, 4 doors,
                // uses class default for hasLeatherSeats
                $scope.car4     = CarFactory
                                    .instance({
                                        color: 'purple',
                                        numberOfDoors: 4
                                    });
                // and then explicitly sets hasLeatherSeats to undefined
                $scope.hasLeatherSeats = undefined;
                $scope.car4.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);

                // in console, type window.testCtrl to see the resulting objects

            });
    </script>

Hier ist ein einfacheres Beispiel. Ich verwende einige Bibliotheken von Drittanbietern, die ein "Position" -Objekt erwarten, das Breiten- und Längengrade anzeigt, jedoch über unterschiedliche Objekteigenschaften. Ich wollte den Herstellercode nicht hacken, also habe ich die "Position" -Objekte angepasst, die ich herumgereicht habe.

    angular.module('app')
.factory('PositionFactory', function() {

    /**
     * BroilerPlate Object Instance Factory Definition / Example
     */
    this.Position = function() {

        // initialize instance properties 
        // (multiple properties to satisfy multiple external interface contracts)
        angular.extend(this, {
            lat         : null,
            lon         : null,
            latitude    : null,
            longitude   : null,
            coords: {
                latitude: null,
                longitude: null
            }
        });

        this.setLatitude = function(latitude) {
            this.latitude           = latitude;
            this.lat                = latitude;
            this.coords.latitude    = latitude;
            return this;
        };
        this.setLongitude = function(longitude) {
            this.longitude          = longitude;
            this.lon                = longitude;
            this.coords.longitude   = longitude;
            return this;
        };

    }; // end class definition

    // instance factory method / constructor
    this.Position.prototype.instance = function(params) {
        return new 
            this.constructor()
                    .setLatitude(params.latitude)
                    .setLongitude(params.longitude)
        ;
    };

    return new this.Position();

}) // end Factory Definition

.controller('testCtrl', function($scope, PositionFactory) {
    $scope.position1 = PositionFactory.instance({latitude: 39, longitude: 42.3123});
    $scope.position2 = PositionFactory.instance({latitude: 39, longitude: 42.3333});
}) // end controller

;;


12

Verwenden Sie als Referenz diese Seite und die Dokumentation (die sich seit meinem letzten Blick stark verbessert zu haben scheint) habe ich die folgende reale (-ish) Weltdemo zusammengestellt, die 4 der 5 Varianten des Anbieters verwendet. Wert, Konstante, Fabrik und ausgewachsener Anbieter.

HTML:

<div ng-controller="mainCtrl as main">
    <h1>{{main.title}}*</h1>
    <h2>{{main.strapline}}</h2>
    <p>Earn {{main.earn}} per click</p>
    <p>You've earned {{main.earned}} by clicking!</p>
    <button ng-click="main.handleClick()">Click me to earn</button>
    <small>* Not actual money</small>
</div>

App

var app = angular.module('angularProviders', []);

// A CONSTANT is not going to change
app.constant('range', 100);

// A VALUE could change, but probably / typically doesn't
app.value('title', 'Earn money by clicking');
app.value('strapline', 'Adventures in ng Providers');

// A simple FACTORY allows us to compute a value @ runtime.
// Furthermore, it can have other dependencies injected into it such
// as our range constant.
app.factory('random', function randomFactory(range) {
    // Get a random number within the range defined in our CONSTANT
    return Math.random() * range;
});

// A PROVIDER, must return a custom type which implements the functionality 
// provided by our service (see what I did there?).
// Here we define the constructor for the custom type the PROVIDER below will 
// instantiate and return.
var Money = function(locale) {

    // Depending on locale string set during config phase, we'll
    // use different symbols and positioning for any values we 
    // need to display as currency
    this.settings = {
        uk: {
            front: true,
            currency: '£',
            thousand: ',',
            decimal: '.'
        },
        eu: {
            front: false,
            currency: '€',
            thousand: '.',
            decimal: ','
        }
    };

    this.locale = locale;
};

// Return a monetary value with currency symbol and placement, and decimal 
// and thousand delimiters according to the locale set in the config phase.
Money.prototype.convertValue = function(value) {

    var settings = this.settings[this.locale],
        decimalIndex, converted;

    converted = this.addThousandSeparator(value.toFixed(2), settings.thousand);

    decimalIndex = converted.length - 3;

    converted = converted.substr(0, decimalIndex) +
        settings.decimal +
        converted.substr(decimalIndex + 1);    

    converted = settings.front ?
            settings.currency + converted : 
            converted + settings.currency; 

    return converted;   
};

// Add supplied thousand separator to supplied value
Money.prototype.addThousandSeparator = function(value, symbol) {
   return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, symbol);
};

// PROVIDER is the core recipe type - VALUE, CONSTANT, SERVICE & FACTORY
// are all effectively syntactic sugar built on top of the PROVIDER construct
// One of the advantages of the PROVIDER is that we can configure it before the
// application starts (see config below).
app.provider('money', function MoneyProvider() {

    var locale;

    // Function called by the config to set up the provider
    this.setLocale = function(value) {
        locale = value;   
    };

    // All providers need to implement a $get method which returns
    // an instance of the custom class which constitutes the service
    this.$get = function moneyFactory() {
        return new Money(locale);
    };
});

// We can configure a PROVIDER on application initialisation.
app.config(['moneyProvider', function(moneyProvider) {
    moneyProvider.setLocale('uk');
    //moneyProvider.setLocale('eu'); 
}]);

// The ubiquitous controller
app.controller('mainCtrl', function($scope, title, strapline, random, money) {

    // Plain old VALUE(s)
    this.title = title;
    this.strapline = strapline;

    this.count = 0;

    // Compute values using our money provider    
    this.earn = money.convertValue(random); // random is computed @ runtime
    this.earned = money.convertValue(0);

    this.handleClick = function() { 
        this.count ++;
        this.earned = money.convertValue(random * this.count);
    };
});

Arbeits Demo .


12

Diese Antwort befasst sich mit dem Thema / der Frage

Wie Fabrik, Service und Konstante - sind nur syntaktischer Zucker über einem Anbieterrezept?

ODER

wie Fabrik, Service und Anbieter intern ähnlich sind

im grunde was passiert ist

Wenn Sie ein factory()it-Set erstellen, functiondas Sie im zweiten Argument an den Provider angegeben haben, $getund es zurückgeben ( provider(name, {$get:factoryFn })), erhalten Sie nur, provideraber es gibt keine andere Eigenschaft / Methode als$get diese provider(bedeutet, dass Sie dies nicht konfigurieren können).

Quellcode der Fabrik

function factory(name, factoryFn, enforce) {
    return provider(name, {
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
    });
};

Wenn Sie ein service()it zurückgeben, geben Sie einer factory () ein function, das das injiziert constructor(geben Sie die Instanz des Konstruktors zurück, den Sie in Ihrem Service angegeben haben) und geben Sie es zurück

Quellcode des Dienstes

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
};

Im Grunde genommen erhalten Sie in beiden Fällen irgendwann einen Anbieter, der auf Ihre von Ihnen bereitgestellte Funktion eingestellt wird, aber Sie können alles andere als $ get angeben, wie Sie es ursprünglich in provider () für den Konfigurationsblock angegeben haben


11

Ich kenne viele ausgezeichnete Antworten, aber ich muss meine Erfahrungen mit der Verwendung von
1. servicefür die meisten Fälle von Standard
2. teilen, factorydie zum Erstellen des Dienstes für diese bestimmte Instanz verwendet werden

// factory.js ////////////////////////////
(function() {
'use strict';
angular
    .module('myApp.services')
    .factory('xFactory', xFactoryImp);
xFactoryImp.$inject = ['$http'];

function xFactoryImp($http) {
    var fac = function (params) {
        this._params = params; // used for query params
    };

    fac.prototype.nextPage = function () {
        var url = "/_prc";

        $http.get(url, {params: this._params}).success(function(data){ ...
    }
    return fac;
}
})();

// service.js //////////////////////////
(function() {
'use strict';
angular
    .module('myApp.services')
    .service('xService', xServiceImp);
xServiceImp.$inject = ['$http'];

function xServiceImp($http) {  
    this._params = {'model': 'account','mode': 'list'};

    this.nextPage = function () {
        var url = "/_prc";

        $http.get(url, {params: this._params}).success(function(data){ ...
    }       
}
})();

und mit:

controller: ['xFactory', 'xService', function(xFactory, xService){

        // books = new instance of xFactory for query 'book' model
        var books = new xFactory({'model': 'book', 'mode': 'list'});

        // accounts = new instance of xFactory for query 'accounts' model
        var accounts = new xFactory({'model': 'account', 'mode': 'list'});

        // accounts2 = accounts variable
        var accounts2 = xService;
... 

10

Etwas spät zur Party. Ich fand dies jedoch hilfreicher für diejenigen, die mehr über die Entwicklung von Angular JS Custom Services mithilfe von Factory-, Service- und Provider-Methoden erfahren möchten (oder Klarheit darüber haben möchten).

Ich bin auf dieses Video gestoßen, in dem die Methoden von Factory, Service und Provider für die Entwicklung von AngularJS Custom Services klar erläutert werden:

https://www.youtube.com/watch?v=oUXku28ex-M

Quellcode: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service

Der hier veröffentlichte Code wird direkt aus der oben genannten Quelle kopiert, um den Lesern zu helfen.

Der Code für den "werkseitigen" benutzerdefinierten Dienst lautet wie folgt (der sowohl für Synchronisierungs- als auch für Asynchronisierungsversionen zusammen mit dem Aufruf des http-Dienstes gilt):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcFactory',
  function($scope, calcFactory) {
    $scope.a = 10;
    $scope.b = 20;

    $scope.doSum = function() {
      //$scope.sum = calcFactory.getSum($scope.a, $scope.b); //synchronous
      calcFactory.getSum($scope.a, $scope.b, function(r) { //aynchronous
        $scope.sum = r;
      });
    };

  }
]);

app.factory('calcFactory', ['$http', '$log',
  function($http, $log) {
    $log.log("instantiating calcFactory..");
    var oCalcService = {};

    //oCalcService.getSum = function(a,b){
    //	return parseInt(a) + parseInt(b);
    //};

    //oCalcService.getSum = function(a, b, cb){
    //	var s = parseInt(a) + parseInt(b);
    //	cb(s);
    //};

    oCalcService.getSum = function(a, b, cb) { //using http service

      $http({
        url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
        method: 'GET'
      }).then(function(resp) {
        $log.log(resp.data);
        cb(resp.data);
      }, function(resp) {
        $log.error("ERROR occurred");
      });
    };

    return oCalcService;
  }
]);

Der Code für die "Service" -Methode für benutzerdefinierte Services (dies ist ziemlich ähnlich zu "Factory", unterscheidet sich jedoch vom Standpunkt der Syntax):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.service('calcService', ['$http', '$log', function($http, $log){
	$log.log("instantiating calcService..");
	
	//this.getSum = function(a,b){
	//	return parseInt(a) + parseInt(b);
	//};

	//this.getSum = function(a, b, cb){
	//	var s = parseInt(a) + parseInt(b);
	//	cb(s);
	//};

	this.getSum = function(a, b, cb){
		$http({
			url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
			method: 'GET'
		}).then(function(resp){
			$log.log(resp.data);
			cb(resp.data);
		},function(resp){
			$log.error("ERROR occurred");
		});
	};

}]);

Der Code für die "Provider" -Methode für benutzerdefinierte Dienste (dies ist erforderlich, wenn Sie einen Dienst entwickeln möchten, der konfiguriert werden kann):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.provider('calcService', function(){

	var baseUrl = '';

	this.config = function(url){
		baseUrl = url;
	};

	this.$get = ['$log', '$http', function($log, $http){
		$log.log("instantiating calcService...")
		var oCalcService = {};

		//oCalcService.getSum = function(a,b){
		//	return parseInt(a) + parseInt(b);
		//};

		//oCalcService.getSum = function(a, b, cb){
		//	var s = parseInt(a) + parseInt(b);
		//	cb(s);	
		//};

		oCalcService.getSum = function(a, b, cb){

			$http({
				url: baseUrl + '/Sum?a=' + a + '&b=' + b,
				method: 'GET'
			}).then(function(resp){
				$log.log(resp.data);
				cb(resp.data);
			},function(resp){
				$log.error("ERROR occurred");
			});
		};		

		return oCalcService;
	}];

});

app.config(['calcServiceProvider', function(calcServiceProvider){
	calcServiceProvider.config("http://localhost:4467");
}]);

Schließlich die Benutzeroberfläche, die mit einem der oben genannten Dienste funktioniert:

<html>
<head>
	<title></title>
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js" ></script>
	<script type="text/javascript" src="t03.js"></script>
</head>
<body ng-app="app">
	<div ng-controller="emp">
		<div>
			Value of a is {{a}},
			but you can change
			<input type=text ng-model="a" /> <br>

			Value of b is {{b}},
			but you can change
			<input type=text ng-model="b" /> <br>

		</div>
		Sum = {{sum}}<br>
		<button ng-click="doSum()">Calculate</button>
	</div>
</body>
</html>


10

Zur Verdeutlichung können Sie aus der AngularJS-Quelle sehen, dass ein Dienst nur die Factory-Funktion aufruft, die wiederum die Provider-Funktion aufruft:

function factory(name, factoryFn) { 
    return provider(name, { $get: factoryFn }); 
}

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
}

9

Lassen Sie uns die drei Möglichkeiten des Umgangs mit Geschäftslogik in AngularJS auf einfache Weise diskutieren: ( Inspiriert von Yaakovs Coursera AngularJS-Kurs )

BEDIENUNG :

Syntax:

app.js.

 var app = angular.module('ServiceExample',[]);
 var serviceExampleController =
              app.controller('ServiceExampleController', ServiceExampleController);
 var serviceExample = app.service('NameOfTheService', NameOfTheService);

 ServiceExampleController.$inject = ['NameOfTheService'] //protects from minification of js files

function ServiceExampleController(NameOfTheService){
     serviceExampleController = this;
     serviceExampleController.data = NameOfTheService.getSomeData();
 }

function NameOfTheService(){
     nameOfTheService = this;
     nameOfTheService.data = "Some Data";
     nameOfTheService.getSomeData = function(){
           return nameOfTheService.data;
     }     
}

index.html

<div ng-controller = "ServiceExampleController as serviceExample">
   {{serviceExample.data}}
</div>

Merkmale des Dienstes:

  1. Faul instanziiert : Wenn es nicht injiziert wird, wird es niemals instanziiert. Um es zu verwenden, muss es in ein Modul injiziert werden.
  2. Singleton : Wenn es in mehrere Module injiziert wird, haben alle Zugriff auf nur eine bestimmte Instanz. Aus diesem Grund ist es sehr praktisch, Daten zwischen verschiedenen Controllern auszutauschen.

FABRIK

Schauen wir uns zunächst die Syntax an:

app.js :

var app = angular.module('FactoryExample',[]);
var factoryController = app.controller('FactoryController', FactoryController);
var factoryExampleOne = app.factory('NameOfTheFactoryOne', NameOfTheFactoryOne);
var factoryExampleTwo = app.factory('NameOfTheFactoryTwo', NameOfTheFactoryTwo);

//first implementation where it returns a function
function NameOfTheFactoryOne(){
   var factory = function(){
      return new SomeService();
    }
   return factory;
}

//second implementation where an object literal would be returned
function NameOfTheFactoryTwo(){
   var factory = {
      getSomeService : function(){
          return new SomeService();
       }
    };
   return factory;
}

Verwenden Sie nun die beiden oben genannten Elemente in der Steuerung:

 var factoryOne = NameOfTheFactoryOne() //since it returns a function
 factoryOne.someMethod();

 var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object
 factoryTwo.someMethod();

Merkmale der Fabrik:

  1. Folgt dem werkseitigen Entwurfsmuster. Die Fabrik ist ein zentraler Ort, an dem neue Objekte oder Funktionen entstehen.
  2. Produziert nicht nur Singleton, sondern auch anpassbare Services.
  3. Die .service()Methode ist eine Factory , die immer dieselbe Art von Dienst erzeugt, nämlich einen Singleton, und ohne eine einfache Möglichkeit, das Verhalten zu konfigurieren. Diese .service()Methode wird normalerweise als Verknüpfung für etwas verwendet, für das keinerlei Konfiguration erforderlich ist.

ANBIETER

Schauen wir uns noch einmal zuerst die Syntax an:

angular.module('ProviderModule', [])
.controller('ProviderModuleController', ProviderModuleController)
.provider('ServiceProvider', ServiceProvider)
.config(Config); //optional

Config.$inject = ['ServiceProvider'];
function Config(ServiceProvider) {
  ServiceProvider.defaults.maxItems = 10; //some default value
}


ProviderModuleController.$inject = ['ServiceProvider'];
function ProviderModuleController(ServiceProvider) {
  //some methods
}

function ServiceProvider() {
  var provider = this;

  provider.defaults = {
    maxItems: 10
  };

  provider.$get = function () {
    var someList = new someListService(provider.defaults.maxItems);

    return someList;
  };
}

}

Funktionen des Anbieters:

  1. Der Anbieter ist die flexibelste Methode zum Erstellen von Diensten in Angular.
  2. Wir können nicht nur eine Factory erstellen, die dynamisch konfigurierbar ist, sondern zum Zeitpunkt der Verwendung der Factory mit der Provider-Methode die Factory nur einmal beim Bootstrapping unserer gesamten Anwendung benutzerdefiniert konfigurieren.
  3. Die Fabrik kann dann mit benutzerdefinierten Einstellungen in der gesamten Anwendung verwendet werden. Mit anderen Worten, wir können diese Factory konfigurieren, bevor die Anwendung gestartet wird. Tatsächlich wird in der Winkeldokumentation erwähnt, dass die Anbietermethode tatsächlich hinter den Kulissen ausgeführt wird, wenn wir unsere Dienste mit einer .serviceoder .factorymehreren Methoden konfigurieren .
  4. Dies $getist eine Funktion, die direkt an die Provider-Instanz angehängt ist. Diese Funktion ist eine Werksfunktion . Mit anderen Worten, es ist genau das, was wir verwenden, um die .factoryMethode bereitzustellen . In dieser Funktion erstellen wir unseren eigenen Service. Diese $getEigenschaft, das ist eine Funktion, macht den Anbieter zu einem Anbieter . AngularJS erwartet, dass der Anbieter über eine $ get-Eigenschaft verfügt, deren Wert eine Funktion ist, die Angular als Factory-Funktion behandelt. Das Besondere an diesem gesamten Provider-Setup ist jedoch die Tatsache, dass wir ein configObjekt innerhalb des Service-Providers bereitstellen können. Dies ist normalerweise mit Standardeinstellungen verbunden, die wir später in dem Schritt überschreiben können, in dem wir die gesamte Anwendung konfigurieren können.

7

Fabrik: Die Fabrik, in der Sie tatsächlich ein Objekt innerhalb der Fabrik erstellen und zurückgeben.
service: Der Service, für den Sie nur eine Standardfunktion haben, die das Schlüsselwort this verwendet, um die Funktion zu definieren.
Provider: Der Provider, für den Sie ein $ get definieren, kann verwendet werden, um das Objekt abzurufen, das die Daten zurückgibt.


7

Im Wesentlichen sind Anbieter, Fabrik und Service alle Services. Eine Factory ist ein Sonderfall eines Dienstes, bei dem Sie lediglich eine $ get () - Funktion benötigen, mit der Sie sie mit weniger Code schreiben können.

Die Hauptunterschiede zwischen Dienstleistungen, Fabriken und Anbietern sind ihre Komplexität. Services sind die einfachste Form, Fabriken sind etwas robuster und Provider können zur Laufzeit konfiguriert werden.

Hier ist eine Zusammenfassung, wann jeder verwendet werden soll:

Fabrik : Der von Ihnen angegebene Wert muss basierend auf anderen Daten berechnet werden.

Service : Sie geben ein Objekt mit Methoden zurück.

Anbieter : Sie möchten in der Konfigurationsphase das Objekt konfigurieren können, das vor seiner Erstellung erstellt werden soll. Verwenden Sie den Provider hauptsächlich in der App-Konfiguration, bevor die App vollständig initialisiert wurde.


ähm. Wert, Fabrik, Service und Konstante - sind nur syntaktischer Zucker über einem Anbieterrezept. Angularjs docs - Anbieter
Sudarshan_SMD

Ja, ich stimme zu, jetzt mit Winkel 4 haben wir diese Kopfschmerzen nicht mehr
eGhoul

4

1.Dienste sind Singleton-Objekte, die bei Bedarf erstellt und erst am Ende des Anwendungslebenszyklus (wenn der Browser geschlossen wird) bereinigt werden. Steuerungen werden zerstört und aufgeräumt, wenn sie nicht mehr benötigt werden.

2.Der einfachste Weg, einen Dienst zu erstellen, ist die Methode factory (). Mit der factory () -Methode können wir einen Service definieren, indem wir ein Objekt zurückgeben, das Servicefunktionen und Servicedaten enthält. In der Service-Definitionsfunktion platzieren wir unsere injizierbaren Services wie $ http und $ q. Ex:

angular.module('myApp.services')
.factory('User', function($http) { // injectables go here
var backendUrl = "http://localhost:3000"; var service = {
    // our factory definition
user: {},
setName: function(newName) {
      service.user['name'] = newName;
    },
setEmail: function(newEmail) { service.user['email'] = newEmail;
},
save: function() {
return $http.post(backendUrl + '/users', { user: service.user
}); }
};
return service; });

Verwenden Sie die factory () in unserer App

Es ist einfach, die Fabrik in unserer Anwendung zu verwenden, da wir sie zur Laufzeit einfach dort injizieren können, wo wir sie benötigen.

angular.module('myApp')
.controller('MainController', function($scope, User) {
  $scope.saveUser = User.save;
});
  1. Die service () -Methode hingegen ermöglicht es uns, einen Service durch Definieren einer Konstruktorfunktion zu erstellen. Wir können ein prototypisches Objekt anstelle eines rohen Javascript-Objekts verwenden, um unseren Service zu definieren. Ähnlich wie bei der factory () -Methode werden auch die injizierbaren Elemente in der Funktionsdefinition festgelegt.
  2. Die niedrigste Methode zum Erstellen eines Dienstes ist die Verwendung der Methode "deploy ()". Dies ist die einzige Möglichkeit, einen Dienst zu erstellen, den wir mit der Funktion .config () konfigurieren können. Im Gegensatz zu den vorherigen Methoden setzen wir die injizierbaren Elemente in einer definierten Funktionsdefinition. $ Get ().

-3

Syntaktischer Zucker ist der Unterschied . Es wird nur ein Anbieter benötigt. Oder mit anderen Worten, nur der Anbieter ist der reale Winkel, alle anderen werden abgeleitet (um den Code zu reduzieren). Es gibt auch eine einfache Version namens Value (), die nur den Wert, keine Berechnung oder Funktion zurückgibt. Gleichmäßiger Wert wird vom Anbieter abgeleitet!

Warum also solche Komplikationen, warum können wir nicht einfach den Anbieter verwenden und alles andere vergessen? Es soll uns helfen, Code einfach zu schreiben und besser zu kommunizieren. Und die Antwort wäre, je komplexer es wird, desto besser wird es sein, ein Framework zu verkaufen.


  • Ein Anbieter, der value = Value zurückgeben kann
  • Ein Anbieter, der nur instanziieren und zurückgeben kann = Factory (+ Value)
  • Ein Anbieter, der etwas instanziieren + tun kann = Service (+ Factory, + Value)
  • Ein Provider = muss eine Eigenschaft namens $ get (+ Factory, + Service, + Value) enthalten.

Die Winkelinjektion gibt uns den ersten Hinweis, um zu dieser Schlussfolgerung zu gelangen.

"$ injoror wird verwendet, um Objektinstanzen abzurufen, wie vom Anbieter definiert ", nicht Service, nicht Factory, sondern Provider.

Eine bessere Antwort wäre folgende: "Ein Angular-Service wird von einer Service-Factory erstellt. Diese Service-Fabriken sind Funktionen, die wiederum von einem Service-Provider erstellt werden. Die Service-Provider sind Konstruktorfunktionen. Wenn sie instanziiert werden, müssen sie eine Eigenschaft enthalten $ get genannt, das die Service Factory-Funktion enthält. "

Also Master Provider und Injektor und alles wird an seinen Platz fallen :). In Typescript wird es interessant, wenn $ get durch Erben von IServiceProvider in einem Anbieter implementiert werden kann.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.