AngularJS : Initialiser le service de transmission de données asynchrones
J'ai un AngularJS service que je veux initialiser avec certains de données asynchrone. Quelque chose comme ceci:
myModule.service('MyService', function($http) {
var myData = null;
$http.get('data.json').success(function (data) {
myData = data;
});
return {
setData: function (data) {
myData = data;
},
doStuff: function () {
return myData.getSomeData();
}
};
});
Évidemment, cela ne marchera pas, parce que si quelque chose essaie de l'appeler doStuff()
avant myData
obtient en retour je reçois une exception de pointeur null. Aussi loin que je puisse dire à la lecture de certaines des autres questions posées ici et ici j'ai un peu d'options, mais aucune d'entre elles semblent très propre (peut-être que je suis absent quelque chose):
Le programme d'installation du Service avec "run"
Lors de la configuration de mon application ce faire:
myApp.run(function ($http, MyService) {
$http.get('data.json').success(function (data) {
MyService.setData(data);
});
});
Puis mon service devrait ressembler à ceci:
myModule.service('MyService', function() {
var myData = null;
return {
setData: function (data) {
myData = data;
},
doStuff: function () {
return myData.getSomeData();
}
};
});
Cela fonctionne de temps en temps, mais si les données asynchrone arrive à prendre plus de temps qu'il en faut pour tout pour obtenir initialisé j'obtiens une exception de pointeur null lorsque j'appelle doStuff()
Utilisation promesse objets
Ce serait probablement travailler. Le seul inconvénient c'partout j'appelle MyService je vais avoir de savoir que doStuff() renvoie une promesse et tout le code devra nous then
d'interagir avec la promesse. Je préfère juste attendre jusqu'à ce myData est de retour avant le chargement du ma demande.
Manuel Bootstrap
angular.element(document).ready(function() {
$.getJSON("data.json", function (data) {
//can't initialize the data here because the service doesn't exist yet
angular.bootstrap(document);
//too late to initialize here because something may have already
//tried to call doStuff() and would have got a null pointer exception
});
});
Mondiale Javascript Var
Je pourrais envoyer mon JSON directement global à une variable Javascript:
HTML:
<script type="text/javascript" src="data.js"></script>
data.js:
var dataForMyService = {
//myData here
};
Alors il serait disponible lors de l'initialisation de MyService
:
myModule.service('MyService', function() {
var myData = dataForMyService;
return {
doStuff: function () {
return myData.getSomeData();
}
};
});
Ce serait trop de travail, mais ensuite, j'ai un javascript global variable qui sent mauvais.
Sont mes options? Sont une de ces options mieux que les autres? Je sais que c'est une bonne question, mais je voulais montrer que j'ai essayé d'explorer toutes mes options. De toute orientation serait très apprécié.
- angulaires - le bootstrap de manière asynchrone promenades à travers le code pour extraire des données à partir d'un serveur avec
$http
, puis enregistrez les données dans un service, puis bootstrap d'une application.
Vous devez vous connecter pour publier un commentaire.
Avez-vous eu un coup d'oeil à
$routeProvider.lorsque('/path',{ résoudre:{...}
? Il peut faire la promesse approche un peu plus propre:Exposer une promesse à votre service:
Ajouter
resolve
à votre itinéraire config:Votre contrôleur ne pas obtenir instancié avant que toutes les dépendances sont résolues:
J'ai fait un exemple à plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview
resolve
propriété à un contrôleur qui n'est pas mentionné dans$routeProvider
. Par exemple,<div ng-controller="IndexCtrl"></div>
. Ici, le contrôleur est explicitement mentionné et ne sont pas chargés par le routage. Dans un tel cas, comment pourrait-on retarder l'instanciation du contrôleur?resolve
est rejetée, la vue n'est pas mis à jour (c'est ce que nous voulons), mais maintenant l'URL dans la barre d'adresse du navigateur est mis à jour vers le nouveau chemin (bizarre). En bref, à ce stade, la vue et l'url sont out-of-sync. Ce serait à l'origine de plusieurs problèmes. Pour plus de détails, reportez-vous à la question ouverte, Problème 2100MyService.promise
? Est-il utile pour une raison quelconque? parce que je n'ai pas vu que vous l'utilisez n'importe où. Si je voulais juste qu'il charge le service sans retour quelque chose de régler, il pourrait fonctionner, mais bizarre. Que dois-je faire dans ce cas?resolve
approche, est que si vous appelez$state.reload
à tout moment dans votre contrôleur, le résoudre fonction ne s'exécute à nouveau. Donc, si vous êtes en train de charger une fois que des données lorsque l'application démarre, mais ensuite le rechargement de l'état parce que vous souhaitez mettre à jour la page, vous retrouverez le rechargement de la fois les données...Basé sur Martin Atkins solution, voici une complète, concise pur-Angulaire de la solution:
Cette solution utilise un auto-exécution de la fonction anonyme pour obtenir le $service http, demande la config, et de l'injecter dans une constante appelée CONFIG quand il devient disponible.
Une fois complètement, nous attendons jusqu'à ce que le document est prêt et ensuite l'amorçage de l'Angulaire de l'app.
C'est une légère amélioration par rapport Martin solution, qui reportés de l'extraction de la configuration jusqu'à ce que après le document est prêt. Autant que je sache, il n'y a aucune raison de retarder l' $http appel pour que.
Tests Unitaires
Note: j'ai découvert cette solution ne fonctionne pas bien lorsque l'unité de test lorsque le code est inclus dans votre
app.js
fichier. La raison pour cela est que le code ci-dessus s'exécute immédiatement lorsque le fichier JS est chargé. Cela signifie que le framework de test (Jasmin dans mon cas) n'ont pas la possibilité de fournir une maquette de mise en œuvre de$http
.Ma solution, que je ne suis pas complètement satisfait, a été de déplacer ce code à notre
index.html
fichier, de sorte que le Grunt/Karma/Jasmin de test de l'unité de l'infrastructure ne le voit pas.J'ai utilisé une approche similaire à celle décrite par @XMLilley mais je voulais avoir la possibilité d'utiliser AngularJS services comme
$http
de charger la configuration et de poursuivre l'initialisation sans l'utilisation de bas niveau de l'Api jQuery.À l'aide de
resolve
sur les routes a aussi pas une option, car j'avais besoin de valeurs disponibles en tant que constantes lorsque mon application est démarrée, et même dansmodule.config()
blocs.J'ai créé une petite application AngularJS qui se charge de la configuration, définit comme constantes sur l'application et l'amorce il.
Le voir en action (à l'aide de
$timeout
au lieu de$http
) ici: http://plnkr.co/edit/FYznxP3xe8dxzwxs37hi?p=previewMise à JOUR
Je vous recommande d'utiliser l'approche décrite ci-dessous par Martin Atkins et JBCP.
Mise à JOUR 2
Parce que j'avais besoin dans de multiples projets, je viens de publier une charmille module qui s'occupe de cela: https://github.com/philippd/angular-deferred-bootstrap
Exemple qui charge les données à partir du back-end et définit une constante appelée APP_CONFIG sur le AngularJS module:
Le "manuel bootstrap" cas peuvent accéder aux Angulaire des services par la création manuelle d'un injecteur avant de bootstrap. Ce premier injecteur seront seul (ne pas être attaché à tous les éléments) et inclure uniquement un sous-ensemble des modules qui sont chargés. Si vous avez besoin de base Angulaire des services, il suffit juste de charge
ng
, comme ceci:Vous pouvez, par exemple, utiliser le
module.constant
mécanisme pour rendre les données disponibles pour votre application:Ce
myAppConfig
peut maintenant être injecté juste comme tout autre service, et, en particulier, il est disponible pendant la phase de configuration:ou, pour une petite application, vous avez juste injecter le global configuration directement dans votre service, au détriment de la diffusion des connaissances sur le format de configuration dans l'application.
Bien sûr, puisque les opérations asynchrones ici va bloquer le démarrage de l'application, et donc bloquer la compilation/liaison du modèle, il est sage d'utiliser le
ng-cloak
directive pour empêcher la unparsed modèle d'apparaître pendant le travail. Vous pouvez également fournir une sorte de chargement indication dans les DOM , en fournissant le code HTML qui sera affiché seulement jusqu'à ce que AngularJS initialise:J'ai créé un travail terminé, exemple de cette approche sur Plunker, chargement de la configuration à partir d'un statique fichier JSON comme un exemple.
J'ai eu le même problème: j'aime la
resolve
objet, mais cela ne fonctionne que pour le contenu de ng-view. Si vous avez des contrôleurs de premier niveau, nav, disons) qui existent en dehors de ng-view et qui a besoin d'être initialisé avec les données avant le routage même commence à se produire? Comment pouvons-nous éviter de coucher sur le côté serveur juste pour faire ce travail?Manuel d'utilisation de bootstrap et angulaire constante. Un naiive XHR vous permet de vous vos données, et vous bootstrap angulaire dans son rappel, qui concerne votre async questions. Dans l'exemple ci-dessous, vous n'avez même pas besoin de créer une variable globale. Les données renvoyées n'existe que dans angulaire portée comme un produit injectable, et n'est même pas présent à l'intérieur de contrôleurs, services, etc. à moins que vous l'injecter. (Comme vous injecter de la sortie de votre
resolve
objet dans le contrôleur pour une acheminé vue.) Si vous préférez, par la suite, d'interagir avec ces données en tant que service, vous pouvez créer un service, d'injecter des données, et personne ne pourra jamais l'être le plus sage.Exemple:
Maintenant, votre
NavData
constante existe. Aller de l'avant et à l'injecter dans un contrôleur ou d'un service:Bien sûr, à l'aide d'un nu XHR objet élimine un certain nombre de subtilités qui
$http
ou JQuery permettrait de prendre en charge pour vous, mais cet exemple fonctionne avec aucun des dépendances, au moins pour un simpleget
. Si vous voulez un peu plus de puissance pour votre demande, charger une bibliothèque externe pour vous aider. Mais je ne pense pas qu'il est possible d'accéder angulaire du$http
ou d'autres outils dans ce contexte.(DONC related post)
Ce que vous pouvez faire dans votre .config de l'application, c'est de créer de la détermination de l'objet pour la route et dans la fonction passe en $q (promesse de l'objet) et le nom du service que vous êtes en fonction, et de résoudre la promesse dans la fonction de rappel pour l' $http, dans le service comme suit:
ROUTE CONFIG
Angulaire de ne pas rendre le modèle ou rendre le contrôleur disponible jusqu'à reporter.resolve() a été appelé. On peut le faire dans notre service:
SERVICE
Maintenant que MyService a les données attribuées à des données de propriété, et la promesse de la route résoudre objet a été résolu, notre contrôleur pour la route des coups de pieds dans la vie, et nous pouvons céder les données à partir du service de notre objet de contrôleur.
CONTRÔLEUR
Maintenant tous nos contraignant dans le champ d'application de l'automate sera en mesure d'utiliser les données qui proviennent de MyService.
resolve
un objet avec une propriété de la fonction. donc, il a fini par êtreresolve:{ dataFetch: function(){ // call function here } }
J'ai donc trouvé une solution. J'ai créé un service angularJS, nous allons l'appeler MyDataRepository et j'ai créé un module pour cela. J'ai ensuite servir de ce fichier javascript de mon côté serveur contrôleur:
HTML:
Côté serveur:
Je peux ensuite l'injecter MyDataRepository où jamais j'en ai besoin:
Cela a très bien fonctionné pour moi, mais je suis ouvert à tout commentaires si quelqu'un a des.
}
Aussi, vous pouvez utiliser les techniques suivantes pour la fourniture de votre service à l'échelle mondiale, avant même les contrôleurs sont exécutées: https://stackoverflow.com/a/27050497/1056679. Juste résoudre vos données à l'échelle mondiale et ensuite passer à votre service dans
run
bloc par exemple.Vous pouvez utiliser
JSONP
pour charger de manière asynchrone de données de service.Le JSONP demande sera faite lors du chargement initial de la page, et les résultats seront disponibles avant votre application démarre. De cette façon, vous n'aurez pas à le gonfler votre routage redondant résout.
Vous html ressemblerait à ceci:
Façon la plus simple de chercher tous les initialiser l'utilisation ng-répertoire d'initialisation.
Vient de mettre ng-init div champ où vous souhaitez récupérer init données
index.html
index.js