use $ http dentro do provedor personalizado na configuração do aplicativo, angular.js

90

A questão principal - é possível? Tentei sem sorte ..

app.js principal

...
var app = angular.module('myApp', ['services']);
app.config(['customProvider', function (customProvider) {

}]);
...

próprio provedor

var services = angular.module('services', []);
services.provider('custom', function ($http) {
});

E eu tenho esse erro:

Uncaught Error: Unknown provider: $http from services 

Alguma ideia?

Obrigado!

Kosmetika
fonte
cara, é verdade, mas estou falando sobre app.configparte
Kosmetika
Eu também sei sobre essa limitação, mas pensei que dentro do provedor é possível de alguma forma ..
Kosmetika

Respostas:

158

O resultado final é:

  • Você NÃO PODE injetar um serviço na seção de configuração do provedor .
  • Você PODE injetar um serviço na seção que inicializa o serviço do provedor .

Detalhes:

A estrutura angular tem um processo de inicialização de 2 fases:

FASE 1: Config

Durante a configfase, todos os provedores são inicializados e todas as configseções são executadas. As configseções podem conter código que configura os objetos do provedor e, portanto, podem ser injetados com objetos do provedor. No entanto, uma vez que os provedores são as fábricas para os objetos de serviço e, neste estágio, os provedores não estão totalmente inicializados / configurados -> você não pode pedir ao provedor para criar um serviço para você neste estágio -> no estágio de configuração você não pode usar / injetar serviços . Quando esta fase é concluída, todos os provedores estão prontos (nenhuma configuração de provedor pode ser feita depois que a fase de configuração for concluída).

FASE 2: Executar

Durante a runfase todas as runseções são executadas. Nesta fase, os provedores estão prontos e podem criar serviços -> durante a runfase você pode usar / injetar serviços .

Exemplos:

1. Injetar o $httpserviço na função de inicialização do provedor NÃO funcionará

//ERRONEOUS
angular.module('myModule').provider('myProvider', function($http) {
    // SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
    ...

    this.$get = function() {
        // code to initialize/configure the SERVICE goes here (executed during `run` stage)

        return myService;
    };
});

Como estamos tentando injetar o $httpserviço em uma função que é executada durante a configfase, obteremos um erro:

Uncaught Error: Unknown provider: $http from services 

O que esse erro está realmente dizendo é que o $httpProviderque é usado para criar o $httpserviço ainda não está pronto (pois ainda estamos na configfase).

2. A injeção do $httpserviço na função de inicialização do serviço FUNCIONARÁ :

//OK
angular.module('myModule').provider('myProvider', function() {
    // SECTION 1: code to initialize/configure the PROVIDER goes here (executed during `config` phase)
    ...

    this.$get = function($http) {
        // code to initialize/configure the SERVICE goes here (executed during `run` stage)

        return myService;
    };
});

Como agora estamos injetando o serviço na função de inicialização do serviço, que é executado durante a runfase, esse código funcionará.

Dana Shalev
fonte
63
Boa resposta, mas enquanto explica como não é possível injetar serviços durante a configuração, não explica como fazer um HTTP POST / GET durante a configuração. Isso é importante para aplicativos que são configurados usando valores fornecidos por uma API.
Sean O'Dell,
3
@bebraw & Kosmetika - A única coisa que eu acho que você precisaria solicitar durante a fase de configuração é algum tipo de objeto de configurações. Talvez ele contenha o endpoint da API, as informações do usuário, a localidade do usuário e as configurações de idioma, etc. Se for esse o caso, eu recomendo incluir essas informações na fonte javascript de alguma forma. Você pode usar a renderização do lado do servidor em index.html para colocar algumas configurações de modo que estejam disponíveis antes que seu aplicativo seja inicializado. Todo o resto, eu tentaria descobrir como fazer isso pós-inicialização
Sean Clark Hess
2
@Sean: Como fazer um HTTP POST / GET é uma questão diferente do OP (é possível usar $ http dentro da fase de configuração?), E provavelmente merece um post separado; devido à natureza síncrona da fase de configuração do Angular, uma boa maneira de fornecer dados do lado do servidor ao seu código de configuração é renderizá-lo como um objeto javascript em sua página HTML durante a renderização do lado do servidor (por exemplo <script>var config = <% = mySettings.toJson() %>;</script>). Isso pode ser feito usando um mecanismo de modelagem como Smarty para PHP, Jinja2 para Python, Nunchucks para NodeJS, etc.
Trevor
4
@threed: inserir dados de configuração diretamente no HTML ou js no servidor funciona apenas se o código do cliente vier do mesmo servidor. Com o CORS, agora é possível (e muito desejável) ter o código do cliente sendo servido por um servidor diferente e os dados sendo fornecidos por servidores separados. Nesses casos, precisamos recuperar dados de configuração usando HTTP.
Bernard,
4
Embora seja uma resposta, não é a resposta à pergunta que foi feita.
Eric
64

Isso pode lhe dar uma pequena vantagem:

var initInjector = angular.injector(['ng']);
var $http = initInjector.get('$http');

Mas tenha cuidado, os retornos de chamada de sucesso / erro podem mantê-lo em uma condição de corrida entre o início do aplicativo e a resposta do servidor.

Cody
fonte
6
A "resposta aceita" falhou para meu provedor ... Passei 2 dias frustrado tentando fazer aquilo funcionar sem esperança. Sua abordagem funcionou imediatamente.
Dave Alperovich
Você pode esclarecer se a instância criada aqui é o singleton de serviço "real" ou apenas uma instância do serviço que é descartada quando o Angular faz sua mágica injetora real.
Eric
Eric, não posso confirmar isso neste momento. No entanto, o que geralmente faço (se aplicável) é angular.injector(['mymodule'])- mas não tenho certeza se você pode usar essa abordagem para o $httpserviço. Eu quero dizer que sim. Não tenho certeza se isso ajuda ou não: - /
Cody
2
Esta deve ser a resposta aceita. Lutei um bom tempo tentando fazer isso funcionar, e essa abordagem resolveu meu problema imediatamente. Acho que esse pode ser um problema muito comum. Obrigado @Cody
iamdash
5
Confirmo que a solução aceita não está funcionando para usar $ http no provedor. Mas a resposta de @Cody faz o truque
Dino
1

Esta é uma velha questão, parece que temos um caso de ovo de galinha acontecendo se quisermos contar com a capacidade central da biblioteca.

Em vez de resolver o problema de maneira fundamental, o que fiz foi contornar. Crie uma diretiva que envolva todo o corpo. Ex.

<body ng-app="app">
  <div mc-body>
    Hello World
  </div>
</body>

Agora mc-bodyprecisa ser inicializado antes da renderização (uma vez), ex.

link: function(scope, element, attrs) {
  Auth.login().then() ...
}

Auth é um serviço ou provedor, por exemplo,

.provider('Auth', function() {
  ... keep your auth configurations
  return {
    $get: function($http) {
      return {
        login: function() {
          ... do something about the http
        }
      }
    }
  }
})

Parece-me que tenho controle sobre a ordem do bootstrap, é depois que o bootstrap regular resolve todas as configurações do provedor e depois tento inicializar a mc-bodydiretiva.

E essa diretiva me parece estar à frente do roteamento, porque o roteamento também é injetado por meio de uma diretiva ex. <ui-route />. Mas posso estar errado nisso. Precisa de mais investigação.

Windmaomao
fonte
Você pode explicar sua solução?
Marcos
-2

Em resposta à sua pergunta, "Alguma ideia?", Eu teria respondido com "sim". Mas espere, tem mais!

Eu sugiro apenas usar JQuery na configuração. Por exemplo:

var app = angular.module('myApp', ['services']);
app.config(['$anyProvider', function ($anyProvider) {
    $.ajax({
        url: 'www.something.com/api/lolol',
        success: function (result) {
            $anyProvider.doSomething(result);
        }
    });
}]);
Suamere
fonte
$ customProvider no retorno de chamada de sucesso inclui $ como se fosse um provedor interno.
Jeff Fischer
1
Você está certo que eu tinha uma mistura de $ e não- $. Eu atualizei tudo para $.
Suamere