AngularJS: Serviço x provedor x fábrica

3319

Quais são as diferenças entre um Service, Providere Factoryem AngularJS?

Lior
fonte
244
Eu descobri que todos os termos angulares eram intimidadores para iniciantes. Começamos com esta folha de dicas que era um pouco mais fácil para nossos programadores entenderem enquanto aprendíamos o Angular demisx.github.io/angularjs/2014/09/14/… . Espero que isso ajude sua equipe também.
Demisx
7
Na minha opinião, a melhor maneira de entender a diferença é usar a documentação da Angular: docs.angularjs.org/guide/providers , é extremamente bem explicada e usa um exemplo peculiar para ajudá-lo a entendê-la.
Rafael Merlin
3
@ Blaise Obrigado! De acordo com meu comentário no post, deixei de fora intencionalmente, pois 99% dos casos de uso da minha experiência podem ser tratados com êxito via service.factory. Não queria complicar ainda mais esse assunto.
demisx
3
Eu acho esta discussão também muito útil stackoverflow.com/questions/18939709/…
Anand Gupta
3
Aqui estão algumas boas respostas sobre comoservices,factorieseprovidersobras.
Mistalis 18/10/16

Respostas:

2866

Na lista de discussão do AngularJS, recebi uma discussão incrível que explica serviço x fábrica x provedor e seu uso de injeção. Compilando as respostas:

Serviços

Sintaxe: module.service( 'serviceName', function );
Resultado: Ao declarar serviceName como um argumento injetável, você receberá uma instância da função. Em outras palavras new FunctionYouPassedToService() .

Fábricas

Sintaxe: module.factory( 'factoryName', function );
Resultado: Ao declarar factoryName como um argumento injetável, você receberá o valor retornado invocando a referência de função passada para module.factory .

Fornecedores

Sintaxe: module.provider( 'providerName', function );
Resultado: Ao declarar providerName como um argumento injetável, você será fornecido (new ProviderFunction()).$get() . A função construtora é instanciada antes que o método $ get seja chamado - ProviderFunctioné a referência da função passada para module.provider.

Os fornecedores têm a vantagem de poderem ser configurados durante a fase de configuração do módulo.

Veja aqui o código fornecido.

Aqui está uma grande explicação adicional de Misko:

provide.value('a', 123);

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

Nesse caso, o injetor simplesmente retorna o valor como está. Mas e se você quiser calcular o valor? Então use uma fábrica

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

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

Assim factoryé uma função que é responsável por criar o valor. Observe que a função de fábrica pode solicitar outras dependências.

Mas e se você quiser ter mais OO e ter uma classe chamada Greeter?

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

Então, para instanciar, você teria que escrever

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

Então poderíamos pedir 'greeter' em um controlador como este

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

Mas isso é muito prolixo. Uma maneira mais curta de escrever isso seriaprovider.service('greeter', Greeter);

Mas e se quiséssemos configurar a Greeterclasse antes da injeção? Então poderíamos escrever

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

Então podemos fazer isso:

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

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

Como uma nota lateral, service, factory, e valuesão todos derivados do provedor.

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
fonte
58
Consulte também stackoverflow.com/a/13763886/215945, que discute as diferenças entre serviço e fábrica.
Mark Rajcok
3
Na edição 611, adicionei o uso de constantes e valores angulares. Para demonstrar as diferenças dos outros já mostrados. jsbin.com/ohamub/611/edit
Nick
17
Embora um serviço seja chamado criando uma instância da função. Na verdade, ele é criado apenas uma vez por injetor, o que o torna como singleton. docs.angularjs.org/guide/dev_guide.services.creating_services
angelokh
33
Este exemplo poderia ser incrível se usasse um exemplo prático claro. Eu me perco tentando descobrir o que o ponto de coisas como toEquale greeter.Greeté. Por que não usar algo um pouco mais real e relacionável?
precisa saber é o seguinte
5
Usar a função expect () é uma má escolha para explicar algo. Use o código do mundo real da próxima vez.
Craig
812

JS Fiddle Demo

Exemplo "Olá, mundo", com 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
fonte
2
Não thismuda o contexto na $getfunção? - você não se refere mais ao provedor instanciado nessa função.
Nate-Wilkins
12
@ Nate: thisnão muda o contexto, na verdade, porque o que está sendo chamado é new Provider(). $ Get (), para onde Providera função está sendo passada app.provider. Ou seja, $get()está sendo chamado como método no construído Provider, portanto, thisserá referido Providercomo o exemplo sugere.
Brandon
1
@Brandon Ohh ok, isso é legal, então. Confuso à primeira vista - obrigado pelo esclarecimento!
Nate-Wilkins
3
Por que recebo Unknown provider: helloWorldProvider <- helloWorldao executar isso localmente? Comentando, o mesmo erro para os outros 2 exemplos. Existe alguma configuração de provedor oculto? (Angular 1.0.8) - Encontrado: stackoverflow.com/questions/12339272/…
Antoine
4
É o motivo pelo qual @Antoine obtém o erro "Fornecimento desconhecido: helloWorldProvider" porque no seu código .config você usa 'helloWorldProvider', mas quando define o provedor em myApp.provider ('helloWorld', function ()), usa 'Olá Mundo'? Em outras palavras, no seu código de configuração, como a angular sabe que você está se referindo ao provedor helloWorld? Obrigado
jmtoung
645

TL; DR

1) Ao usar um Factory, você cria um objeto, adiciona propriedades a ele e retorna o mesmo objeto. Quando você passar esta fábrica para o seu controlador, essas propriedades no objeto estarão agora disponíveis nesse controlador através da sua fábrica.

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) Ao usar o Serviço , o AngularJS o instancia nos bastidores com a palavra-chave 'new'. Por isso, você adicionará propriedades a 'this' e o serviço retornará 'this'. Quando você passa o serviço para o seu controlador, essas propriedades agora estarão disponíveis nesse controlador através do seu serviço.

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

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



3) Os provedores são o único serviço que você pode transmitir para a função .config (). Use um provedor quando desejar fornecer configuração em todo o módulo para seu objeto de serviço antes de disponibilizá-lo.

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



Não TL; DR

1)
Fábricas de fábrica são a maneira mais popular de criar e configurar um serviço. Realmente não há muito mais do que o TL; DR disse. Você acabou de criar um objeto, adicionar propriedades a ele e retornar o mesmo objeto. Então, quando você passar a fábrica para o seu controlador, essas propriedades no objeto estarão agora disponíveis nesse controlador através da sua fábrica. Um exemplo mais extenso está abaixo.

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

Agora, quaisquer propriedades que anexarmos a 'service' estarão disponíveis quando passarmos 'myFactory' para o nosso controlador.

Agora vamos adicionar algumas variáveis ​​'privadas' à nossa função de retorno de chamada. Eles não estarão diretamente acessíveis no controlador, mas, eventualmente, configuraremos alguns métodos getter / setter no 'service' para poder alterar essas variáveis ​​'privadas' quando necessário.

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

Aqui você notará que não estamos anexando essas variáveis ​​/ funções ao 'serviço'. Estamos simplesmente criando-os para usá-los ou modificá-los posteriormente.

  • baseUrl é o URL base que a API do iTunes requer
  • _artist é o artista que queremos pesquisar
  • _finalUrl é o URL final e totalmente construído para o qual faremos a chamada para o iTunes
  • makeUrl é uma função que criará e retornará nossa URL amigável do iTunes.

Agora que nossas variáveis ​​e funções auxiliares / particulares estão em vigor, vamos adicionar algumas propriedades ao objeto 'service'. Tudo o que colocamos em 'serviço' pode ser usado diretamente dentro de qualquer controlador no qual passamos 'myFactory'.

Vamos criar métodos setArtist e getArtist que simplesmente retornam ou definem o artista. Também vamos criar um método que chamará a API do iTunes com nosso URL criado. Esse método retornará uma promessa que será cumprida assim que os dados voltarem da API do iTunes. Se você não teve muita experiência no uso de promessas no AngularJS, recomendo fazer um mergulho profundo nelas.

Abaixo, o setArtist aceita um artista e permite que você o defina. O getArtist retorna o artista. O callItunes chama primeiro makeUrl () para criar o URL que usaremos com nossa solicitação $ http. Em seguida, ele configura um objeto de promessa, faz uma solicitação $ http com nosso URL final e, como $ http retorna uma promessa, podemos chamar .success ou .error após nossa solicitação. Em seguida, resolvemos nossa promessa com os dados do iTunes ou a rejeitamos com uma mensagem dizendo 'Houve um erro'.

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

Agora nossa fábrica está completa. Agora podemos injetar 'myFactory' em qualquer controlador e, em seguida, poderemos chamar nossos métodos que anexamos ao nosso objeto de serviço (setArtist, getArtist e 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);
      })
  }
});

No controlador acima, estamos injetando no serviço 'myFactory'. Em seguida, definimos propriedades em nosso objeto $ scope com dados de 'myFactory'. O único código complicado acima é se você nunca lidou com promessas antes. Como o callItunes está retornando uma promessa, podemos usar o método .then () e definir apenas $ scope.data.artistData quando nossa promessa for cumprida com os dados do iTunes. Você notará que nosso controlador é muito 'fino' (esta é uma boa prática de codificação). Todos os nossos dados lógicos e persistentes estão localizados em nosso serviço, não em nosso controlador.

2) Serviço
Talvez a maior coisa a saber quando se lida com a criação de um Serviço é que ele é instanciado com a palavra-chave 'new'. Para os gurus do JavaScript, isso deve fornecer uma grande dica sobre a natureza do código. Para aqueles com conhecimentos limitados em JavaScript ou para aqueles que não conhecem muito bem o que a palavra-chave 'new' realmente faz, vamos revisar alguns fundamentos do JavaScript que eventualmente nos ajudarão a entender a natureza de um Serviço.

Para realmente ver as alterações que ocorrem quando você invoca uma função com a palavra-chave 'new', vamos criar uma função e invocá-la com a palavra-chave 'new', depois vamos mostrar o que o intérprete faz quando vê a palavra-chave 'new'. Os resultados finais serão os mesmos.

Primeiro, vamos criar nosso Construtor.

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

Essa é uma função típica do construtor JavaScript. Agora, sempre que invocarmos a função Pessoa usando a palavra-chave 'new', 'this' será vinculado ao objeto recém-criado.

Agora, vamos adicionar um método ao protótipo de nossa Person, para que fique disponível em todas as instâncias da nossa classe de Person.

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

Agora, como colocamos a função sayName no protótipo, todas as instâncias de Person poderão chamar a função sayName para alertar o nome dessa instância.

Agora que temos nossa função construtora Person e nossa função sayName em seu protótipo, vamos criar uma instância de Person e chamar a função sayName.

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

Portanto, todo o código para criar um construtor Person, adicionar uma função a seu protótipo, criar uma instância Person e chamar a função em seu protótipo se parece com isso.

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’

Agora, vamos ver o que realmente está acontecendo quando você usa a palavra-chave 'new' em JavaScript. A primeira coisa que você deve notar é que, depois de usar 'new' em nosso exemplo, podemos chamar um método (sayName) em 'tyler' como se fosse um objeto - é porque é. Então, primeiro, sabemos que nosso construtor Person está retornando um objeto, se podemos ver isso no código ou não. Segundo, sabemos que, como nossa função sayName está localizada no protótipo e não diretamente na instância Person, o objeto que a função Person está retornando deve delegar ao seu protótipo em pesquisas com falha. Em termos mais simples, quando chamamos tyler.sayName (), o intérprete diz: “OK, vou olhar para o objeto 'tyler' que acabamos de criar, localize a função sayName e chame-a. Espere um minuto, não o vejo aqui - tudo o que vejo é nome e idade, deixe-me verificar o protótipo. Sim, parece que está no protótipo, deixe-me chamá-lo. ”.

Abaixo está o código de como você pode pensar sobre o que a palavra-chave 'nova' está realmente fazendo em JavaScript. É basicamente um exemplo de código do parágrafo acima. Coloquei a 'visão do intérprete' ou a maneira como o intérprete vê o código dentro das notas.

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

Agora, tendo esse conhecimento do que a palavra-chave 'new' realmente faz em JavaScript, a criação de um Serviço no AngularJS deve ser mais fácil de entender.

A principal coisa a entender ao criar um Serviço é saber que os Serviços são instanciados com a palavra-chave 'new'. Combinando esse conhecimento com nossos exemplos acima, você deve reconhecer agora que anexará suas propriedades e métodos diretamente a 'this', que serão devolvidos pelo próprio Serviço. Vamos dar uma olhada nisso em ação.

Diferentemente do que fizemos originalmente com o exemplo do Factory, não precisamos criar um objeto e, em seguida, retorná-lo, porque, como mencionado muitas vezes antes, usamos a palavra-chave 'new' para que o intérprete crie esse objeto, faça-o delegar para é protótipo e, em seguida, devolva-o para nós sem que tenhamos que fazer o trabalho.

Primeiramente, vamos criar nossa função 'privada' e auxiliar. Isso deve parecer muito familiar, já que fizemos exatamente a mesma coisa com nossa fábrica. Não vou explicar o que cada linha faz aqui, porque fiz isso no exemplo de fábrica; se você estiver confuso, releia o exemplo de fábrica.

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

Agora, anexaremos todos os nossos métodos que estarão disponíveis em nosso controlador a 'this'.

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

});

Agora, assim como em nossa fábrica, setArtist, getArtist e callItunes estarão disponíveis em qualquer controlador em que passarmos o myService. Aqui está o controlador myService (que é quase exatamente o mesmo que o nosso controlador de fábrica).

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

Como mencionei antes, quando você realmente entende o que 'novo' faz, os Serviços são quase idênticos às fábricas no AngularJS.

3) Fornecedor

A principal coisa a lembrar sobre os Provedores é que eles são o único serviço que você pode passar para a parte app.config do seu aplicativo. Isso é de grande importância se você precisar alterar parte do objeto de serviço antes que ele esteja disponível em qualquer outro lugar do aplicativo. Embora muito parecido com Serviços / Fábricas, existem algumas diferenças que discutiremos.

Primeiro, configuramos nosso provedor de maneira semelhante ao nosso serviço e fábrica. As variáveis ​​abaixo são nossa função 'privada' e auxiliar.

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

* Novamente, se qualquer parte do código acima estiver confusa, consulte a seção Factory, onde explico o que isso faz com mais detalhes.

Você pode pensar em Provedores como tendo três seções. A primeira seção são as variáveis ​​/ funções 'particulares' que serão modificadas / definidas posteriormente (mostradas acima). A segunda seção são as variáveis ​​/ funções que estarão disponíveis na função app.config e, portanto, estão disponíveis para alteração antes de estarem disponíveis em qualquer outro lugar (também mostrado acima). É importante observar que essas variáveis ​​precisam ser anexadas à palavra-chave 'this'. No nosso exemplo, apenas 'thingFromConfig' estará disponível para alteração no app.config. A terceira seção (mostrada abaixo) são todas as variáveis ​​/ funções que estarão disponíveis no seu controlador quando você passar o serviço 'myProvider' para esse controlador específico.

Ao criar um serviço com o Provedor, as únicas propriedades / métodos que estarão disponíveis no seu controlador são as propriedades / métodos retornados da função $ get (). O código abaixo coloca $ get on 'this' (que sabemos que eventualmente serão retornados dessa função). Agora, essa função $ get retorna todos os métodos / propriedades que queremos que estejam disponíveis no controlador. Aqui está um exemplo de código.

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

Agora, o código completo do provedor se parece com isso

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

Agora, assim como em nossa fábrica e Serviço, setArtist, getArtist e callItunes estarão disponíveis em qualquer controlador em que passarmos o myProvider. Aqui está o controlador myProvider (que é quase exatamente o mesmo que o nosso controlador de fábrica / serviço).

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

Como mencionado anteriormente, o objetivo principal de criar um serviço com o Provedor é poder alterar algumas variáveis ​​por meio da função app.config antes que o objeto final seja passado para o restante do aplicativo. Vamos ver um exemplo disso.

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

Agora você pode ver como 'thingFromConfig' é uma string vazia no nosso provedor, mas quando isso aparecer no DOM, será 'Esta frase foi configurada ...'.

Tyler McGinnis
fonte
11
A única parte que falta nessa excelente redação é as vantagens relativas de usar o serviço em uma fábrica; que é claramente explicado na resposta aceita por Lior
infinity
2
FWIW (talvez não muito), aqui é um blogueiro que tem problema com angular, e não gosta providerProvider codeofrob.com/entries/you-have-ruined-javascript.html
barlop
3
O punchline dos 'gurus do JavaScript' era astuto. : Eu acho que essa resposta esclarece muito as coisas. Soberbamente escrito.
amarmishra
4
Seu TLDR precisa de um TLDR.
JensB
3
@JensB tl; dr - Aprenda a reagir.
Tyler McGinnis
512

Todos os serviços são singletons ; eles são instanciados uma vez por aplicativo. Eles podem ser de qualquer tipo , seja um primitivo, literal de objeto, função ou até uma instância de um tipo personalizado.

O value,factory , service, constant, e providermétodos são todos os provedores. Eles ensinam ao Injetor como instanciar os Serviços.

A mais detalhada, mas também a mais abrangente, é uma receita de Provedor. Os quatro restantes tipos de receita - Valor, Fábrica, Serviço e Constante - são apenas açúcar sintático em cima de uma receita de provedor .

  • o receita do valor é o caso mais simples, em que você instancia o serviço e fornece o valor instanciado ao injetor.
  • o receita de fábrica fornece ao injetor uma função de fábrica que ele chama quando precisa instanciar o serviço. Quando chamada, a função de fábrica cria e retorna a instância de serviço. As dependências do Serviço são injetadas como argumentos das funções. Então, usar esta receita adiciona as seguintes habilidades:
    • Capacidade de usar outros serviços (ter dependências)
    • Inicialização de serviço
    • Inicialização atrasada / lenta
  • A receita de serviço é quase a mesma que a receita de fábrica, mas aqui o Injector chama um construtor com o novo operador, em vez de uma função de fábrica.
  • A receita do provedor geralmente é um exagero . Ele adiciona mais uma camada de indireção, permitindo que você configure a criação da fábrica.

    Você deve usar a receita do provedor apenas quando quiser expor uma API para a configuração de todo o aplicativo que deve ser feita antes do aplicativo ser iniciado. Isso geralmente é interessante apenas para serviços reutilizáveis ​​cujo comportamento pode precisar variar um pouco entre aplicativos.

  • A receita Constant é exatamente como a receita Value, exceto que permite definir serviços disponíveis na configuração fase de . Antes dos serviços criados usando a receita Value. Ao contrário de Valores, eles não podem ser decorados usando decorator.
Consulte a documentação do provedor .

flup
fonte
2
Então serviço e fábrica são essencialmente os mesmos? Usar um dos outros fornece nada além de sintaxe alternativa?
Matt
2
@ Matt, sim, serviço é uma maneira concisa quando você já tem sua própria função que deseja expor como serviço. De documentos: myApp.factory ('unicornLauncher', ["apiToken", função (apiToken) {retorna novo UnicornLauncher (apiToken);}]); vs: myApp.service ('unicornLauncher', ["apiToken", UnicornLauncher]);
janek
5
@joshperry Como novato, pesquisei a diferença entre serviço e fábrica por um tempo. Concordo que esta é a melhor resposta de sempre! Eu entenderia serviço como uma classe de serviço (por exemplo, classe codificador / decodificador), que pode ter algumas propriedades particulares. E a fábrica fornece um conjunto de métodos auxiliares sem estado.
stanleyxu2005
3
Os exemplos da Yaa em outras respostas acima falham em explicar muito claramente a diferença principal em relação aos serviços e fornecedores, que é injetada no momento em que essas receitas são instanciadas.
precisa
223

Compreendendo o AngularJS Factory, Service and Provider

Tudo isso é usado para compartilhar objetos singleton reutilizáveis. Ajuda a compartilhar código reutilizável em seu aplicativo / vários componentes / módulos.

No Docs Service / Factory :

  • Instanciado preguiçosamente - o Angular apenas instancia um serviço / fábrica quando um componente de aplicativo depende dele.
  • Singletons - Cada componente dependente de um serviço obtém uma referência à instância única gerada pela fábrica de serviços.

Fábrica

Uma fábrica é uma função na qual você pode manipular / adicionar lógica antes de criar um objeto, para que o objeto recém-criado seja retornado.

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

Uso

Pode ser apenas uma coleção de funções como uma classe. Portanto, ele pode ser instanciado em diferentes controladores quando você o injeta nas funções de controlador / fábrica / diretiva. É instanciado apenas uma vez por aplicativo.

Serviço

Simplesmente enquanto olha para os serviços, pense no protótipo da matriz. Um serviço é uma função que instancia um novo objeto usando a palavra-chave 'new'. Você pode adicionar propriedades e funções a um objeto de serviço usando a thispalavra - chave Ao contrário de uma fábrica, ele não retorna nada (retorna um objeto que contém métodos / propriedades).

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

Uso

Use-o quando precisar compartilhar um único objeto em todo o aplicativo. Por exemplo, detalhes do usuário autenticado, métodos / dados compartilhados, funções do utilitário etc.

Fornecedor

Um provedor é usado para criar um objeto de serviço configurável. Você pode definir a configuração de serviço na função de configuração. Retorna um valor usando a $get()função A $getfunção é executada na fase de execução em angular.

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

Uso

Quando você precisar fornecer configuração em módulo para o seu objeto de serviço antes de disponibilizá-lo, por exemplo. suponha que você queira definir o URL da API com base no seu ambiente dev, como , stageouprod

NOTA

Somente o provedor estará disponível na fase de configuração angular, enquanto o serviço e a fábrica não estiverem.

Espero que isso tenha esclarecido sua compreensão sobre a fábrica, o serviço e o fornecedor .

Pankaj Parkar
fonte
1
O que eu faria se quisesse ter um serviço com uma interface específica, mas ter duas implementações diferentes e injetar cada uma em um controlador, mas vinculado a estados diferentes usando o ui-router? por exemplo, faça chamadas remotas em um estado, mas grave no armazenamento local em outro. Os documentos do provedor dizem para usar 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, então não parece possível, certo?
Qix 9/15
191

Para mim, a revelação veio quando percebi que todos eles funcionam da mesma maneira: executando algo uma vez , armazenando o valor que recebem e depois tossindo esse mesmo valor armazenado quando referenciado por injeção de dependência .

Digamos que temos:

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

A diferença entre os três é que:

  1. aO valor armazenado de vem da execução fn.
  2. bO valor armazenado vem de newingfn .
  3. cO valor armazenado é obtido primeiro pela obtenção de uma instância newing fne, em seguida, pela execução de um $getmétodo da instância.

O que significa que há algo como um objeto de cache dentro do AngularJS, cujo valor de cada injeção é atribuído apenas uma vez, quando foram injetados pela primeira vez e onde:

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

É por isso que usamos thisem serviços e definimos um this.$getem provedores.

Lucia
fonte
2
Eu também gosto mais desta resposta. O objetivo de todos eles é fornecer acesso a um objeto sempre que necessário através do DI. Normalmente você está bem com factorys. Os únicos motivos pelos quais serviceexistem são linguagens como CoffeeScript, TypeScript, ES6 etc., para que você possa usar a sintaxe da classe. Você precisa providerapenas se seu módulo for usado em vários aplicativos com configurações diferentes usando app.config(). Se o seu serviço for um singleton puro ou for capaz de criar instâncias de algo, isso depende apenas da sua implementação.
Andreas Linnert
137

Serviço x fornecedor x fábrica:

Estou tentando simplificar. É tudo sobre o conceito básico de JavaScript.

Primeiro de tudo, vamos falar sobre serviços no AngularJS!

O que é serviço: no AngularJS, serviçonada mais é que um objeto JavaScript singleton que pode armazenar alguns métodos ou propriedades úteis. Esse objeto singleton é criado por ngApp (aplicativo Angular) e é compartilhado entre todos os controladores no aplicativo atual. Quando o Angularjs instancia um objeto de serviço, ele registra esse objeto de serviço com um nome de serviço exclusivo. Portanto, sempre que precisamos de uma instância de serviço, o Angular pesquisa no registro esse nome de serviço e retorna a referência ao objeto de serviço. Para que possamos chamar método, acessar propriedades etc. no objeto de serviço. Você pode ter dúvidas se também pode colocar propriedades, métodos no escopo objeto dos controladores! Então, por que você precisa de objeto de serviço? As respostas são: os serviços são compartilhados entre vários escopos de controladores. Se você colocar algumas propriedades / métodos no objeto de escopo de um controlador, ele estará disponível apenas no escopo atual.

Portanto, se houver três escopos de controlador, sejam controllerA, controllerB e controllerC, todos compartilharão a mesma instância de serviço.

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

Como criar um serviço?

O AngularJS fornece métodos diferentes para registrar um serviço. Aqui vamos nos concentrar em três métodos: fábrica (..), serviço (..), provedor (..);

Use este link para referência de código

Função de fábrica:

Podemos definir uma função de fábrica como abaixo.

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

O AngularJS fornece o método 'factory (' serviceName ', fnFactory)' que utiliza dois parâmetros, serviceName e uma função JavaScript. Angular cria instância de serviço chamando a função fnFactory () como abaixo.

var serviceInstace = fnFactory();

A função passada pode definir um objeto e retornar esse objeto. O AngularJS simplesmente armazena essa referência de objeto em uma variável que é passada como primeiro argumento. Tudo o que for retornado do fnFactory será vinculado ao serviceInstance. Em vez de retornar o objeto, também podemos retornar funções, valores, etc. O que retornarmos estará disponível para a instância de serviço.

Exemplo:

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

Função de serviço:

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

Por outro lado, podemos registrar um serviço. A única diferença é a maneira como o AngularJS tenta instanciar o objeto de serviço. Desta vez, o angular usa a palavra-chave 'new' e chama a função construtora como algo abaixo.

var serviceInstance = new fnServiceConstructor();

Na função construtora, podemos usar a palavra-chave 'this' para adicionar propriedades / métodos ao objeto de serviço. exemplo:

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

Função do provedor:

A função Provider () é a outra maneira de criar serviços. Vamos nos interessar em criar um serviço que apenas exiba alguma mensagem de saudação ao usuário. Mas também queremos fornecer uma funcionalidade para que o usuário possa definir sua própria mensagem de saudação. Em termos técnicos, queremos criar serviços configuráveis. Como podemos fazer isso ? Deve haver uma maneira de que o aplicativo possa transmitir suas mensagens de saudação personalizadas e o Angularjs disponibilizá-lo para a função de fábrica / construtor que cria nossa instância de serviços. Nesse caso, a função provider () faz o trabalho. usando a função provider (), podemos criar serviços configuráveis.

Podemos criar serviços configuráveis ​​usando a sintaxe do provedor, conforme indicado abaixo.

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

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

Como a sintaxe do provedor funciona internamente?

O objeto 1.Provider é criado usando a função construtora que definimos em nossa função de provedor.

var serviceProvider = new serviceProviderConstructor();

2.A função que passamos em app.config () é executada. Isso é chamado de fase de configuração, e aqui temos a chance de personalizar nosso serviço.

configureService(serviceProvider);

3. Finalmente, a instância de serviço é criada chamando o método $ get de serviceProvider.

serviceInstance = serviceProvider.$get()

Código de amostra para criar serviço usando sintaxe de fornecimento:

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

Demonstração de trabalho

Resumo:


Fábrica use uma função de fábrica que retorne uma instância de serviço. serviceInstance = fnFactory ();

O serviço usa uma função construtora e o Angular invoca essa função construtora usando a palavra-chave 'new' para criar a instância do serviço. serviceInstance = novo fnServiceConstructor ();

O provedor define uma função providerConstructor, essa função providerConstructor define uma função de fábrica $ get . Chamadas angulares $ get () para criar o objeto de serviço. A sintaxe do provedor tem uma vantagem adicional de configurar o objeto de serviço antes que ele seja instanciado. serviceInstance = $ get ();

Uma formiga
fonte
63

Fábrica

Você atribui uma função ao AngularJS, o AngularJS armazenará em cache e injetará o valor de retorno quando a fábrica for solicitada.

Exemplo:

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

Uso:

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

Serviço

Você atribui uma função ao AngularJS, o AngularJS chamará new para instancia-lo. É a instância criada pelo AngularJS que será armazenada em cache e injetada quando o serviço for solicitado. Como new foi usado para instanciar o serviço, a palavra - chave é válida e refere-se à instância.

Exemplo:

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

Uso:

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

Fornecedor

Você atribui uma função ao AngularJS e o AngularJS chamará sua $getfunção. É o valor de retorno da $getfunção que será armazenado em cache e injetado quando o serviço for solicitado.

Os provedores permitem que você configure o provedor antes que o AngularJS chame o $getmétodo para obter o injetável.

Exemplo:

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

Uso (como um injetável em um controlador)

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

Uso (a configuração do provedor anterior $geté chamada para criar o injetável)

app.config(function(providerProvider) {
    providerProvider.setName('John');
});
pixelbits
fonte
56

Notei algo interessante ao brincar com os provedores.

A visibilidade dos injetáveis ​​é diferente para os fornecedores e para os serviços e fábricas. Se você declarar uma "constante" AngularJS (por exemplo,myApp.constant('a', 'Robert'); ), poderá injetá-la em serviços, fábricas e fornecedores.

Mas se você declarar um "valor" do AngularJS (por exemplo., myApp.value('b', {name: 'Jones'});), Poderá injetá-lo em serviços e fábricas, mas NÃO na função de criação de provedor. No entanto, você pode injetá-lo na $getfunção que você define para o seu provedor. Isso é mencionado na documentação do AngularJS, mas é fácil perder. Você pode encontrá-lo na página% de fornecimento nas seções sobre métodos de valor e constante.

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>
apenas olhando
fonte
45

Esta é uma parte muito confusa para o novato e tentei esclarecê-lo com palavras fáceis

Serviço AngularJS: é usado para compartilhar funções de utilitário com a referência de serviço no controlador. O serviço é de natureza única, portanto, para um serviço, apenas uma instância é criada no navegador e a mesma referência é usada em toda a página.

No serviço, criamos nomes de função como propriedade com este objeto.

AngularJS Factory: o objetivo do Factory também é o mesmo do Service, no entanto, neste caso, criamos um novo objeto e adicionamos funções como propriedades desse objeto e, no final, retornamos esse objeto.

Provedor AngularJS: o objetivo disso é novamente o mesmo, no entanto, o Provedor fornece a saída de sua função $ get.

A definição e o uso de Serviço, Fábrica e Fornecedor são explicados em http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider

Sheo Narayan
fonte
2
Fábrica e fornecedores também são objeto singleton? Algum scanrio em que as fábricas são recomendadas sobre os serviços?
Sunil Garg
34

Para mim, a melhor e a maneira mais simples de entender a diferença é:

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

Como o AngularJS instancia componentes específicos (simplificado):

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

// factory
var angularFactory = factory(injection);

Portanto, para o serviço, o que se torna o componente AngularJS é a instância do objeto da classe que é representada pela função de declaração de serviço. Para a fábrica, é o resultado retornado da função de declaração de fábrica. A fábrica pode se comportar da mesma maneira que o serviço:

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

A maneira mais simples de pensar é a seguinte:

  • Serviço é uma instância de objeto singleton. Use serviços se desejar fornecer um objeto singleton para seu código.
  • Fábrica é uma classe. Use fábricas se desejar fornecer classes personalizadas para o seu código (não pode ser feito com serviços porque eles já estão instanciados).

O exemplo de 'classe' de fábrica é fornecido nos comentários, bem como a diferença de fornecedor.

Lukasz Frankowski
fonte
como um serviço pode ser um singleton se for instanciado toda vez que for usado? eu posso colocar minha cabeça em torno disso ...
joe
O serviço é instanciado apenas uma vez durante a resolução de dependências e, quando você solicita o serviço do injector, obtém sempre a mesma instância. Pode ser facilmente verificado aqui: jsfiddle.net/l0co/sovtu55t/1 , execute-o com o console. O console mostra que o serviço é instanciado apenas uma vez.
Lukasz Frankowski
Ah eu vejo. eu estava esperando para ser capaz de, literalmente, new MyService()ou algo :)
joe
33

Meu esclarecimento sobre este assunto:

Basicamente, todos os tipos mencionados (serviço, fábrica, provedor etc.) estão apenas criando e configurando variáveis ​​globais (que são obviamente globais para todo o aplicativo), exatamente como eram as variáveis ​​globais antiquadas.

Embora as variáveis ​​globais não sejam recomendadas, o uso real dessas variáveis ​​globais é fornecer injeção de dependência , passando a variável para o controlador relevante.

Existem muitos níveis de complicações na criação dos valores para as "variáveis ​​globais":

  1. Constante
    Define uma constante real que não deve ser modificada durante todo o aplicativo, assim como as constantes em outros idiomas (algo que falta no JavaScript).
  2. Valor
    Este é um valor ou objeto modificável e serve como alguma variável global, que pode até ser injetada ao criar outros serviços ou fábricas (veja mais adiante). No entanto, ele deve ser um " valor literal ", o que significa que é preciso escrever o valor real e não pode usar nenhuma lógica de computação ou programação (em outras palavras, 39 ou myText ou {prop: "value"} estão OK, mas 2 +2 não é).
  3. Fábrica
    Um valor mais geral, que pode ser calculado imediatamente. Ele funciona passando uma função para o AngularJS com a lógica necessária para calcular o valor e o AngularJS o executa e salva o valor de retorno na variável nomeada.
    Observe que é possível retornar um objeto (nesse caso, ele funcionará de maneira semelhante a um serviço ) ou uma função (que será salva na variável como uma função de retorno de chamada).
  4. Serviço
    Um serviço é uma versão mais simplificada da fábrica que é válida apenas quando o valor é um objeto e permite gravar qualquer lógica diretamente na função (como se fosse um construtor), além de declarar e acessar as propriedades do objeto usando a palavra - chave this .
  5. Provedor
    Diferente de um serviço que é uma versão simplificada da fábrica , um provedor é uma maneira mais complexa, porém mais flexível, de inicializar as variáveis ​​"globais", com a maior flexibilidade sendo a opção de definir valores no app.config.
    Funciona como usar uma combinação de serviço e provedor , passando para o provedor uma função que possui propriedades declaradas usando a palavra - chave this , que pode ser usada no app.config.
    Em seguida, ele precisa ter uma função $ .get separada, que é executada pelo AngularJS após definir as propriedades acima através do app.configarquivo, e essa função $ .get se comporta exatamente como a fábrica acima, em que seu valor de retorno é usado para inicializar as variáveis ​​"globais".
yoel halb
fonte
26

Meu entendimento é muito simples abaixo.

Fábrica: você simplesmente cria um objeto dentro da fábrica e o devolve.

Serviço:

Você apenas tem uma função padrão que usa essa palavra-chave para definir uma função.

Fornecedor:

Há um $getobjeto que você define e pode ser usado para obter o objeto que retorna dados.

sajan
fonte
Você não misturou a fábrica e serviço? Serviços cria onde a fábrica retorna.
Flavien Volken
Ao declarar o nome do serviço como um argumento injetável, você receberá uma instância da função. Em outras palavras, novo FunctionYouPassedToService (). Essa instância do objeto se torna o objeto de serviço que o AngularJS registra e injeta posteriormente em outros serviços / controladores, se necessário. // factory Ao declarar factoryname como um argumento injetável, você receberá o valor retornado invocando a referência de função passada para module.factory.
sajan
Ok, então ... em angular da fábrica é um singleton , onde o "serviço" é uma verdade, uma fábrica (em termos comuns design patterns)
Flavien Volken
25

Resumo dos documentos angulares :

  • Existem cinco tipos de receita que definem como criar objetos: Valor , Fábrica , Serviço , Fornecedor e Constante .
  • Fábrica e serviço são as receitas mais usadas. A única diferença entre eles é que a receita de serviço funciona melhor para objetos de um tipo personalizado, enquanto a fábrica pode produzir primitivas e funções JavaScript.
  • A receita do provedor é o tipo de receita principal e todas as outras são apenas açúcar sintático.
  • O provedor é o tipo de receita mais complexo. Você não precisa dele, a menos que esteja criando um pedaço de código reutilizável que precise de configuração global.

insira a descrição da imagem aqui


Melhores respostas de SO:

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

Yarin
fonte
20

Todas as boas respostas já. Gostaria de acrescentar mais alguns pontos sobre Serviço e Fábrica . Junto com a diferença entre serviço / fábrica. E também se pode ter perguntas como:

  1. Devo usar o serviço ou a fábrica? Qual é a diferença?
  2. Eles fazem o mesmo ou têm o mesmo comportamento?

Vamos começar com a diferença entre serviço e fábrica:

  1. Ambos são singletons : sempre que o Angular os encontra como dependência pela primeira vez, ele cria uma única instância de serviço / fábrica. Depois que a instância é criada, a mesma instância é usada para sempre.

  2. Pode ser usado para modelar um objeto com comportamento : ambos podem ter métodos, variáveis ​​de estado interno e assim por diante. Embora a maneira como você escreve esse código seja diferente.

Serviços:

Um serviço é uma função construtora, e o Angular irá instancia-lo chamando new yourServiceName(). Isso significa algumas coisas.

  1. Funções e variáveis ​​de instância serão propriedades de this .
  2. Você não precisa retornar um valor. Quando o Angular chama new yourServiceName(), ele recebe o thisobjeto com todas as propriedades que você coloca nele.

Exemplo de exemplo:

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

Quando o Angular injeta esse MyServiceserviço em um controlador que depende dele, esse controlador recebe um MyServiceque pode chamar funções, por exemplo, MyService.aServiceMethod ().

Seja cuidadoso com this :

Como o serviço construído é um objeto, os métodos dentro dele podem se referir a isso quando são chamados:

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

Você pode ficar tentado a entrar ScoreKeeper.setScoreem uma cadeia de promessas, por exemplo, se inicializou a pontuação pegando-a no servidor: $http.get('/score').then(ScoreKeeper.setScore).O problema é que ScoreKeeper.setScoreserá chamado com thisvinculado nulle você obterá erros. A melhor maneira seria $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper)). Se você optar por usar isso em seus métodos de serviço ou não, tome cuidado com a forma como os chama.

Retornando um valor deService :

Devido à maneira como os construtores JavaScript funcionam, se você retornar um valor complexo (i.e., an Object)de uma constructorfunção, o chamador obterá esse Objeto em vez da instância.

Isso significa que você pode basicamente copiar e colar o exemplo de fábrica abaixo, substituir factorypor servicee ele funcionará:

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

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

Portanto, quando Angular constrói seu serviço com o novo MyService (), ele obtém esse objeto api em vez da instância do MyService.

Esse é o comportamento para quaisquer valores complexos (objetos, funções), mas não para tipos primitivos.

Fábricas:

Uma fábrica é uma função antiga simples que retorna um valor. O valor de retorno é o que é injetado em coisas que dependem da fábrica. Um padrão de fábrica típico em Angular é retornar um objeto com funções como propriedades, assim:

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

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

  return api;
});

O valor injetado para uma dependência de fábrica é o valor de retorno da fábrica e não precisa ser um objeto. Poderia ser uma função

Respostas para as perguntas 1 e 2 acima:

Na maioria das vezes, continue usando fábricas para tudo. O comportamento deles é mais fácil de entender. Não há escolha a fazer sobre retornar ou não um valor e, além disso, não há bugs a serem introduzidos se você fizer a coisa errada.

Ainda me refiro a eles como "serviços" quando estou falando de injetá-los como dependências.

O comportamento do serviço / fábrica é muito semelhante e algumas pessoas dizem que qualquer um deles está bem. Isso é verdade, mas acho mais fácil seguir os conselhos do guia de estilo de John Papa e permanecer nas fábricas. **

Ved
fonte
16

Um esclarecimento adicional é que as fábricas podem criar funções / primitivas, enquanto os serviços não. Verifique este jsFiddle com base no Epokk: http://jsfiddle.net/skeller88/PxdSP/1351/ .

A fábrica retorna uma função que pode ser chamada:

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

A fábrica também pode retornar um objeto com um método que pode ser chamado:

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

O serviço retorna um objeto com um método que pode ser chamado:

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

Para obter mais detalhes, consulte um post que escrevi sobre a diferença: http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/

skeller88
fonte
16

Já existem boas respostas, mas só quero compartilhar essa.

Primeiro de tudo: o provedor é o caminho / receita para criar umservice (objeto singleton) que deve ser injetado pelo $ injector (como o AngulaJS segue o padrão de IoC).

E Valor, Fábrica, Serviço e Constante (4 vias) - o açúcar sintático sobre a maneira / recebimento do Fornecedor .

Não é Service vs Factoryparte foi coberta: https://www.youtube.com/watch?v=BLzNCkPn3ao

O serviço tem tudo a ver com newpalavras-chave, que, como sabemos, fazem quatro coisas:

  1. cria um novo objeto
  2. vincula-o ao seu prototypeobjeto
  3. se conecta contextathis
  4. e retorna this

E Factory é tudo sobre Factory Pattern - contém funções que retornam objetos como esse serviço.

  1. capacidade de usar outros serviços (ter dependências)
  2. inicialização de serviço
  3. inicialização atrasada / lenta

E este vídeo simples / curto: abrange também o provedor : https://www.youtube.com/watch?v=HvTZbQ_hUZY (lá você pode ver como eles vão da fábrica para o fornecedor)

A receita do provedor é usada principalmente na configuração do aplicativo, antes que o aplicativo seja totalmente iniciado / inicializado.

ses
fonte
14

Depois de ler todos esses posts, criou mais confusão para mim .. Mas ainda assim tudo é informação valiosa .. finalmente encontrei a tabela a seguir que dará informações com comparação simples

  • O injetor usa receitas para criar dois tipos de objetos: serviços e objetos de finalidade especial
  • Existem cinco tipos de receita que definem como criar objetos: Valor, Fábrica, Serviço, Fornecedor e Constante.
  • Fábrica e serviço são as receitas mais usadas. A única diferença entre eles é que a receita de serviço funciona melhor para objetos de um tipo personalizado, enquanto o Factory pode produzir primitivas e funções JavaScript.
  • A receita do provedor é o tipo de receita principal e todas as outras são apenas açúcar sintático.
  • O provedor é o tipo de receita mais complexo. Você não precisa dele, a menos que esteja criando um pedaço de código reutilizável que precise de configuração global.
  • Todos os objetos de finalidade especial, exceto o Controlador, são definidos nas receitas de fábrica.

insira a descrição da imagem aqui

E, para iniciantes, entenda: - Isso pode não corrigir o caso de uso, mas em alto nível é o que usamos para esses três.

  1. Se você deseja usar no módulo angular, a função de configuração deve ser criada como provedor

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

  1. Ajax chamada ou necessidades terceiros integrações de partido para ser de serviço .
  2. Para manipulações de dados, crie-o como fábrica

Para cenários básicos, a fábrica e o serviço se comportam da mesma maneira.

BEJGAM SHIVA PRASAD
fonte
13

Aqui está um código que eu criei como um modelo de código para a fábrica de objetos no AngularjS. Eu usei um Car / CarFactory como exemplo para ilustrar. Cria um código de implementação simples no controlador.

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

Aqui está um exemplo mais simples. Estou usando algumas bibliotecas de terceiros que esperam um objeto "Posição" expondo latitude e longitude, mas através de diferentes propriedades do objeto. Como não queria invadir o código do fornecedor, ajustei os objetos "Posição" que estava passando.

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

Usando como referência esta página e a documentação (que parece ter melhorado muito desde a última vez que procurei), montei a seguinte demonstração do mundo real (-ish) que usa quatro dos cinco tipos de provedor; Valor, constante, fábrica e fornecedor completo.

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>

aplicativo

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

Demonstração de trabalho .

net.uk.sweet
fonte
12

Esta resposta aborda o tópico / pergunta

como Factory, Service e Constant - são apenas açúcar sintático em cima de uma receita de provedor?

OU

como fábrica, serviço e fornecedores são semelhantes internamente

basicamente o que acontece é

Quando você factory()cria um conjunto de configurações functionfornecido no segundo argumento para o provedor $gete o retorna ( provider(name, {$get:factoryFn })), tudo o que você obtém é que providernão há nenhuma propriedade / método além$get disso provider(significa que você não pode configurar isso)

Código fonte da fábrica

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

Ao fazer um service()retorno, você fornece a uma fábrica () um functionque injeta constructor(retorna a instância do construtor que você forneceu em seu serviço) e o devolve

Código fonte de serviço

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

Então, basicamente, em ambos os casos, você eventualmente obtém um conjunto de provedores $ definido para sua função que você forneceu, mas você pode fornecer algo a mais que $ get, como você pode fornecer originalmente no provedor () para o bloco de configuração

AB
fonte
11

Conheço muitas respostas excelentes, mas tenho que compartilhar minha experiência de usar
1. servicena maioria dos casos de padrão
2. factoryusado para criar o serviço nessa instância específica

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

e usando:

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

Um pouco tarde para a festa. Mas achei que isso seria mais útil para quem gostaria de aprender (ou ter clareza) sobre o desenvolvimento de serviços personalizados JS angulares usando metodologias de fábrica, serviço e provedor.

Encontrei este vídeo que explica claramente sobre as metodologias de fábrica, serviço e provedor para o desenvolvimento de serviços personalizados do AngularJS:

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

Código fonte: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service

O código postado aqui é copiado diretamente da fonte acima, para beneficiar os leitores.

O código para o serviço personalizado baseado em "fábrica" ​​é o seguinte (que acompanha as versões de sincronização e assíncrona, além de chamar o serviço 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;
  }
]);

O código para a metodologia "service" para serviços personalizados (isso é bastante semelhante ao 'factory', mas diferente do ponto de vista da sintaxe):

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

}]);

O código da metodologia "provider" para Serviços Personalizados (isso é necessário, se você desejar desenvolver um serviço que possa ser configurado):

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

Finalmente, a interface do usuário que funciona com qualquer um dos serviços acima:

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

user203687
fonte
10

Apenas para esclarecer as coisas, na fonte AngularJS, você pode ver um serviço que apenas chama a função de fábrica que, por sua vez, chama a função de provedor:

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

Vamos discutir as três maneiras de lidar com a lógica de negócios no AngularJS de uma maneira simples: ( Inspirado no curso Coursera AngularJS do Yaakov )

SERVIÇO :

Sintaxe:

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>

Recursos do serviço:

  1. Instanciado preguiçosamente : se não for injetado, nunca será instanciado. Portanto, para usá-lo, será necessário injetá-lo em um módulo.
  2. Singleton : se injetado em vários módulos, todos terão acesso a apenas uma instância específica. É por isso que é muito conveniente compartilhar dados entre diferentes controladores.

FÁBRICA

Primeiro, vamos dar uma olhada na sintaxe:

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

Agora, usando os dois acima no controlador:

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

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

Características da fábrica:

  1. Segue o padrão de design da fábrica. A fábrica é um local central que produz novos objetos ou funções.
  2. Não apenas produz serviços únicos, mas personalizáveis.
  3. O .service()método é uma fábrica que sempre produz o mesmo tipo de serviço, que é um singleton e sem nenhuma maneira fácil de configurar seu comportamento. Esse .service()método geralmente é usado como um atalho para algo que não requer nenhuma configuração.

FORNECEDOR

Vamos novamente dar uma olhada na sintaxe primeiro:

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

}

Recursos do Provedor:

  1. O provedor é o método mais flexível de criar serviços no Angular.
  2. Não apenas podemos criar uma fábrica configurável dinamicamente, mas no momento de usá-la, com o método do provedor, poderíamos configurar a fábrica de forma personalizada apenas uma vez no bootstrap de todo o aplicativo.
  3. A fábrica pode ser usada em todo o aplicativo com configurações personalizadas. Em outras palavras, podemos configurar esta fábrica antes que o aplicativo seja iniciado. De fato, na documentação angular, é mencionado que o método do provedor é o que realmente é executado nos bastidores quando configuramos nossos serviços com um .serviceou outro .factorymétodo.
  4. A $geté uma função diretamente anexada à instância do provedor. Essa função é uma função de fábrica . Em outras palavras, é exatamente como o que usamos para fornecer ao .factorymétodo. Nessa função, criamos nosso próprio serviço. Essa $getpropriedade, que é uma função, é o que torna o provedor um provedor . O AngularJS espera que o provedor tenha uma propriedade $ get cujo valor é uma função que o Angular tratará como uma função de fábrica. Mas o que torna toda essa configuração de provedor muito especial é o fato de podermos fornecer algum configobjeto dentro do provedor de serviços, e isso geralmente vem com padrões que podemos substituir posteriormente na etapa, onde podemos configurar o aplicativo inteiro.
Pritam Banerjee
fonte
7

Fábrica: a fábrica em que você realmente cria um objeto dentro da fábrica e o devolve.
service: o serviço, você apenas possui uma função padrão que usa a palavra-chave this para definir a função.
provider: O provedor tem um $ get you define e pode ser usado para obter o objeto que retorna os dados.

Mohanrajan
fonte
7

Essencialmente, Provedor, Fábrica e Serviço são todos Serviços. Um Factory é um caso especial de um Serviço quando tudo o que você precisa é de uma função $ get (), permitindo escrever com menos código.

As principais diferenças entre serviços, fábricas e fornecedores são suas complexidades. Os serviços são a forma mais simples, as fábricas são um pouco mais robustas e os provedores são configuráveis ​​em tempo de execução.

Aqui está um resumo de quando usar cada um:

Fábrica : o valor que você está fornecendo precisa ser calculado com base em outros dados.

Serviço : você está retornando um objeto com métodos.

Fornecedor : você deseja configurar, durante a fase de configuração, o objeto que será criado antes de ser criado. Use o provedor principalmente na configuração do aplicativo, antes que o aplicativo seja totalmente inicializado.

eGhoul
fonte
erm. Valor, Fábrica, Serviço e Constante - são apenas açúcar sintático em cima de uma receita de fornecedor. Angularjs docs - provider
Sudarshan_SMD
Sim, eu concordo, agora com angular 4 não temos essa dor de cabeça mais
eGhoul
4

1. Serviços são objetos únicos que são criados quando necessário e nunca são limpos até o final do ciclo de vida do aplicativo (quando o navegador é fechado). Os controladores são destruídos e limpos quando não são mais necessários.

2. A maneira mais fácil de criar um serviço é usando o método factory (). O método factory () nos permite definir um serviço retornando um objeto que contém funções de serviço e dados de serviço. A função de definição de serviço é onde colocamos nossos serviços injetáveis, como $ http e $ q. Ex:

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

Usando a factory () em nosso aplicativo

É fácil usar a fábrica em nosso aplicativo, pois podemos simplesmente injetá-lo onde for necessário em tempo de execução.

angular.module('myApp')
.controller('MainController', function($scope, User) {
  $scope.saveUser = User.save;
});
  1. O método service (), por outro lado, permite criar um serviço definindo uma função construtora. Podemos usar um objeto prototípico para definir nosso serviço, em vez de um objeto javascript bruto. Semelhante ao método factory (), também definiremos os injetáveis ​​na definição da função.
  2. A maneira de nível mais baixo para criar um serviço é usando o método allow (). Essa é a única maneira de criar um serviço que podemos configurar usando a função .config (). Diferentemente dos métodos anteriores, definiremos os injetáveis ​​em uma definição de função definida como. $ Get ().
Shankar Gangadhar
fonte
-3

Açúcar sintático é a diferença . Somente o provedor é necessário. Ou, em outras palavras, apenas o provedor é o angular real, todos os outros são derivados (para reduzir o código). Também existe uma versão simples, chamada Value (), que retorna apenas o valor, sem cálculo ou função. Even Value é derivado do provedor!

Então, por que essas complicações, por que não podemos simplesmente usar o provedor e esquecer todo o resto? Ele deve nos ajudar a escrever código facilmente e nos comunicar melhor. E a resposta direta seria, quanto mais complexo for, melhor será a venda de uma estrutura.


  • Um provedor que pode retornar value = Value
  • Um provedor que pode apenas instanciar e retornar = Factory (+ Value)
  • Um provedor que pode instanciar + fazer algo = Serviço (+ Fábrica, + Valor)
  • Um provedor = deve conter uma propriedade chamada $ get (+ Factory, + Service, + Value)

A injeção angular nos dá a primeira dica para chegar a essa conclusão.

"$ injector é usado para recuperar instâncias de objetos, conforme definido pelo provedor " não pelo serviço, não pela fábrica, mas pelo provedor.

E uma resposta melhor seria a seguinte: "Um serviço Angular é criado por uma fábrica de serviços. Essas fábricas de serviços são funções que, por sua vez, são criadas por um provedor de serviços. Os provedores de serviços são funções construtoras. Quando instanciadas, elas devem conter uma propriedade. chamado $ get, que mantém a função da fábrica de serviços ".

Então, o provedor principal e o injetor e tudo se encaixará :). E fica interessante no Typescript quando $ get pode ser implementado em um provedor, herdando do IServiceProvider.

Nuvens azuis
fonte