AngularJS: Dienst vs aanbieder vs fabriek

Wat zijn de verschillen tussen een Service, Provider en Factory in AngularJS?

Oplossing

Van de AngularJS mailing list kreeg ik een geweldige thread die service vs factory vs provider en hun injectie gebruik uitlegt. Compileren van de antwoorden:

Services

Syntax: module.service( 'serviceName', function );
Resultaat: Als je serviceName als injectable argument declareert krijg je een instantie van de functie. Met andere woorden new FunctionYouPassedToService().

Factories

Syntax: module.factory( 'factoryName', function );
Resultaat: Wanneer je factoryName als een injecteerbaar argument declareert, krijg je de waarde die wordt geretourneerd door het aanroepen van de functieverwijzing die aan module.factory is doorgegeven.

Providers

Syntaxis: module.provider( 'providerName', function );
Resultaat: Bij het declareren van providerName als een injecteerbaar argument krijg je (new ProviderFunction()).$get(). De constructor functie wordt geïnstantieerd voordat de $get methode wordt aangeroepen - ProviderFunction is de functie referentie die wordt doorgegeven aan module.provider.

Providers hebben het voordeel dat ze geconfigureerd kunnen worden tijdens de module configuratie fase.

Zie hier voor de meegeleverde code.

Hier's een geweldige verdere uitleg door Misko:

provide.value('a', 123);

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

In dit geval geeft de injector gewoon de waarde terug zoals die is. Maar wat als je de waarde wilt berekenen? Gebruik dan een fabriek

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

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

Dus factory is een functie die verantwoordelijk is voor het creëren van de waarde. Merk op dat de factory functie kan vragen om andere afhankelijkheden.

Maar wat als je meer OO wilt zijn en een klasse wilt hebben met de naam Greeter?

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

Dan zou je om te instantiëren moeten schrijven

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

Dan zouden we kunnen vragen om 'greeter' in controller zoals dit

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

Maar dat is veel te wijd. Een kortere manier om dit te schrijven zou zijn provider.service('greeter', Greeter);

Maar wat als we de Greeter klasse willen configureren voor de injectie? Dan zouden we kunnen schrijven

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

Dan kunnen we dit doen:

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

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

Terzijde: service, factory, en value zijn allemaal afgeleid van 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;
  });
};
Commentaren (10)

[JS Fiddle Demo][1]

" Hello world " voorbeeld met 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>
Commentaren (7)

Ik merkte iets interessants op toen ik met providers speelde.

Zichtbaarheid van injectables is anders voor providers dan het is voor services en factories. Als je een AngularJS "constant" declareert (bijvoorbeeld, myApp.constant('a', 'Robert');), kun je het injecteren in services, factories, en providers.

Maar als je een AngularJS "value" declareert (bijvoorbeeld, myApp.value('b', {name: 'Jones'});), dan kun je het injecteren in services en factories, maar NIET in de provider-creërende functie. Je kunt het echter wel injecteren in de $get functie die je definieert voor je provider. Dit wordt vermeld in de AngularJS documentatie, maar het'is gemakkelijk te missen. Je kunt het vinden op de %provide pagina in de secties over de value en constant methods.

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