Dostawca (Provider)

Kontynuujemy temat z poprzedniego rozdziału i tym samym pozostajemy w świecie usług. Usługa typu provider, o której chcemy napisać jest mocno powiązana z usługami typu factory i service. Wszystkie te trzy typy mają jedną cechę wspólną. Okazuje się, że tak samo jak service wywołuje factory, tak samo factory wywołuje dalej providera! Provider ten jest źródłem dla pozostałych usług w Angularze.
function factory(name, factoryFn, enforce) {
    return provider(name, {
        $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
    });
}
Natomiast mimo tego, że pod spodem każdej fabryki znajduje się provider, to tutaj w przeciwieństwie do relacji typu service - factory, nie można powiedzieć, że provider znajduje się z nimi w relacji równoważnej. Co prawda jest on odpowiedzialny za dostarczenie obiektu usługi, niemniej nie posiada on dokładnie takich samych cech jak service czy factory.

Podstawowa różnica polega na tym, że do providera nie możemy wstrzykiwać zależności w postaci serwisów i fabryk. Tak więc nie wstrzykniemy do niego ani angularowego serwisu $http czy $q, ani też żadnej innej usługi stworzonej przez nas w aplikacji. Dodatkowo, podstawowym przeznaczeniem providera jest dostarczanie obiektów usługowych działających w ramach całej aplikacji i ładowanych od razu podczas jej startu.

Ze względu na wspomniane właściwości provider doskonale nadaje się do konfigurowania aplikacji w ramach metody config (w przeciwieństwie do fabryk i serwisów - ich nie możemy podpiąć za pomocą tej metody).

Zobaczmy teraz jak użycie providera wygląda w praktyce.

Provider w akcji

Tworzymy providera dostarczającego informacje o naszej aplikacji (numer wersji, data releasu) i podpinamy go do modułu aplikacji. To co jest charakterystyczne dla providerów w AngularJS, to konstrukcja opierająca się na wykorzystaniu własności $get. Własności tej przypisujemy funkcję fabryki opakowującej de facto funkcję serwisu, co w efekcie dopełnia klamrą model usług: provider - factory - service.
angular.module('Appa').provider("ApplicationInfo", [function () {

    var versionLocal = "UNKNOWN";
    var releaseDateLocal = "UNKNOWN";
    
    this.setVersion = function(versionValue) {
    	version = versionValue;
    }
    
    this.setReleaseDate = function(releaseDateValue) {
    	releaseDate = releaseDateValue;
    }	
    
    this.$get = [function () {
    
        return {
            getApplicationInfo : function() {
                return {
                    version : versionLocal,
                    releaseDate : releaseDateLocal
                };
            }
        };	
    }];
}]);
Jeśli chcemy to możemy użyć providera w taki sposób aby zarówno wykorzystywać jego możliwości konfiguracyjne, jak i używać go razem z fabryką, która będzie posiadać metody służące przetwarzaniu danych. Można wykorzystać takie rozwiązanie jeśli na przykład bedziemy chcieli stworzyć generyczną usługę przygotowaną do użycia w różnych aplikacjach. Na poniższym przykładzie pokazujemy podobny kod usługi, jak ten który opisujemy w ramach rozdziału dotyczącego fabryki i serwisu, jednak tutaj przyjmujemy założenie, że będzie on użyteczny globalnie (nie będzie się opierał o model jednej konkretnej aplikacji).
this.$get = ['$http', '$q', '$log', 'Upload', function($http, $q, $log, Upload) {

    var dataCache = {
        appaCategories : null,
        appaTypes : null,
        appaAttributes : null,
        ...
    };                    
    
    function getAppaTypes() {
    
    	dataCache.appaTypes = ...
    }
    
    function getAppaCategories() {
    
    	dataCache.appaCategories = ...
    }
    
    function getAppaAttributes() {
    
    	dataCache.appaAttributes = ...
    }
    
    ...
    
    return {
        getAppaTypes : getAppaTypes,
        getAppaCategories : getAppaCategories,
        getAppaAttributes : getAppaAttributes,
        ...
    };
}];
Spostrzegawcze osoby z pewnością zauważyły, że nazwa providera wyjątkowo została pozbawiona przez nas suffixu Provider. Nie jest to przypadek. Celowo użyliśmy takiej nazwy, ponieważ w bloku konfiguracyjnym do nazwy wstrzykniętego providera i tak zostanie automatycznie dołączone słowo Provider.

Ustawiamy zatem numer wersji oraz datę releasu w ramach funkcji config. Jak widać zgodnie z tym co napisaliśmy w poprzednim akapicie, mimo że nazwę providera określiliśmy jako ApplicationInfo, to do naszego bloku konfiguracyjnego wstrzykujemy go jako ApplicationInfoProvider:
angular.module("Appa").config(["ApplicationInfoProvider", function (ApplicationInfoProvider) {
    ApplicationInfoProvider.setVersion('2.0.0');
    ApplicationInfoProvider.setReleaseDate('28.10.2018');
}]);
Rozwiązanie z automatycznym doklejaniem suffixu nie było by wcale złe gdyby nie fakt, że teraz jeśli będziemy chcieli użyć naszego providera aby odczytać skonfigurowane wartości w którymś z naszych kontrolerów w aplikacji, to będziemy to robić wstrzykując go za pomocą nazwy źródłowej, a więc ApplicationInfo!
angular.module('Appa').controller('StartappaCtrl', ['ApplicationInfo', function(ApplicationInfo) {	
    console.log(ApplicationInfo.getApplicationInfo().version);
    console.log(ApplicationInfo.getApplicationInfo().releaseDate);
}]);
Na koniec jeszcze jedna ważna sprawa, o której należy wspomnieć w kontekście dostawców. Funkcje, które są przypisane bezpośrednio do obiektu providera (w naszym przykładzie są to: setVersion oraz setReleaseDate), są widoczne tylko z poziomu bloku config. Nie dostaniemy się do nich z poziomu kontrolera. Natomiast funkcje fabryki i serwisu (zawartych w providerze) odwrotnie - nie są dostępne z poziomu funkcji config. Możemy dostać się do nich standardowo z poziomu kontrolera (np. po to by odczytać wartość ustawioną w konfiguracji).
Nasza rekomendacja
Mimo, że teoretycznie jest to możliwe (poprzez implementowanie kodu fabryki w providerze), to jednak nie jest zalecane aby providera wykorzystywać jako podstawowy typ dla usług budowanych wokół modelu domenowego aplikacji. Pownien on nam służyć jako dostawca usług niezależnych od modelu biznesowego konkretnej aplikacji, tak aby był możliwy do przeniesienia do innych aplikacji. Dodatkowo, dzięki odseparowaniu części konfiguracyjnej od wykonawczej, może być bardzo pomocny we wdrażaniu różnych wersji konfiguracji dla tej samej usługi.
Używamy w StartAPPa


Aplikacja ze względu na szereg ustawień konfiguracyjnych posiada specjalnie zdefiniowanego providera z wieloma elementami konfiguracyjnymi. W kursach stosujemy głównie gotowe providery, takie jak $growlProvider czy $stateProvider, dlatego też zamieściliśmy w bieżącym rozdziale cały przykład customowego providera, który możecie w łatwy sposób wpiąć do swoich aplikacji (dodając kod Javascript do app.js oraz kontroler o nazwie StartappaCtrl do pliku index.html).
Linki
https://docs.angularjs.org/guide/providers#provider-recipe
https://docs.angularjs.org/api/auto/service/$provide

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy:
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .