AngularJS: Tjeneste vs. leverandør vs. fabrikk

Hva er forskjellen mellom en Service, Provider og Factory i AngularJS?

Løsning

Fra AngularJS-postlisten fikk jeg en fantastisk tråd som forklarer service vs fabrikk vs leverandør og deres injeksjonsbruk. Kompilerer svarene:

Services

Syntaks: module.service( 'serviceName', function );
Resultat: Når du deklarerer serviceName som et injiserbart argument vil du få en forekomst av funksjonen. Med andre ord new FunctionYouPassedToService().

Factories

Syntaks: module.factory( 'factoryName', function );
Resultat: Når du deklarerer factoryName som et injiserbart argument, vil du få verdien som returneres ved å påkalle funksjonsreferansen som sendes til module.factory.

Tilbydere

Syntaks: module.provider( 'providerName', function );
Resultat: Når du deklarerer providerName som et injiserbart argument vil du få (new ProviderFunction()).$get(). Konstruktørfunksjonen instansieres før $get-metoden kalles - ProviderFunction er funksjonsreferansen som sendes til module.provider.

Providers har den fordelen at de kan konfigureres under modulkonfigurasjonsfasen.

Se her for den medfølgende koden.

Her er en god ytterligere forklaring av Misko:

provide.value('a', 123);

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

I dette tilfellet returnerer injektoren ganske enkelt verdien som den er. Men hva om du vil beregne verdien? Bruk da en fabrikk

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

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

factory er en funksjon som er ansvarlig for å skape verdien. Legg merke til at factory-funksjonen kan be om andre avhengigheter.

Men hva om du vil være mer OO og ha en klasse som heter Greeter?

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

For å instansiere må du da skrive

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

Deretter kan vi be om 'greeter' i kontrolleren slik

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

Men det er altfor omstendelig. En kortere måte å skrive dette på ville være provider.service('greeter', Greeter);

Men hva om vi ønsket å konfigurere Greeter klasse før injeksjon? Da kunne vi skrive

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

Da kan vi gjøre dette:

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

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

Som en side bemerkning er service, factory og value alle avledet fra provider.

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;
  });
};
Kommentarer (10)

[JS Fiddle Demo][1]

" Hello world " eksempel med 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>

<div ng-controller="MyCtrl">
    {{hellos}}
</div>
Kommentarer (7)

Jeg la merke til noe interessant da jeg lekte med leverandører.

Synligheten av injeksjoner er annerledes for leverandører enn for tjenester og fabrikker. Hvis du erklærer en AngularJS "konstant" (for eksempel myApp.constant('a', 'Robert');), kan du injisere den i tjenester, fabrikker og leverandører.

Men hvis du erklærer en AngularJS "verdi" (for eksempel., myApp.value('b', {name: 'Jones'});), kan du injisere den i tjenester og fabrikker, men IKKE i leverandøropprettingsfunksjonen. Du kan imidlertid injisere den i $get-funksjonen som du definerer for leverandøren din. Dette er nevnt i AngularJS-dokumentasjonen, men det er lett å overse. Du finner det på %provide-siden i avsnittene om verdi- og konstantmetoder.

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>
Kommentarer (0)