utilisez $ http dans le fournisseur personnalisé dans la configuration de l'application, angular.js

90

La question principale - est-ce possible? J'ai essayé sans succès.

app.js principal

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

}]);
...

fournisseur lui-même

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

Et j'ai une telle erreur:

Uncaught Error: Unknown provider: $http from services 

Des idées?

Merci!

Kosmetika
la source
mec, ouais c'est vrai, mais je parle d'une app.configpartie
Kosmetika
Je connais aussi cette limitation, mais je pensais que le fournisseur interne était possible d'une manière ou d'une autre ..
Kosmetika

Réponses:

158

L'essentiel est:

  • Vous NE POUVEZ PAS injecter un service dans la section de configuration du fournisseur .
  • Vous POUVEZ injecter un service dans la section qui initialise le service du fournisseur .

Détails:

Le framework angulaire a un processus d'initialisation en 2 phases:

PHASE 1: Config

Pendant la configphase, tous les fournisseurs sont initialisés et toutes les configsections sont exécutées. Les configsections peuvent contenir du code qui configure les objets fournisseur et par conséquent, elles peuvent être injectées avec des objets fournisseur. Cependant, comme les fournisseurs sont les usines des objets de service et à ce stade, les fournisseurs ne sont pas entièrement initialisés / configurés -> vous ne pouvez pas demander au fournisseur de créer un service pour vous à ce stade -> à l'étape de configuration, vous ne pouvez pas utiliser / injecter des services . Lorsque cette phase est terminée, tous les fournisseurs sont prêts (aucune autre configuration de fournisseur ne peut être effectuée une fois la phase de configuration terminée).

PHASE 2: Courir

Pendant la runphase, toutes les runsections sont exécutées. À ce stade, les fournisseurs sont prêts et peuvent créer des services -> pendant la runphase, vous pouvez utiliser / injecter des services .

Exemples:

1. Injecter le $httpservice de la fonction d'initialisation du fournisseur WILL NOT travail

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

Puisque nous essayons d'injecter le $httpservice dans une fonction qui est exécutée pendant la configphase, nous obtiendrons une erreur:

Uncaught Error: Unknown provider: $http from services 

Ce que cette erreur dit en fait, c'est que le $httpProviderqui est utilisé pour créer le $httpservice n'est pas encore prêt (puisque nous sommes encore dans la configphase).

2. Injecter le $httpservice de la fonction d' initialisation du service WILL travail:

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

Puisque nous injectons maintenant le service dans la fonction d'initialisation du service, qui est exécutée pendant la runphase, ce code fonctionnera.

Dana Shalev
la source
63
Bonne réponse, mais bien qu'elle explique comment il n'est pas possible d'injecter des services lors de la configuration, elle n'explique pas comment faire un HTTP POST / GET lors de la configuration. Ceci est important pour les applications qui sont configurées à l'aide de valeurs fournies par une API.
Sean O'Dell
3
@bebraw & Kosmetika - La seule chose que je pense que vous auriez besoin de demander pendant la phase de configuration est une sorte d'objet de paramètres. Peut-être qu'il contient le point de terminaison de l'API, les informations utilisateur, les paramètres régionaux et linguistiques de l'utilisateur, etc. Si c'est le cas, je recommanderais d'inclure ces informations dans la source javascript. Vous pouvez utiliser le rendu côté serveur sur index.html pour insérer quelques paramètres afin qu'ils soient disponibles avant l'initialisation de votre application. Tout le reste, j'essaierais de comprendre comment le faire après l'initiation
Sean Clark Hess
2
@Sean: Comment créer un HTTP POST / GET est une question différente de celle des OP (est-il possible d'utiliser $ http dans la phase de configuration?), Et mérite probablement un message séparé; en raison de la nature synchrone de la phase de configuration d'Angular, un bon moyen de fournir des données côté serveur à votre code de configuration est de le rendre comme un objet javascript dans votre page HTML pendant le rendu côté serveur (par exemple <script>var config = <% = mySettings.toJson() %>;</script>). Cela peut être fait en utilisant un moteur de création de modèles tel que Smarty pour PHP, Jinja2 pour Python, Nunchucks pour NodeJS, etc.
Trevor
4
@threed: L'insertion des données de configuration directement dans le HTML ou js sur le serveur ne fonctionne que si votre code client provient du même serveur. Avec CORS, il est désormais possible (et très souhaitable) que le code client soit servi à partir d'un serveur différent et que les données soient servies à partir de serveurs séparés. Dans ces cas, nous devons récupérer les données de configuration à l'aide de HTTP.
Bernard
4
Bien que ce soit une réponse, ce n'est pas la réponse à la question qui a été posée.
Eric le
64

Cela pourrait vous donner un petit effet de levier:

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

Mais attention, les rappels succès / erreur peuvent vous maintenir dans une situation de concurrence critique entre le démarrage de l'application et la réponse du serveur.

Cody
la source
6
La «réponse acceptée» a échoué pour mon fournisseur ... J'ai passé 2 jours de frustration à essayer de faire fonctionner cela sans espoir. Votre approche a fonctionné immédiatement.
Dave Alperovich
Pouvez-vous clarifier si l'instance créée ici est le singleton de service "réel" ou simplement une instance du service qui est rejetée lorsque Angular fait sa vraie magie d'injecteur.
Eric
Eric, je ne peux pas le confirmer pour le moment. Cependant, ce que je fais habituellement (le cas échéant) est angular.injector(['mymodule'])- mais je ne suis pas sûr que vous puissiez utiliser cette approche pour le $httpservice. Je veux dire que je l'ai fait. Je ne sais pas si cela aide ou non: - /
Cody
2
Cela devrait être la réponse acceptée. J'ai lutté pendant un bon moment en essayant de faire fonctionner cela, et cette approche a résolu mon problème immédiatement. Je pense que c'est peut-être un problème très courant. Merci @Cody
iamdash
5
Je confirme que la solution acceptée ne fonctionne pas pour l'utilisation de $ http dans le fournisseur. Mais la réponse de @Cody fait l'affaire
Dino
1

C'est une vieille question, il semble que nous ayons quelque chose à faire avec des œufs de poule si nous voulons nous fier aux capacités de base de la bibliothèque.

Au lieu de résoudre le problème de manière fondamentale, ce que j'ai fait, c'est le contourner. Créez une directive qui englobe tout le corps. Ex.

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

Il mc-bodydoit maintenant être initialisé avant le rendu (une fois), ex.

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

Auth est un service ou un fournisseur, ex.

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

Il me semble que j'ai le contrôle sur l'ordre du bootstrap, c'est après que le bootstrap normal ait résolu toute la configuration du fournisseur et ensuite essayé d'initialiser la mc-bodydirective.

Et cette directive me semble pouvoir être en avance sur le routage, car le routage est également injecté via une directive ex. <ui-route />. Mais je peux me tromper là-dessus. Nécessite une enquête plus approfondie.

Windmaomao
la source
Pouvez-vous expliquer votre solution?
Mark
-2

En réponse à votre question, "Des idées?", J'aurais répondu par "oui". Mais attendez, il y a plus!

Je suggère simplement d'utiliser JQuery dans la configuration. Par exemple:

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
la source
$ customProvider dans le rappel de réussite inclut le $ comme s'il s'agissait d'un fournisseur interne.
Jeff Fischer
1
Vous avez raison de dire que j'avais un mélange de $ et non de $. Je l'ai mis à jour pour que tout soit $.
Suamere