AngularJS: usługa kontra dostawca vs fabryka

3319

Jakie są różnice pomiędzy a Service, Provideroraz Factoryw angularjs?

Lior
źródło
244
Przekonałem się, że wszystkie warunki w Angular były zastraszające dla początkujących. Zaczęliśmy od tego Cheatsheet że było trochę łatwiej zrozumieć nasze programiści podczas nauki kątowa demisx.github.io/angularjs/2014/09/14/... . Mam nadzieję, że pomoże to również Twojemu zespołowi.
demisx
7
Moim zdaniem najlepszym sposobem na zrozumienie różnicy jest skorzystanie z własnej dokumentacji Angulara : docs.quarejs.org/guide/providers , jest ona wyjątkowo dobrze wyjaśniona i używa osobliwego przykładu, który pomaga ci to zrozumieć.
Rafael Merlin
3
@ Blaise Dziękuję! Zgodnie z moim komentarzem w poście celowo go pominąłem, ponieważ 99% przypadków użycia z mojego doświadczenia można z powodzeniem rozwiązać za pośrednictwem service.factory. Nie chciałem dalej komplikować tego tematu.
demisx
3
Uważam, że ta dyskusja jest również bardzo przydatna stackoverflow.com/questions/18939709/...
Anand Gupta
3
Oto kilka dobrych odpowiedzi o tym, jakservices,factoriesiprovidersdzieł.
Mistalis

Odpowiedzi:

2866

Z listy mailingowej AngularJS otrzymałem niesamowity wątek, który wyjaśnia obsługę kontra fabryka kontra dostawca i sposób ich użycia. Kompilowanie odpowiedzi:

Usługi

Składnia: module.service( 'serviceName', function );
Wynik: Gdy deklarujesz serviceName jako argument do wstrzyknięcia , otrzymasz instancję funkcji. Innymi słowy new FunctionYouPassedToService() .

Fabryki

Składnia: module.factory( 'factoryName', function );
Wynik: deklarując nazwę_produktu jako argument do wstrzyknięcia, otrzymasz wartość zwracaną przez wywołanie odwołania do funkcji przekazanego do module.factory .

Dostawcy

Składnia: module.provider( 'providerName', function );
Wynik: deklarując nazwę dostawcy jako argument do wstrzyknięcia , otrzymasz (new ProviderFunction()).$get() . Funkcja konstruktora jest tworzona przed wywołaniem metody $ get - ProviderFunctionczy odwołanie do funkcji jest przekazywane do module.provider.

Zaletą dostawców jest to, że można je konfigurować podczas fazy konfiguracji modułu.

Widzieć tutaj podany kod.

Oto świetne dalsze wyjaśnienie Misko:

provide.value('a', 123);

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

W takim przypadku wtryskiwacz po prostu zwraca wartość jaka jest. Ale co jeśli chcesz obliczyć wartość? Następnie skorzystaj z fabryki

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

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

Więc factory jest funkcja odpowiedzialna za tworzenie wartości. Zauważ, że funkcja fabryczna może prosić o inne zależności.

Ale co, jeśli chcesz być bardziej OO i mieć klasę o nazwie Greeter?

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

Następnie, aby utworzyć wystąpienie, musisz napisać

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

Następnie moglibyśmy poprosić o „powitanie” w kontrolerze takim jak ten

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

Ale to jest zbyt trudne. Byłby to krótszy sposób na napisanie tegoprovider.service('greeter', Greeter);

Ale co, jeśli chcielibyśmy skonfigurować Greeterklasę przed wstrzyknięciem? Wtedy moglibyśmy pisać

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

Następnie możemy to zrobić:

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

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

Na marginesie, service, factory, i valuesą pochodzące od dostawcy.

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;
  });
};
Lior
źródło
58
Zobacz także stackoverflow.com/a/13763886/215945, który omawia różnice między usługą a fabryką.
Mark Rajcok
3
W edycji 611 dodałem użycie stałych kątowych i wartości. Aby zademonstrować różnice między drugim pokazanym już. jsbin.com/ohamub/611/edit
Nick
17
Chociaż usługa jest wywoływana przez utworzenie instancji funkcji. W rzeczywistości jest tworzony tylko raz dla każdego wtryskiwacza, co sprawia, że ​​przypomina singleton. docs.quarejs.org/guide/dev_guide.services.creating_services
angelokh
33
Ten przykład mógłby być niewiarygodny, gdyby wykorzystał wyraźny praktyczny przykład. Gubię próbuje dowiedzieć się, o co chodzi rzeczy jak toEquali greeter.Greetto. Dlaczego nie użyć czegoś nieco bardziej realnego i powiązanego?
Kyle Pennell,
5
Użycie funkcji expect () jest złym wyborem, aby coś wyjaśnić. Następnym razem użyj kodu świata rzeczywistego.
Craig
812

JS Fiddle Demo

Przykład „Witaj świecie” z 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>

EpokK
źródło
2
Nie thiszmienia kontekstu w $getfunkcji? - już nie odwołujesz się do dostawcy instancji w tej funkcji.
Nate-Wilkins
12
@Nate: w thisrzeczywistości nie zmienia kontekstu, ponieważ wywoływana jest funkcja new Provider(). $ Get (), do której Providerprzekazywana jest funkcja app.provider. To znaczy, że $get()jest wywoływany jako metoda na skonstruowanej Provider, więc thisbędzie się odnosił Providertak, jak sugeruje to przykład.
Brandon
1
@Brandon Ohh ok, to całkiem miłe. Mylące na pierwszy rzut oka - dziękuję za wyjaśnienie!
Nate-Wilkins
3
Dlaczego dostaję Unknown provider: helloWorldProvider <- helloWorld, uruchamiając to lokalnie? Komentując to, ten sam błąd dla pozostałych 2 przykładów. Czy jest jakaś ukryta konfiguracja dostawcy? (Angular 1.0.8) - Znaleziono: stackoverflow.com/questions/12339272/…
Antoine,
4
To jest powód, dla którego @Antoine dostaje błąd „Nieznane dostarczenie: helloWorldProvider”, ponieważ w kodzie .config używasz „helloWorldProvider”, ale kiedy definiujesz dostawcę w myApp.provider („helloWorld”, funkcja ()), używasz 'Witaj świecie'? Innymi słowy, w kodzie konfiguracji, w jaki sposób kątowa wie, że masz na myśli dostawcę helloWorld? Dzięki
jmtoung
645

TL; DR

1) Gdy używasz fabryki , tworzysz obiekt, dodajesz do niego właściwości, a następnie zwraca ten sam obiekt. Po przekazaniu tej fabryki do kontrolera właściwości tego obiektu będą teraz dostępne w tym kontrolerze za pośrednictwem fabryki.

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) Gdy korzystasz z usługi , AngularJS tworzy ją za kulisami ze słowem kluczowym „new”. Z tego powodu dodasz właściwości do „tego”, a usługa zwróci „to”. Po przekazaniu usługi do kontrolera właściwości „this” będą teraz dostępne na tym kontrolerze za pośrednictwem usługi.

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

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



3) Dostawcy są jedyną usługą, którą można przekazać do funkcji .config (). Skorzystaj z usługodawcy, jeśli chcesz udostępnić konfigurację całego modułu dla obiektu usługi przed udostępnieniem go.

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’;
});



Non TL; DR

1)
Fabryki to najpopularniejszy sposób tworzenia i konfigurowania usługi. Naprawdę niewiele więcej niż mówi TL; DR. Wystarczy utworzyć obiekt, dodać do niego właściwości, a następnie zwrócić ten sam obiekt. Następnie, po przekazaniu fabryki do kontrolera, te właściwości obiektu będą teraz dostępne w tym kontrolerze przez fabrykę. Bardziej obszerny przykład znajduje się poniżej.

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

Teraz wszelkie właściwości, które dołączamy do „usługi”, będą dostępne, gdy przekażemy „myFactory” do naszego kontrolera.

Dodajmy teraz trochę „prywatnych” zmiennych do naszej funkcji zwrotnej. Nie będą one bezpośrednio dostępne z poziomu kontrolera, ale w końcu skonfigurujemy metody getter / setter dla „service”, aby móc zmienić te „prywatne” zmienne w razie potrzeby.

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

Tutaj zauważysz, że nie przypisujemy tych zmiennych / funkcji do „usługi”. Po prostu tworzymy je w celu późniejszego wykorzystania lub modyfikacji.

  • baseUrl to podstawowy adres URL wymagany przez interfejs API iTunes
  • _artist to artysta, którego chcemy szukać
  • _finalUrl to końcowy iw pełni zbudowany adres URL, do którego zadzwonimy do iTunes
  • makeUrl to funkcja, która utworzy i zwróci nasz przyjazny adres URL iTunes.

Teraz, gdy nasze zmienne pomocnicze / prywatne i funkcja są już na miejscu, dodajmy pewne właściwości do obiektu „service”. Cokolwiek nałożymy na „usługę”, może być użyte bezpośrednio w dowolnym kontrolerze, w którym przekazujemy „myFactory”.

Stworzymy metody setArtist i getArtist, które po prostu zwrócą lub ustawią artystę. Stworzymy również metodę, która wywoła interfejs API iTunes z naszym utworzonym adresem URL. Ta metoda zwróci obietnicę, która spełni się, gdy dane wrócą z interfejsu API iTunes. Jeśli nie masz dużego doświadczenia w korzystaniu z obietnic w AngularJS, zdecydowanie polecam głębokie zanurzenie się w nich.

Poniżej setArtist akceptuje wykonawcę i umożliwia ustawienie wykonawcy. getArtist zwraca wykonawcę. callItunes najpierw wywołuje makeUrl () w celu zbudowania adresu URL, którego będziemy używać z naszym żądaniem $ http. Następnie ustawia obiekt obietnicy, wysyła żądanie $ http z naszym ostatnim adresem URL, a ponieważ $ http zwraca obietnicę, jesteśmy w stanie wywołać .success lub .error po naszej prośbie. Następnie wypełniamy naszą obietnicę za pomocą danych iTunes lub odrzucamy ją komunikatem „Wystąpił błąd”.

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

Teraz nasza fabryka jest kompletna. Jesteśmy teraz w stanie wstrzyknąć „myFactory” do dowolnego kontrolera, a następnie będziemy mogli wywoływać nasze metody, które podłączyliśmy do naszego obiektu usługi (setArtist, getArtist i 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);
      })
  }
});

W powyższym kontrolerze wprowadzamy usługę „myFactory”. Następnie ustawiamy właściwości naszego obiektu $ scope danymi z „myFactory”. Jedynym trudnym kodem powyżej jest to, że nigdy wcześniej nie dotykałeś obietnic. Ponieważ callItunes zwraca obietnicę, jesteśmy w stanie użyć metody .then () i ustawić wartość $ scope.data.artistData tylko wtedy, gdy nasza obietnica zostanie wypełniona danymi iTunes. Zauważysz, że nasz kontroler jest bardzo „cienki” (jest to dobra praktyka kodowania). Wszystkie nasze logiczne i trwałe dane znajdują się w naszym serwisie, a nie w naszym kontrolerze.

2) Usługa
Być może największą rzeczą, o której należy wiedzieć podczas tworzenia Usługi, jest to, że jest ona tworzona za pomocą słowa kluczowego „new”. Dla ciebie guru JavaScript powinien dać ci dużą wskazówkę dotyczącą natury kodu. Dla tych z ograniczonym doświadczeniem w JavaScript lub dla tych, którzy nie są zbyt dobrze zaznajomieni z tym, co faktycznie robi „nowe” słowo kluczowe, przejrzyjmy niektóre podstawy JavaScript, które ostatecznie pomogą nam zrozumieć naturę Usługi.

Aby naprawdę zobaczyć zmiany, które następują po wywołaniu funkcji słowem kluczowym „new”, utwórzmy funkcję i wywołaj ją słowem kluczowym „new”, a następnie pokażmy, co robi tłumacz, gdy zobaczy słowo kluczowe „new”. Oba wyniki końcowe będą takie same.

Najpierw stwórzmy naszego Konstruktora.

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

Jest to typowa funkcja konstruktora JavaScript. Teraz za każdym razem, gdy wywołujemy funkcję Osoba za pomocą słowa kluczowego „new”, „to” zostanie powiązane z nowo utworzonym obiektem.

Dodajmy teraz metodę do prototypu naszej Osoby, aby była dostępna w każdej instancji naszej „klasy” Osoby.

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

Teraz, ponieważ umieszczamy funkcję sayName na prototypie, każda instancja Person będzie mogła wywołać funkcję sayName, aby zaalarmować nazwę tej instancji.

Teraz, gdy mamy prototyp konstruktora Person i funkcję sayName na jego prototypie, utwórzmy instancję Person, a następnie wywołaj funkcję sayName.

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

Cały kod do tworzenia konstruktora Person, dodawania funkcji do jego prototypu, tworzenia instancji Person, a następnie wywoływania funkcji na prototypie wygląda następująco.

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’

Teraz spójrzmy na to, co się właściwie dzieje, gdy używasz słowa kluczowego „new” w JavaScript. Pierwszą rzeczą, którą powinieneś zauważyć, jest to, że po użyciu „new” w naszym przykładzie jesteśmy w stanie wywołać metodę (sayName) na „tyler” tak, jakby to był obiekt - to dlatego, że tak jest. Po pierwsze wiemy, że nasz konstruktor Person zwraca obiekt, bez względu na to, czy widzimy to w kodzie, czy nie. Po drugie, wiemy, że ponieważ nasza funkcja sayName znajduje się na prototypie, a nie bezpośrednio na instancji Person, obiekt, który zwraca funkcja Person, musi delegować do swojego prototypu podczas nieudanych wyszukiwań. Mówiąc prościej, kiedy wywołujemy tyler.sayName () interpreter mówi „OK, przejrzę właśnie utworzony obiekt„ tyler ”, zlokalizuj funkcję sayName, a następnie wywołaj ją. Chwileczkę, nie widzę tego tutaj - widzę tylko imię i wiek, pozwól mi sprawdzić prototyp. Tak, wygląda na to, że jest na prototypie, nazwijmy to ”.

Poniżej znajduje się kod wyjaśniający, co tak naprawdę robi „nowe” słowo kluczowe w JavaScript. Jest to w zasadzie przykład kodu powyższego akapitu. Umieściłem „widok tłumacza” lub sposób, w jaki tłumacz widzi kod w notatkach.

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;
}

Teraz, mając wiedzę na temat tego, co tak naprawdę robi „nowe” słowo kluczowe w JavaScript, tworzenie Usługi w AngularJS powinno być łatwiejsze do zrozumienia.

Najważniejszą rzeczą do zrozumienia podczas tworzenia Usługi jest świadomość, że Usługi są tworzone za pomocą słowa kluczowego „new”. Łącząc tę ​​wiedzę z naszymi powyższymi przykładami, powinieneś teraz wiedzieć, że będziesz przypisywać swoje właściwości i metody bezpośrednio do „tego”, który następnie zostanie zwrócony z samej Usługi. Spójrzmy na to w akcji.

W przeciwieństwie do tego, co pierwotnie zrobiliśmy z przykładem Factory, nie musimy tworzyć obiektu, a następnie zwracać go, ponieważ, jak wspomniano wiele razy wcześniej, użyliśmy słowa kluczowego „new”, aby interpreter utworzył ten obiekt, zlecając mu to prototyp, a następnie zwróć go nam bez konieczności wykonywania pracy.

Po pierwsze, stwórzmy naszą funkcję „prywatną” i pomocniczą. Powinno to wyglądać bardzo znajomo, ponieważ zrobiliśmy dokładnie to samo z naszą fabryką. Nie wyjaśnię, co robi tutaj każda linia, ponieważ zrobiłem to w przykładzie fabrycznym. Jeśli jesteś zdezorientowany, przeczytaj ponownie przykład fabryczny.

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

Teraz dołączymy wszystkie nasze metody, które będą dostępne w naszym kontrolerze do „tego”.

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;
  }

});

Teraz, podobnie jak w naszej fabryce, setArtist, getArtist i callItunes będą dostępne w dowolnym kontrolerze, do którego przekazujemy myService. Oto kontroler myService (który jest prawie dokładnie taki sam jak nasz kontroler fabryczny).

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

Jak wspomniałem wcześniej, kiedy naprawdę zrozumiesz, co robi „nowość”, usługi są prawie identyczne z fabrykami w AngularJS.

3) Dostawca

Najważniejszą rzeczą, o której należy pamiętać, jest to, że są to jedyne usługi, które można przekazać do części app.config aplikacji. Ma to ogromne znaczenie, jeśli musisz zmienić pewną część obiektu usługi, zanim będzie on dostępny wszędzie w aplikacji. Chociaż są bardzo podobne do usług / fabryk, istnieje kilka różnic, które omówimy.

Najpierw skonfigurowaliśmy naszego dostawcę w podobny sposób, jak w przypadku naszego serwisu i fabryki. Poniższe zmienne to nasza funkcja „prywatna” i pomocnicza.

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;
  }
}

* Ponownie, jeśli jakakolwiek część powyższego kodu jest myląca, sprawdź sekcję Fabryka, w której wyjaśniam, co to wszystko robi, zawiera więcej szczegółów.

Możesz myśleć o Dostawcach jako o trzech sekcjach. Pierwsza sekcja to „prywatne” zmienne / funkcje, które zostaną zmodyfikowane / ustawione później (pokazane powyżej). Druga sekcja to zmienne / funkcje, które będą dostępne w funkcji app.config i dlatego można je zmienić, zanim będą dostępne nigdzie indziej (pokazano również powyżej). Należy zauważyć, że zmienne te muszą być dołączone do słowa kluczowego „this”. W naszym przykładzie w app.config będzie można zmienić tylko „thingFromConfig”. Trzecia sekcja (pokazana poniżej) to wszystkie zmienne / funkcje, które będą dostępne w twoim kontrolerze po przekazaniu usługi „myProvider” do tego konkretnego kontrolera.

Podczas tworzenia usługi z Dostawcą jedynymi właściwościami / metodami, które będą dostępne w twoim kontrolerze, są te właściwości / metody, które są zwracane z funkcji $ get (). Poniższy kod nakłada $ get na „this” (co, jak wiemy, w końcu zostanie zwrócone z tej funkcji). Teraz ta funkcja $ get zwraca wszystkie metody / właściwości, które chcemy być dostępne w kontrolerze. Oto przykład kodu.

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
    }
  }

Teraz pełny kod dostawcy wygląda następująco

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

Teraz, podobnie jak w naszej fabryce i serwisie, setArtist, getArtist i callItunes będą dostępne w dowolnym kontrolerze, do którego przekazujemy myProvider. Oto kontroler myProvider (który jest prawie dokładnie taki sam jak nasz kontroler fabryczny / serwisowy).

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

Jak wspomniano wcześniej, celem tworzenia usługi za pomocą dostawcy jest możliwość zmiany niektórych zmiennych za pomocą funkcji app.config, zanim obiekt końcowy zostanie przekazany do reszty aplikacji. Zobaczmy przykład tego.

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';
});

Teraz możesz zobaczyć, jak „thingFromConfig” jest jak pusty ciąg u naszego dostawcy, ale kiedy to pojawi się w DOM, będzie to „To zdanie zostało ustawione…”.

Tyler McGinnis
źródło
11
Jedyną częścią, której brakuje w tym doskonałym zapisie, są względne zalety korzystania z usługi nad fabryką; co jest jasno wyjaśnione w przyjętej odpowiedzi Liora
nieskończoność
2
FWIW (może nie wiele), oto bloger, który ma problem z Angularem
barlop
3
Punktacja guru JavaScript była przebiegła. : Myslę, że ta odpowiedź bardzo wszystko wyjaśnia. Wspaniale napisane.
amarmishra
4
Twój TLDR potrzebuje TLDR.
JensB
3
@JensB tl; dr - Learn React.
Tyler McGinnis
512

Wszystkie usługi są singletonami ; są one tworzone raz na aplikację. Mogą być dowolnego typu , bez względu na to, czy jest to prymityw, literał obiektu, funkcja, a nawet instancja typu niestandardowego.

value, factory, service, constant, I providermetody są wszyscy dostawcy. Uczą wtryskiwacza, jak tworzyć instancję Usług.

Najbardziej szczegółowym, ale także najbardziej kompleksowym jest przepis na dostawcę. Te pozostałe cztery rodzaje przepis - Wartość, fabryka, serwis i stałe - są po prostu cukier syntaktyczny na szczycie receptury dostawcy .

  • Wartość Przepis jest najprostszy przypadek, w którym usługa instancji siebie i podać wartość instancji do wtryskiwacza.
  • Przepis fabryka daje wtryskiwacza funkcji fabrycznego, który wywołuje to, gdy jest konieczne do wystąpienia usługi. Po wywołaniu funkcja fabryki tworzy i zwraca instancję usługi. Zależności usługi są wprowadzane jako argumenty funkcji. Zatem użycie tego przepisu dodaje następujące umiejętności:
    • Możliwość korzystania z innych usług (mają zależności)
    • Inicjalizacja usługi
    • Opóźniona / leniwa inicjalizacja
  • Przepis obsługa jest prawie taka sama jak w recepturze fabryczne, ale tutaj wtryskiwacza wywołuje konstruktor za pomocą operatora new zamiast funkcji fabrycznego.
  • Przepis Provider jest zwykle przesadą . Dodaje jeszcze jedną warstwę pośrednictwa, umożliwiając skonfigurowanie tworzenia fabryki.

    Z przepisu Provider należy korzystać tylko wtedy, gdy chcesz udostępnić interfejs API dla konfiguracji obejmującej całą aplikację, którą należy wykonać przed uruchomieniem aplikacji. Jest to zwykle interesujące tylko w przypadku usług wielokrotnego użytku, których zachowanie może wymagać nieco różnić w zależności od aplikacji.

  • Stała przepis jest jak przepis Wartość oprócz tego, że pozwala na definiowanie usług, które są dostępne w konfiguracji fazie. Wcześniej niż usługi utworzone przy użyciu receptury Value. W przeciwieństwie do Wartości, nie można ich ozdobić za pomocą decorator.
Zobacz dokumentację dostawcy .

flup
źródło
2
Czy serwis i fabryka są w zasadzie takie same? Użycie jednego z nich zapewnia jedynie alternatywną składnię?
Matt
2
@Matt, tak, usługa to zwięzły sposób, gdy masz już własną funkcję, którą chcesz udostępnić jako usługę. Z dokumentów: myApp.factory ('unicornLauncher', [„apiToken”, funkcja (apiToken) {zwróć nowy UnicornLauncher (apiToken);}]); vs: myApp.service („unicornLauncher”, [„apiToken”, UnicornLauncher]);
janek
5
@ joshperry Jako początkujący, przez jakiś czas przeglądałem różnicę między serwisem i fabryką. Zgadzam się, że to najlepsza odpowiedź na świecie! Zrozumiałbym usługę jako klasę usług (np. Klasa kodera / dekodera), która może mieć pewne własności prywatne. A fabryka zapewnia zestaw bezpaństwowych metod pomocniczych.
stanleyxu2005
3
Przykłady Yaa w innych powyższych odpowiedziach nie wyjaśniają bardzo jasno podstawowej różnicy między usługami i dostawcami, a tym, co wstrzykuje się w momencie tworzenia tych przepisów.
Ashish Singh,
223

Zrozumienie fabryki, serwisu i dostawcy AngularJS

Wszystkie z nich służą do udostępniania obiektów singletonowych wielokrotnego użytku. Pomaga udostępniać kod wielokrotnego użytku w aplikacji / różnych komponentach / modułach.

Z serwisu / fabryki dokumentów :

  • Instancja lazurowa - Angular tworzy instancję usługi / fabryki tylko wtedy, gdy zależy od niej składnik aplikacji.
  • Singletony - każdy komponent zależny od usługi otrzymuje odniesienie do pojedynczego wystąpienia wygenerowanego przez fabrykę usług.

Fabryka

Fabryka to funkcja, w której można manipulować / dodawać logikę przed utworzeniem obiektu, a następnie nowo utworzony obiekt zostaje zwrócony.

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

Stosowanie

Może to być tylko zbiór funkcji takich jak klasa. Dlatego może być tworzony w różnych kontrolerach, gdy wstrzykujesz go do funkcji kontrolera / fabryki / dyrektywy. Jest on tworzony tylko raz na aplikację.

Usługa

Po prostu patrząc na usługi pomyśl o prototypie macierzy. Usługa to funkcja, która tworzy nowy obiekt za pomocą słowa kluczowego „new”. Możesz dodać właściwości i funkcje do obiektu usługi za pomocą thissłowa kluczowego. W przeciwieństwie do fabryki nic nie zwraca (zwraca obiekt zawierający metody / właściwości).

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

Stosowanie

Użyj go, gdy chcesz udostępnić pojedynczy obiekt w aplikacji. Na przykład uwierzytelnione dane użytkownika, udostępniane metody / dane, funkcje narzędziowe itp.

Dostawca

Do utworzenia konfigurowalnego obiektu usługi służy dostawca. Możesz skonfigurować ustawienia usługi za pomocą funkcji konfiguracji. Zwraca wartość za pomocą $get()funkcji. $getFunkcja zostanie wykonana na etapie wykonywania w kątowe.

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

Stosowanie

Kiedy musisz udostępnić konfigurację modułową dla obiektu usługi przed udostępnieniem go, np. załóżmy, że chcemy ustawić API URL na podstawie danego środowiska podobnego dev, stagelubprod

UWAGA

Tylko dostawca będzie dostępny w fazie konfiguracji kątowej, podczas gdy serwis i fabryka nie.

Mam nadzieję, że to wyjaśniło twoje zrozumienie dotyczące fabryki, serwisu i dostawcy .

Pankaj Parkar
źródło
1
Co bym zrobił, gdybym chciał mieć usługę z określonym interfejsem, ale mieć dwie różne implementacje i wstrzyknąć każdą do kontrolera, ale powiązaną z różnymi stanami za pomocą interfejsu użytkownika? np. wykonuj połączenia zdalne w jednym stanie, ale pisz w lokalnej pamięci zamiast w innym. Dokumenty dostawcy mówią, aby używać 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, więc nie brzmi to możliwe, prawda?
qix
191

Dla mnie objawienie pojawiło się, gdy zdałem sobie sprawę, że wszystkie działają w ten sam sposób: uruchamiając coś raz , przechowując otrzymaną wartość, a następnie odkrztuszając tę samą przechowywaną wartość, do której odwołuje się poprzez wstrzyknięcie zależności .

Powiedzmy, że mamy:

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

Różnica między tymi trzema polega na tym, że:

  1. aZapisana wartość pochodzi z działania fn.
  2. bZapisana wartość pochodzi z newing fn.
  3. cZapisana wartość pochodzi z pierwszego pobrania instancji przez newing fn, a następnie uruchomienia $getmetody instancji.

Co oznacza, że ​​wewnątrz AngularJS znajduje się coś w rodzaju obiektu pamięci podręcznej, którego wartość każdego zastrzyku jest przypisywana tylko raz, gdy zostały wstrzyknięte po raz pierwszy i gdzie:

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

Dlatego używamy thisw usługach i definiujemy this.$getdostawców.

Lucia
źródło
2
Najbardziej podoba mi się również ta odpowiedź. Ich celem jest zapewnienie dostępu do obiektu w razie potrzeby za pośrednictwem DI. Zwykle masz się dobrze z factorys. Jedynymi servicepowodami są języki takie jak CoffeeScript, TypeScript, ES6 itp., Więc możesz użyć ich składni klas. Potrzebujesz providertylko, jeśli moduł jest używany w kilku aplikacjach z różnymi ustawieniami przy użyciu app.config(). Jeśli twoja usługa jest czystym singletonem lub jest w stanie stworzyć instancje czegoś, zależy tylko od twojej implementacji.
Andreas Linnert,
137

Usługa kontra dostawca vs fabryka:

Staram się to uprościć. Chodzi o podstawową koncepcję JavaScript.

Przede wszystkim porozmawiajmy o usługach w AngularJS!

Co to jest usługa: w AngularJS, usługajest niczym innym jak pojedynczym obiektem JavaScript, który może przechowywać użyteczne metody lub właściwości. Ten obiekt singletonowy jest tworzony na podstawie ngApp (aplikacja Angular) i jest współdzielony przez wszystkie kontrolery w bieżącej aplikacji. Kiedy Angularjs tworzy obiekt usługi, rejestruje ten obiekt usługi pod unikalną nazwą usługi. Dlatego za każdym razem, gdy potrzebujemy wystąpienia usługi, Angular przeszukuje rejestr w poszukiwaniu tej nazwy usługi i zwraca odwołanie do obiektu usługi. Tak, że możemy wywołać metodę, dostęp do właściwości itp. Na obiekcie usługi. Możesz mieć pytanie, czy możesz również umieścić właściwości, metody na obiekcie zakresu kontrolerów! Dlaczego więc potrzebujesz obiektu usługi? Odpowiedź brzmi: usługi są współużytkowane przez wiele kontrolerów. Jeśli umieścisz niektóre właściwości / metody w obiekcie zakresu kontrolera, będzie on dostępny tylko dla bieżącego zakresu.

Jeśli więc istnieją trzy zakresy kontrolerów, niech będą to kontroler A, kontroler B i kontroler C, wszystkie będą współużytkować tę samą instancję usługi.

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

Jak stworzyć usługę?

AngularJS zapewnia różne metody rejestracji usługi. Tutaj skoncentrujemy się na trzech metodach: fabryce (..), serwisie (..), dostawcy (..);

Użyj tego linku jako odniesienia do kodu

Funkcja fabryczna:

Możemy zdefiniować funkcję fabryczną jak poniżej.

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

AngularJS zapewnia metodę „factory („ serviceName ”, fnFactory)” , która przyjmuje dwa parametry: serviceName i funkcję JavaScript. Angular tworzy instancję usługi, wywołując funkcję fnFactory (), taką jak poniżej.

var serviceInstace = fnFactory();

Przekazana funkcja może zdefiniować obiekt i zwrócić ten obiekt. AngularJS po prostu przechowuje to odwołanie do obiektu do zmiennej, która jest przekazywana jako pierwszy argument. Wszystko, co zostanie zwrócone z fnFactory, będzie powiązane z usługą serviceInstance. Zamiast zwracać obiekt, możemy również zwrócić funkcję, wartości itp. Cokolwiek zwrócimy, będzie dostępne dla instancji usługi.

Przykład:

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

Funkcja serwisowa:

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

To inny sposób, w jaki możemy zarejestrować usługę. Jedyną różnicą jest sposób, w jaki AngularJS próbuje utworzyć instancję obiektu usługi. Tym razem angular używa słowa kluczowego „new” i wywołuje funkcję konstruktora jak poniżej.

var serviceInstance = new fnServiceConstructor();

W funkcji konstruktora możemy użyć słowa kluczowego „this” do dodania właściwości / metod do obiektu usługi. przykład:

//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);
  };
});

Funkcja dostawcy:

Funkcja Provider () to kolejny sposób tworzenia usług. Pozwól nam zainteresować się stworzeniem usługi, która po prostu wyświetli powitanie dla użytkownika. Ale chcemy również zapewnić taką funkcjonalność, aby użytkownik mógł ustawić własne powitanie. Z technicznego punktu widzenia chcemy tworzyć konfigurowalne usługi. Jak możemy to zrobić? Musi istnieć sposób, aby aplikacja mogła przekazać swoje niestandardowe wiadomości powitalne, a Angularjs udostępniłby ją funkcji fabryki / konstruktora, która tworzy instancję naszych usług. W takim przypadku funkcja dostawca () wykonuje zadanie. za pomocą funkcji provider () możemy tworzyć konfigurowalne usługi.

Możemy tworzyć konfigurowalne usługi przy użyciu składni dostawcy, jak podano poniżej.

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

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

Jak wewnętrznie działa składnia dostawcy?

1. Obiekt Provider jest tworzony za pomocą funkcji konstruktora, którą zdefiniowaliśmy w naszej funkcji dostawcy.

var serviceProvider = new serviceProviderConstructor();

2. Funkcja, którą przekazaliśmy w app.config (), zostanie wykonana. Nazywa się to fazą konfiguracji i tutaj mamy szansę na dostosowanie naszej usługi.

configureService(serviceProvider);

3. Wreszcie instancja usługi jest tworzona przez wywołanie metody $ get z serviceProvider.

serviceInstance = serviceProvider.$get()

Przykładowy kod do tworzenia usługi przy użyciu składni:

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 ?');
  }
);

Demo pracy

Podsumowanie:


Fabrycznie użyj funkcji fabryki, która zwraca instancję usługi. serviceInstance = fnFactory ();

Usługa korzysta z funkcji konstruktora, a Angular wywołuje tę funkcję konstruktora, używając słowa kluczowego „new” do utworzenia instancji usługi. serviceInstance = new fnServiceConstructor ();

Dostawca definiuje funkcję dostawcaKonstruktor, ta funkcja dostawcaKonstruktor definiuje funkcję fabryczną $ get . Angular wywołuje $ get () w celu utworzenia obiektu usługi. Składnia dostawcy ma dodatkową zaletę konfigurowania obiektu usługi przed jego utworzeniem. serviceInstance = $ get ();

Mrówka
źródło
63

Fabryka

Dajesz AngularJS funkcję, AngularJS zbuforuje pamięć podręczną i wstrzykuje wartość zwracaną, gdy zażąda tego fabryka.

Przykład:

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

Stosowanie:

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

Usługa

Dajesz AngularJS funkcję, AngularJS wywoła new, aby ją utworzyć. Jest to instancja utworzona przez AngularJS, która będzie buforowana i wstrzykiwana, gdy usługa zostanie zażądana. Ponieważ nowy użyto instancji usługi, słowo to jest ważna i odnosi się do instancji.

Przykład:

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

Stosowanie:

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

Dostawca

Dajesz AngularJS funkcję, a AngularJS wywoła tę $getfunkcję. Jest to wartość zwracana z $getfunkcji, która będzie buforowana i wstrzykiwana, gdy usługa zostanie zażądana.

Dostawcy pozwalają skonfigurować dostawcę, zanim AngularJS $getwywoła metodę uzyskania wstrzyknięcia.

Przykład:

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

Zastosowanie (jako wstrzyknięcie w sterowniku)

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

Użycie (wcześniej skonfigurowano dostawcę, $getaby utworzyć plik do wstrzykiwania)

app.config(function(providerProvider) {
    providerProvider.setName('John');
});
piksele
źródło
56

Zauważyłem coś interesującego podczas zabawy z dostawcami.

Widoczność zastrzyków jest inna dla dostawców niż dla usług i fabryk. Jeśli zadeklarujesz „stałą” AngularJS (na przykład myApp.constant('a', 'Robert');), możesz wprowadzić ją do usług, fabryk i dostawców.

Ale jeśli zadeklarujesz „wartość” AngularJS (na przykład. myApp.value('b', {name: 'Jones'});), Możesz wstrzyknąć ją do usług i fabryk, ale NIE do funkcji tworzenia dostawcy. Możesz jednak wprowadzić ją do $getfunkcji zdefiniowanej dla swojego dostawcy. Jest to wspomniane w dokumentacji AngularJS, ale łatwo go przeoczyć. Można go znaleźć na stronie% dostarczyć w sekcjach dotyczących wartości i metod stałych.

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>
tylko patrzę
źródło
45

Jest to bardzo mylące dla początkujących i starałem się to wyjaśnić prostymi słowami

Usługa AngularJS: służy do współdzielenia funkcji narzędziowych z odniesieniem do usługi w kontrolerze. Usługa ma charakter singleton, więc w przypadku jednej usługi w przeglądarce tworzona jest tylko jedna instancja, a na stronie używane jest to samo odwołanie.

W usłudze tworzymy nazwy funkcji jako właściwość dla tego obiektu.

AngularJS Factory: cel Factory jest również taki sam jak Service, ale w tym przypadku tworzymy nowy obiekt i dodajemy funkcje jako właściwości tego obiektu, a na koniec zwracamy ten obiekt.

AngularJS Provider: cel tego jest ponownie taki sam, jednak Provider podaje wynik funkcji $ get.

Definiowanie i korzystanie z usługi, fabryki i dostawcy wyjaśniono na stronie http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider

Sheo Narayan
źródło
2
Fabryka i dostawcy są również obiektami singletonowymi? Jakieś scanrio, w którym fabryki są zalecane zamiast usług?
Sunil Garg
34

Dla mnie najlepszym i najprostszym sposobem zrozumienia różnicy jest:

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

Jak AngularJS tworzy poszczególne komponenty (uproszczone):

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

// factory
var angularFactory = factory(injection);

Tak więc dla usługi komponent AngularJS staje się instancją obiektu klasy reprezentowaną przez funkcję deklaracji usługi. W przypadku fabryki jest to wynik zwracany z funkcji deklaracji fabrycznej. Fabryka może zachowywać się tak samo jak usługa:

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

Najprostszy sposób myślenia jest następujący:

  • Usługa jest instancją obiektu singleton. Skorzystaj z usług, jeśli chcesz podać obiekt typu singleton dla swojego kodu.
  • Fabryka jest klasą. Użyj fabryk, jeśli chcesz podać niestandardowe klasy dla swojego kodu (nie można tego zrobić z usługami, ponieważ są one już tworzone).

Fabryczny przykład „klasy” znajduje się w komentarzach, a także różnice między dostawcami.

Łukasz Frankowski
źródło
jak usługa może być singletonem, jeśli jest tworzona za każdym razem, gdy jest używana? mogę sobie poradzić z tym ...
Joe
Usługa jest tworzona tylko raz podczas rozwiązywania zależności, a następnie, gdy poprosisz o usługę od wtryskiwacza, zawsze otrzymujesz tę samą instancję. Można to łatwo sprawdzić tutaj: jsfiddle.net/l0co/sovtu55t/1 , uruchom go z konsolą. Konsola pokazuje, że usługa jest tworzona tylko raz.
Łukasz Frankowski
rozumiem. spodziewałem się, że będę w stanie dosłownie new MyService()czy coś :)
Joe
33

Moje wyjaśnienie w tej sprawie:

Zasadniczo wszystkie wymienione typy (usługa, fabryka, dostawca itp.) Po prostu tworzą i konfigurują zmienne globalne (które są oczywiście globalne dla całej aplikacji), podobnie jak staromodne zmienne globalne.

Chociaż zmienne globalne nie są zalecane, prawdziwym zastosowaniem tych zmiennych globalnych jest zapewnienie wstrzyknięcia zależności , poprzez przekazanie zmiennej do odpowiedniego kontrolera.

Istnieje wiele poziomów komplikacji w tworzeniu wartości dla „zmiennych globalnych”:

  1. Stała
    Definiuje rzeczywistą stałą, która nie powinna być modyfikowana podczas całej aplikacji, podobnie jak stałe w innych językach (coś, czego brakuje JavaScript).
  2. Wartość
    Jest to wartość lub obiekt, który można modyfikować i służy ona jako pewna zmienna globalna, którą można wprowadzić nawet podczas tworzenia innych usług lub fabryk (patrz dalej). Jednak musi to być „ wartość dosłowne ”, co oznacza, że trzeba pisać rzeczywistą wartość i nie może używać żadnych obliczeń lub programowanie logiki (innymi słowy 39 lub myText lub {podpory: „wartość”} są OK, ale 2 +2 nie jest).
  3. Fabryka
    Bardziej ogólna wartość, którą można obliczyć od razu. Działa poprzez przekazanie funkcji do AngularJS z logiką potrzebną do obliczenia wartości i AngularJS ją wykonuje, i zapisuje wartość zwracaną w nazwie zmiennej.
    Zauważ, że można zwrócić obiekt (w takim przypadku będzie działał podobnie do usługi ) lub funkcję (która zostanie zapisana w zmiennej jako funkcja wywołania zwrotnego).
  4. Usługa
    Usługa jest bardziej uproszczoną wersją fabryki, która jest ważna tylko wtedy, gdy wartość jest obiektem, i pozwala na zapisanie dowolnej logiki bezpośrednio w funkcji (tak jakby to był konstruktor), a także deklarowanie i uzyskiwanie dostępu właściwości obiektu za pomocą tego słowa kluczowego.
  5. Dostawca
    W przeciwieństwie do usługi, która jest uproszczoną wersją fabryki , dostawca jest bardziej złożonym, ale bardziej elastycznym sposobem inicjowania zmiennych „globalnych”, przy czym największą elastycznością jest możliwość ustawienia wartości z app.config.
    Działa to jak użycie kombinacji usługi i dostawcy , przekazując do dostawcy funkcję, która ma właściwości zadeklarowane za pomocą tego słowa kluczowego, którego można użyć z app.config.
    Następnie musi mieć osobną funkcję $ .get, która jest wykonywana przez AngularJS po ustawieniu powyższych właściwości za pomocą app.configpliku, a ta funkcja $ .get zachowuje się tak jak fabryka powyżej, ponieważ jego wartość zwracana jest używana do inicjalizacji zmiennych „globalnych”.
yoel halb
źródło
26

Moje zrozumienie jest bardzo proste poniżej.

Fabryka: po prostu tworzysz obiekt w fabryce i oddajesz go.

Usługa:

Masz tylko standardową funkcję, która używa tego słowa kluczowego do zdefiniowania funkcji.

Dostawca:

Istnieje $getobiekt, który zdefiniujesz i można go użyć do uzyskania obiektu, który zwraca dane.

Sajan
źródło
Nie pomieszałeś fabryki i serwisu? Usługi tworzą miejsca, w których następuje powrót do fabryki
Flavien Volken
Gdy podasz nazwę usługi jako argument do wstrzyknięcia, otrzymasz instancję funkcji. Innymi słowy nowa funkcja FunctionYouPassedToService (). Ta instancja obiektu staje się obiektem usługi, który AngularJS rejestruje i wstrzykuje później do innych usług / kontrolerów, jeśli jest to wymagane. // fabryka Gdy deklarujesz nazwę fabryki jako argument do wstrzyknięcia, otrzymasz wartość zwracaną przez wywołanie odwołania do funkcji przekazanego do module.factory.
sajan
Okej, więc… pod kątem fabryka jest singletonem, w którym „usługa” jest tak naprawdę fabryką (zgodnie z powszechnymi wzorami projektowymi)
Flavien Volken
25

Podsumowanie z dokumentów Angular :

  • Istnieje pięć typów receptur, które definiują sposób tworzenia obiektów: Wartość , Fabryka , Usługa , Dostawca i Stała .
  • Fabryka i serwis to najczęściej stosowane przepisy. Jedyna różnica między nimi polega na tym, że przepis Service działa lepiej dla obiektów niestandardowego typu, podczas gdy Factory może tworzyć prymitywy i funkcje JavaScript.
  • Provider przepis jest rdzeniem typu przepis i wszystkie pozostałe z nich to po prostu cukier syntaktyczny na nim.
  • Dostawca jest najbardziej złożonym rodzajem przepisu. Nie potrzebujesz go, chyba że budujesz fragment kodu wielokrotnego użytku, który wymaga globalnej konfiguracji.

wprowadź opis zdjęcia tutaj


Najlepsze odpowiedzi z SO:

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

Yarin
źródło
20

Wszystkie dobre odpowiedzi już. Chciałbym dodać jeszcze kilka punktów w serwisie i fabryce . Wraz z różnicą między serwisem / fabryką. Można też zadawać pytania:

  1. Czy powinienem skorzystać z serwisu czy fabryki? Co za różnica?
  2. Czy zachowują się tak samo, czy zachowują się tak samo?

Zacznijmy od różnicy między usługą a fabryką:

  1. Oba są singletonami : ilekroć Angular po raz pierwszy znajdzie je jako zależność, tworzy pojedyncze wystąpienie usługi / fabryki. Po utworzeniu instancji ta sama instancja jest używana na zawsze.

  2. Może być użyty do modelowania obiektu z zachowaniem : Oba mogą mieć metody, wewnętrzne zmienne stanu i tak dalej. Chociaż sposób pisania tego kodu będzie inny.

Usługi:

Usługa jest funkcją konstruktora, a Angular utworzy ją, wywołując new yourServiceName(). Oznacza to kilka rzeczy.

  1. Funkcje i zmienne instancji będą właściwościami this.
  2. Nie musisz zwracać wartości. Kiedy Angular wywoła new yourServiceName(), otrzyma thisobiekt ze wszystkimi właściwościami, które na niego umieścisz.

Przykładowy przykład:

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

Gdy Angular wstrzykuje tę MyServiceusługę do kontrolera, który jest od niej zależny, ten kontroler otrzyma funkcję MyService, na której może wywoływać funkcje, np. MyService.aServiceMethod ().

Uważaj nathis :

Ponieważ skonstruowana usługa jest obiektem, znajdujące się w niej metody mogą się do tego odwoływać, gdy są wywoływane:

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++;
  };
});

Możesz mieć ochotę sprawdzić ScoreKeeper.setScorew łańcuchu obietnic, na przykład, jeśli zainicjowałeś wynik, chwytając go z serwera: $http.get('/score').then(ScoreKeeper.setScore).Problem polega na tym, że ScoreKeeper.setScorezostanie wywołany z thispowiązaniem z, nulla otrzymasz błędy. Byłby lepszy sposób $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper)). Niezależnie od tego, czy zdecydujesz się użyć tego w swoich metodach serwisowych, czy nie, bądź ostrożny, jak je nazywasz.

Zwracanie wartości zService :

Ze względu na działanie konstruktorów JavaScript, jeśli zwrócisz wartość złożoną (i.e., an Object)z constructorfunkcji, program wywołujący otrzyma ten obiekt zamiast tego wystąpienia.

Oznacza to, że możesz w zasadzie skopiować i wkleić przykład fabryczny od dołu, zastąpić factorygo service, a to zadziała:

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

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

Więc kiedy Angular konstruuje twoją usługę za pomocą nowej MyService (), otrzyma ten obiekt interfejsu API zamiast instancji MyService.

Jest to zachowanie dla dowolnych złożonych wartości (obiektów, funkcji), ale nie dla typów pierwotnych.

Fabryki:

Fabryka jest zwykłą starą funkcją, która zwraca wartość. Wartość zwracana jest wprowadzana do rzeczy zależnych od fabryki. Typowym wzorcem fabrycznym w Angular jest zwracanie obiektu z funkcjami jako właściwości, jak poniżej:

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

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

  return api;
});

Wprowadzona wartość zależności fabrycznej jest wartością zwracaną przez fabrykę i nie musi to być obiekt. To może być funkcja

Odpowiedzi na pytania powyżej 1 i 2:

Przeważnie trzymaj się fabryk do wszystkiego. Ich zachowanie jest łatwiejsze do zrozumienia. Nie ma wyboru, czy zwrócić wartość, czy nie, a ponadto, jeśli zrobisz coś złego, nie zostaną wprowadzone żadne błędy.

Nadal nazywam je „usługami”, kiedy mówię o wprowadzaniu ich jako zależności.

Zachowanie serwisowe / fabryczne jest bardzo podobne, a niektórzy powiedzą, że jedno z nich jest w porządku. To trochę prawda, ale łatwiej mi postępować zgodnie z radą przewodnika po stylu Johna Papy i trzymać się fabryk. **

Ved
źródło
16

Dodatkowym wyjaśnieniem jest to, że fabryki mogą tworzyć funkcje / operacje podstawowe, podczas gdy usługi nie. Sprawdź to jsFiddle oparte na Epokk's: http://jsfiddle.net/skeller88/PxdSP/1351/ .

Fabryka zwraca funkcję, którą można wywołać:

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

Fabryka może również zwrócić obiekt za pomocą metody, którą można wywołać:

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

Usługa zwraca obiekt za pomocą metody, którą można wywołać:

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

Aby uzyskać więcej informacji, zobacz post, który napisałem o różnicy: http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/

skeller88
źródło
16

Są już dobre odpowiedzi, ale chcę się tylko z tym podzielić.

Po pierwsze: dostawca to sposób / przepis na stworzenieservice (obiektu singleton), który powinien być wstrzykiwany przez $ injector (jak AngulaJS podchodzi do wzoru IoC).

I wartość, fabryka, obsługa i stała (4 sposoby) - cukier składniowy w stosunku do sposobu dostawcy / recepty.

Jest Service vs Factoryczęść została pokryta: https://www.youtube.com/watch?v=BLzNCkPn3ao

Usługa polega newwłaściwie na słowach kluczowych, które, jak wiemy, obejmują 4 rzeczy:

  1. tworzy nowy obiekt
  2. łączy go z prototypeobiektem
  3. łączy się contextzthis
  4. i wraca this

A fabryka jest o fabryczne Pattern - zawiera funkcje, które zwracają obiekty tak Serwisu.

  1. możliwość korzystania z innych usług (mają zależności)
  2. inicjalizacja usługi
  3. opóźniona / leniwa inicjalizacja

I ten prosty / krótki film: obejmuje również dostawcę : https://www.youtube.com/watch?v=HvTZbQ_hUZY (tam widać, jak przechodzą od fabryki do dostawcy)

Przepis dostawcy jest używany głównie w konfiguracji aplikacji, zanim aplikacja zostanie w pełni uruchomiona / zainicjowana.

ses
źródło
14

Po przeczytaniu wszystkich tych postów stworzyło to dla mnie więcej zamieszania .. Ale nadal wszystkie wartościowe informacje .. w końcu znalazłem poniższą tabelę, która poda informacje z prostym porównaniem

  • Wtryskiwacz wykorzystuje przepisy do tworzenia dwóch rodzajów obiektów: usług i obiektów specjalnego przeznaczenia
  • Istnieje pięć typów receptur, które definiują sposób tworzenia obiektów: Wartość, Fabryka, Usługa, Dostawca i Stała.
  • Fabryka i serwis to najczęściej stosowane przepisy. Jedyna różnica między nimi polega na tym, że przepis na usługę działa lepiej w przypadku obiektów niestandardowego typu, podczas gdy fabryka może tworzyć prymitywy i funkcje JavaScript.
  • Przepis Provider jest podstawowym rodzajem przepisu, a wszystkie pozostałe są po prostu cukrem syntaktycznym.
  • Dostawca jest najbardziej złożonym rodzajem przepisu. Nie potrzebujesz go, chyba że budujesz fragment kodu wielokrotnego użytku, który wymaga globalnej konfiguracji.
  • Wszystkie obiekty specjalnego przeznaczenia oprócz kontrolera są zdefiniowane poprzez receptury fabryczne.

wprowadź opis zdjęcia tutaj

A dla początkującego rozumiem: - To może nie poprawić przypadku użycia, ale na wysokim poziomie jest to przypadek użycia tych trzech.

  1. Jeśli chcesz używać w module kątowym, należy skonfigurować funkcję konfiguracji jako dostawca

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

  1. Połączenia Ajax lub integracje stron trzecich muszą być usługami .
  2. W przypadku manipulacji danymi utwórz go fabrycznie

W przypadku podstawowych scenariuszy fabryka i obsługa zachowują się tak samo.

BEJGAM SHIVA PRASAD
źródło
13

Oto kod brojlerów, który wymyśliłem jako szablon kodu dla fabryki obiektów w AngularjS. Jako przykład zilustrowałem Car / CarFactory. Tworzy prosty kod implementacyjny w kontrolerze.

     <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>

Oto prostszy przykład. Korzystam z kilku bibliotek stron trzecich, które oczekują obiektu „Pozycja” pokazującego szerokość i długość geograficzną, ale za pomocą różnych właściwości obiektu. Nie chciałem zhakować kodu dostawcy, więc dostosowałem przekazywane przeze mnie obiekty „Pozycjonowanie”.

    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

;

James Earlywine
źródło
12

Wykorzystując jako odniesienie tę stronę i dokumentację (która wydaje się znacznie ulepszona od czasu ostatniego spojrzenia), stworzyłem następujące prawdziwe (-ish) światowe demo, które wykorzystuje 4 z 5 smaków dostawcy; Wartość, stała, fabryczna i pełna oferta dostawcy.

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

Działające demo .

net.uk.sweet
źródło
12

Ta odpowiedź dotyczy tematu / pytania

jak Factory, Service i Constant - to tylko cukier syntaktyczny na szczycie przepisu dostawcy?

LUB

jak fabryka, serwis i dostawcy są wewnętrznie podobni

w zasadzie to, co się dzieje

Kiedy tworzysz zestaw factory()it functionpodany w drugim argumencie dostawcy $geti zwraca go ( provider(name, {$get:factoryFn })), wszystko, co otrzymujesz, providerto brak właściwości / metody innej niż$get ta provider(oznacza, że ​​nie możesz tego skonfigurować)

Kod źródłowy fabryki

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

Dokonując service()zwrotu, użytkownik udostępnia fabrykę (), functionktóra wstrzykuje constructor(zwraca instancję konstruktora podaną w usłudze) i zwraca ją

Kod źródłowy usługi

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

Zasadniczo w obu przypadkach ostatecznie otrzymasz dostawcę $ ustawioną na podaną funkcję, ale możesz podać coś więcej niż $ get, jak pierwotnie możesz podać w dostawca () dla bloku konfiguracji

AB
źródło
11

Znam wiele doskonałych odpowiedzi, ale muszę podzielić się swoim doświadczeniem w korzystaniu z
1. servicew większości przypadków domyślnie
2. factoryużytych do stworzenia usługi dla tej konkretnej instancji

// 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){ ...
    }       
}
})();

i używając:

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;
... 
nguyên
źródło
10

Trochę późno na imprezę. Ale pomyślałem, że jest to bardziej pomocne dla tych, którzy chcieliby się dowiedzieć (lub mieć jasność) na temat opracowywania niestandardowych usług Angular JS przy użyciu metod produkcji, usług i dostawców.

Natknąłem się na ten film, który jasno wyjaśnia metody produkcji, usług i dostawcy w celu opracowania usług niestandardowych AngularJS:

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

Kod źródłowy: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service

Kod zamieszczony tutaj jest kopiowany bezpośrednio z powyższego źródła, z korzyścią dla czytelników.

Kod niestandardowej usługi „fabrycznej” jest następujący (która dotyczy zarówno wersji synchronicznej, jak i asynchronicznej wraz z wywoływaniem usługi http):

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

Kod metodyki „usługi” dla usług niestandardowych (jest podobny do „fabryki”, ale różni się od punktu widzenia składni):

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");
		});
	};

}]);

Kod metodyki „dostawcy” dla usług niestandardowych (jest to konieczne, jeśli chcesz opracować usługę, którą można skonfigurować):

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

Wreszcie interfejs użytkownika współpracujący z dowolną z powyższych usług:

<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>

użytkownik203687
źródło
10

Aby to wyjaśnić, ze źródła AngularJS można zobaczyć, że usługa wywołuje funkcję fabryczną, która z kolei wywołuje funkcję dostawcy:

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

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
}
Ricardo Rossi
źródło
9

Omówmy trzy sposoby obsługi logiki biznesowej w AngularJS w prosty sposób: ( Zainspirowany kursem Yaakova Coursera AngularJS )

SERWIS :

Składnia:

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>

Funkcje usługi:

  1. Instancja Lazily : Jeśli nie zostanie wstrzyknięta, nigdy nie zostanie utworzona. Aby go użyć, będziesz musiał wstrzyknąć go do modułu.
  2. Singleton : Jeśli zostanie wstrzyknięty do wielu modułów, wszystkie będą miały dostęp tylko do jednej konkretnej instancji. Dlatego bardzo wygodne jest udostępnianie danych między różnymi kontrolerami.

FABRYKA

Najpierw spójrzmy na składnię:

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;
}

Teraz używając dwóch powyższych w kontrolerze:

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

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

Funkcje fabryki:

  1. Podąża za fabrycznym wzorem projektowym. Fabryka jest centralnym miejscem, w którym powstają nowe obiekty lub funkcje.
  2. Nie tylko produkuje singleton, ale można także dostosowywać usługi.
  3. .service()Metoda jest fabryka , która zawsze daje ten sam rodzaj usługi, która jest pojedyncza i bez łatwy sposób skonfigurować jej zachowanie. Ta .service()metoda jest zwykle używana jako skrót do czegoś, co nie wymaga żadnej konfiguracji.

DOSTAWCA

Przyjrzyjmy się najpierw składni:

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;
  };
}

}

Funkcje dostawcy:

  1. Dostawca jest najbardziej elastyczną metodą tworzenia usług w Angular.
  2. Nie tylko możemy stworzyć fabrykę, która jest dynamicznie konfigurowalna, ale w czasie korzystania z fabryki, przy użyciu metody dostawcy, możemy niestandardowo skonfigurować fabrykę tylko raz przy ładowaniu całej naszej aplikacji.
  3. Fabrykę można następnie wykorzystać w aplikacji z niestandardowymi ustawieniami. Innymi słowy, możemy skonfigurować tę fabrykę przed uruchomieniem aplikacji. W rzeczywistości w dokumentacji kątowej wspomniano, że metoda dostawcy jest faktycznie wykonywana za sceną, gdy konfigurujemy nasze usługi za pomocą jednej .servicelub dwóch .factorymetod.
  4. $getTo funkcja, która jest przyłączona bezpośrednio do instancji dostawcy. Ta funkcja jest funkcją fabryczną . Innymi słowy, jest to po prostu jak ten, którego używamy, aby dostarczyć do .factorymetody. W tej funkcji tworzymy własną usługę. Ta $getwłaściwość, która jest funkcją, czyni dostawcę dostawcą . AngularJS oczekuje od dostawcy posiadania właściwości $ get, której wartość jest funkcją, którą Angular będzie traktował jako funkcję fabryczną. Ale to, co czyni tę konfigurację całego dostawcy bardzo wyjątkową, to fakt, że możemy udostępnić jakiś configobiekt wewnątrz dostawcy usług, i który zwykle zawiera wartości domyślne, które możemy później zastąpić w kroku, w którym możemy skonfigurować całą aplikację.
Pritam Banerjee
źródło
7

Fabryka: Fabryka, w której faktycznie tworzysz obiekt w fabryce i oddajesz go.
usługa: usługa, którą masz po prostu standardową funkcją, która używa tego słowa kluczowego do zdefiniowania funkcji.
dostawca: dostawca ma zdefiniowaną przez ciebie $ get i można go użyć do uzyskania obiektu, który zwraca dane.

Mohanrajan
źródło
7

Zasadniczo Dostawca, Fabryka i Usługa to wszystkie Usługi. Fabryka to szczególny przypadek Usługi, gdy wszystko czego potrzebujesz to funkcja $ get (), która pozwala napisać ją z mniejszym kodem.

Główne różnice między usługami, fabrykami i dostawcami to ich złożoność. Usługi są najprostszą formą, fabryki są nieco bardziej niezawodne, a dostawców można konfigurować w czasie wykonywania.

Oto podsumowanie, kiedy użyć każdego z nich:

Fabryka : Podana wartość musi zostać obliczona na podstawie innych danych.

Usługa : Zwracasz obiekt metodami.

Dostawca : Chcesz mieć możliwość skonfigurowania podczas fazy konfiguracji obiektu, który zostanie utworzony przed jego utworzeniem. Użyj dostawcy głównie w konfiguracji aplikacji, zanim aplikacja zostanie w pełni zainicjowana.

eGhoul
źródło
erm. Wartość, fabryka, obsługa i stała - to tylko cukier syntaktyczny na szczycie przepisu dostawcy. Dokumenty Angularjs - dostawcy
Sudarshan_SMD
tak, zgadzam się, teraz z kątowym 4 nie odczuwamy już tego bólu głowy
eGhoul
4

1.Usługi są obiektami singletonowymi, które są tworzone w razie potrzeby i nigdy nie są czyszczone do końca cyklu życia aplikacji (gdy przeglądarka jest zamknięta). Kontrolery są niszczone i czyszczone, gdy nie są już potrzebne.

2. Najłatwiejszym sposobem utworzenia usługi jest użycie metody factory (). Metoda factory () pozwala nam zdefiniować usługę, zwracając obiekt zawierający funkcje serwisowe i dane serwisowe. Funkcja definicji usługi to miejsce, w którym umieszczamy nasze usługi do wstrzykiwania, takie jak $ http i $ q. Dawny:

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

Korzystanie z fabryki () w naszej aplikacji

Łatwo jest korzystać z fabryki w naszej aplikacji, ponieważ możemy po prostu wstrzyknąć ją tam, gdzie jest potrzebna w czasie wykonywania.

angular.module('myApp')
.controller('MainController', function($scope, User) {
  $scope.saveUser = User.save;
});
  1. Natomiast metoda service () pozwala nam stworzyć usługę poprzez zdefiniowanie funkcji konstruktora. Możemy użyć obiektu prototypowego do zdefiniowania naszej usługi, zamiast surowego obiektu javascript. Podobnie jak w przypadku metody factory (), ustawimy także pliki do wstrzykiwań w definicji funkcji.
  2. Najniższym sposobem na utworzenie usługi jest użycie metody dostarczania (). Jest to jedyny sposób na stworzenie usługi, którą możemy skonfigurować za pomocą funkcji .config (). W przeciwieństwie do poprzednich metod, ustawiamy pliki do wstrzykiwania w zdefiniowanej definicji tej funkcji. $ Get ().
Shankar Gangadhar
źródło
-3

Różnica polega na cukrze syntaktycznym . Potrzebny jest tylko dostawca. Innymi słowy, tylko dostawca jest rzeczywisty kątowy, wszystkie pozostałe są wyprowadzane (w celu zmniejszenia kodu). Istnieje również prosta wersja o nazwie Value (), która zwraca tylko wartość, brak obliczeń lub funkcji. Nawet wartość pochodzi od dostawcy!

Skąd więc takie komplikacje, dlaczego nie możemy po prostu skorzystać z usługodawcy i zapomnieć o wszystkim innym? Ma nam pomóc w łatwym pisaniu kodu i lepszej komunikacji. I odpowiedź byłaby trudna, im bardziej skomplikowana, tym lepsza będzie sprzedaż ram.


  • Dostawca, który może zwrócić wartość = wartość
  • Dostawca, który może po prostu utworzyć wystąpienie i zwrócić = Fabryka (+ Wartość)
  • Dostawca, który może utworzyć instancję + wykonać coś = usługa (+ fabryka, + wartość)
  • Dostawca = musi zawierać właściwość o nazwie $ get (+ Factory, + Service, + Value)

Zastrzyk kątowy daje nam pierwszą wskazówkę, jak dojść do tego wniosku.

„$ injector służy do pobierania instancji obiektów zdefiniowanych przez dostawcę ”, nie usługa, nie fabryka, ale dostawca.

Lepszą odpowiedzią byłoby: „Usługa Angular jest tworzona przez fabrykę usług. Te fabryki usług to funkcje, które z kolei są tworzone przez usługodawcę. Dostawcy usług są funkcjami konstruktora. Po utworzeniu instancji muszą zawierać właściwość o nazwie $ get, która pełni funkcję fabryki usług. ”

Więc główny dostawca i wtryskiwacz i wszystko się ułoży :). Interesujące staje się w Typescript, kiedy $ get można zaimplementować u dostawcy, dziedzicząc po IServiceProvider.

Niebieskie chmury
źródło