AngularJS: Dienst vs. Anbieter vs. Fabrik

Was sind die Unterschiede zwischen einem Service, Provider und Factory in AngularJS?

Lösung

Von der AngularJS-Mailingliste erhielt ich einen erstaunlichen Thread, der Service vs. Factory vs. Provider und deren Injektionsverwendung erklärt. Kompilieren Sie die Antworten:

Services

Syntax: module.service( 'serviceName', function );
Ergebnis: Wenn Sie serviceName als injizierbares Argument deklarieren, werden Sie mit einer Instanz der Funktion versorgt. Mit anderen Worten neue FunktionYouPassedToService().

*Fabriken

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

Provider

Syntax: module.provider( 'providerName', function );
Ergebnis: Wenn Sie providerName als injizierbares Argument deklarieren, werden Sie mit (new ProviderFunction()).$get() versorgt. Die Konstruktorfunktion wird instanziiert, bevor die $get-Methode aufgerufen wird - ProviderFunction ist die Funktionsreferenz, die an module.provider übergeben wird.

Provider haben den Vorteil, dass sie während der Konfigurationsphase des Moduls konfiguriert werden können.

Siehe hier für den bereitgestellten Code.

Hier's eine große weitere Erklärung von Misko:

provide.value('a', 123);

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

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

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

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

Fabrik" ist also eine Funktion, die für die Erstellung des Wertes verantwortlich ist. Beachten Sie, dass die Fabrikfunktion nach anderen Abhängigkeiten fragen kann.

Was aber, wenn Sie mehr OO sein wollen und eine Klasse namens Greeter haben?

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

Dann müssten Sie zum Instanziieren schreiben

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

Dann könnten wir nach 'greeter' im Controller wie folgt fragen

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

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

Was aber, wenn wir die Klasse Greeter vor der Injektion konfigurieren wollten? Dann könnten wir schreiben

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

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

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

Dann können wir dies tun:

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

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

Nebenbei bemerkt, service, factory und value sind alle von provider abgeleitet.

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

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

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

[JS Fiddle Demo][1]

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

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

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

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

//provider style, full blown, configurable version     
myApp.provider('helloWorld', function() {

    this.name = 'Default';

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

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

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

function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {

    $scope.hellos = [
        helloWorld.sayHello(),
        helloWorldFromFactory.sayHello(),
        helloWorldFromService.sayHello()];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

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

Beim Herumspielen mit Anbietern ist mir etwas Interessantes aufgefallen.

Die Sichtbarkeit von Injectables ist bei Providern anders als bei Services und Factories. Wenn Sie eine AngularJS "Konstante" deklarieren (z. B. myApp.constant('a', 'Robert');), können Sie sie in Dienste, Factories und Anbieter injizieren.

Wenn Sie jedoch einen AngularJS "Wert" deklarieren (z. B. myApp.value('b', {Name: 'Jones'});), können Sie ihn in Dienste und Factories injizieren, aber NICHT in die Funktion zur Erstellung des Providers. Sie können es jedoch in die Funktion "$get" injizieren, die Sie für Ihren Anbieter definieren. Dies wird in der AngularJS-Dokumentation erwähnt, ist aber leicht zu übersehen. Sie finden es auf der Seite %provide in den Abschnitten über die Methoden value und constant.

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