use $ http dentro del proveedor personalizado en la configuración de la aplicación, angular.js

90

La pregunta principal: ¿es posible? Lo intenté sin suerte ..

app.js principal

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

}]);
...

propio proveedor

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

Y tengo tal error:

Uncaught Error: Unknown provider: $http from services 

¿Algunas ideas?

¡Gracias!

Kosmetika
fuente
hombre, sí, es verdad, pero estoy hablando de app.configparte
Kosmetika
También conozco esta limitación, pero pensé que el proveedor interno es posible de alguna manera ..
Kosmetika

Respuestas:

158

La conclusión es:

  • Usted NO puede inyectar un servicio a la sección de configuración del proveedor .
  • Usted PUEDE inyectar un servicio dentro de la sección que inicializa el servicio del proveedor .

Detalles:

El marco angular tiene un proceso de inicialización de 2 fases:

FASE 1: Configuración

Durante la configfase, todos los proveedores se inicializan y todas las configsecciones se ejecutan. Las configsecciones pueden contener código que configura los objetos del proveedor y, por lo tanto, se pueden inyectar con los objetos del proveedor. Sin embargo, dado que los proveedores son las fábricas de los objetos de servicio y en esta etapa los proveedores no están completamente inicializados / configurados -> no puede pedirle al proveedor que cree un servicio para usted en esta etapa -> en la etapa de configuración no puede usar / servicios de inyección . Cuando se completa esta fase, todos los proveedores están listos (no se pueden realizar más configuraciones de proveedores una vez completada la fase de configuración).

FASE 2: Ejecutar

Durante la runfase runse ejecutan todas las secciones. En esta etapa, los proveedores están listos y pueden crear servicios -> durante la runfase puede usar / inyectar servicios .

Ejemplos:

1. La inyección del $httpservicio a la función de inicialización del proveedor NO 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 intentando inyectar el $httpservicio en una función que se ejecuta durante la configfase, obtendremos un error:

Uncaught Error: Unknown provider: $http from services 

Lo que realmente dice este error es que el $httpProviderque se usa para crear el $httpservicio aún no está listo (ya que todavía estamos en la configfase).

2. Inyectar el $httpservicio a la función de inicialización del servicio 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;
    };
});

Dado que ahora estamos inyectando el servicio en la función de inicialización del servicio, que se ejecuta durante la runfase, este código funcionará.

Dana Shalev
fuente
63
Buena respuesta, pero si bien explica cómo no es posible inyectar servicios durante la configuración, no explica cómo hacer un HTTP POST / GET durante la configuración. Esto es importante para las aplicaciones que se configuran utilizando valores proporcionados por una API.
Sean O'Dell
3
@bebraw & Kosmetika - Lo único que puedo pensar que necesitarías solicitar durante la fase de configuración es algún tipo de objeto de configuración. Tal vez contenga el punto final de la API, la información del usuario, la configuración regional y de idioma del usuario, etc. Si ese es el caso, recomendaría incluir esa información en la fuente de JavaScript de alguna manera. Puede usar la representación del lado del servidor en index.html para poner algunas configuraciones de modo que estén disponibles antes de que se inicialice su aplicación. Todo lo demás, trataría de averiguar cómo hacerlo después de la inicialización
Sean Clark Hess
2
@Sean: Cómo hacer un HTTP POST / GET es una pregunta diferente a la de los OP (¿Es posible usar $ http dentro de la fase de configuración?), Y probablemente merezca una publicación separada; Debido a la naturaleza síncrona de la fase de configuración de Angular, una buena manera de proporcionar datos del lado del servidor a su código de configuración es renderizarlo como un objeto javascript en su página HTML durante la representación del lado del servidor (por ejemplo <script>var config = <% = mySettings.toJson() %>;</script>). Esto se puede hacer usando un motor de plantillas como Smarty para PHP, Jinja2 para Python, Nunchucks para NodeJS, etc.
Trevor
4
@threed: Insertar datos de configuración directamente en HTML o js en el servidor solo funciona si su código de cliente proviene del mismo servidor. Con CORS, ahora es posible (y muy deseable) que el código del cliente se entregue desde un servidor diferente y que los datos se entreguen desde servidores separados. En esos casos, necesitamos recuperar datos de configuración usando HTTP.
Bernard
4
Si bien esta es una respuesta, no es la respuesta a la pregunta que se hizo.
Eric
64

Esto podría darle un pequeño apalancamiento:

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

Pero tenga cuidado, las devoluciones de llamada de éxito / error pueden mantenerlo en una condición de carrera entre el inicio de la aplicación y la respuesta del servidor.

Cody
fuente
6
La "respuesta aceptada" falló para mi proveedor ... Pasé 2 días de frustración tratando de hacer que eso funcionara sin esperanza. Su enfoque funcionó de inmediato.
Dave Alperovich
¿Puede aclarar si la instancia creada aquí es el singleton del servicio "real" o simplemente una instancia del servicio que se descarta cuando Angular hace su magia de inyector real?
Eric
Eric, no puedo confirmar eso en este momento. Sin embargo, lo que suelo hacer (si corresponde) es angular.injector(['mymodule']), pero no estoy seguro de que pueda utilizar este enfoque para el $httpservicio. Aunque quiero decir que sí. No estoy seguro de si esto ayuda o no: - /
Cody
2
Esta debería ser la respuesta aceptada. Luché durante un buen rato mientras intentaba que esto funcionara, y este enfoque resolvió mi problema de inmediato. Creo que este puede ser un problema muy común. Gracias @Cody
iamdash
5
Confirmo que la solución aceptada no funciona para usar $ http en el proveedor. Pero la respuesta de @Cody hace el truco
Dino
1

Esta es una vieja pregunta, parece que tenemos algo de huevo de gallina si queremos confiar en la capacidad central de la biblioteca.

En lugar de resolver el problema de manera fundamental, lo que hice fue pasar por alto. Crea una directiva que envuelva todo el cuerpo. Ex.

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

Ahora mc-bodydebe inicializarse antes de renderizar (una vez), ej.

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

Auth es un servicio o proveedor, ej.

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

Me parece que tengo el control sobre el orden del bootstrap, es después de que el bootstrap normal resuelve toda la configuración del proveedor y luego intento inicializar la mc-bodydirectiva.

Y esta directiva me parece que puede estar por delante del enrutamiento, porque el enrutamiento también se inyecta a través de una directiva ex. <ui-route />. Pero puedo equivocarme en esto. Necesita más investigación.

Windmaomao
fuente
¿Puede dar más detalles sobre su solución?
Mark
-2

En respuesta a su pregunta, "¿Alguna idea?", Habría respondido con "sí". ¡Pero espera hay mas!

Sugiero usar JQuery en la configuración. Por ejemplo:

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
fuente
$ customProvider en la devolución de llamada de éxito incluye $ como si fuera un proveedor interno.
Jeff Fischer
1
Tienes razón en que tenía una mezcla de $ y no $. Lo actualicé para que todo sea $.
Suamere