AngularJS: initialiser le service avec des données asynchrones

475

J'ai un service AngularJS que je veux initialiser avec des données asynchrones. Quelque chose comme ça:

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 fonctionnera pas parce que si quelque chose essaie d'appeler doStuff()avant myDatarevient, j'obtiendrai une exception de pointeur nul. Autant que je sache en lisant certaines des autres questions posées ici et ici, j'ai quelques options, mais aucune ne semble très propre (peut-être que je manque quelque chose):

Service d'installation avec "exécuter"

Lors de la configuration de mon application, procédez comme suit:

myApp.run(function ($http, MyService) {
    $http.get('data.json').success(function (data) {
        MyService.setData(data);
    });
});

Ensuite, mon service ressemblerait à ceci:

myModule.service('MyService', function() {
    var myData = null;
    return {
        setData: function (data) {
            myData = data;
        },
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

Cela fonctionne parfois, mais si les données asynchrones prennent plus de temps qu'il n'en faut pour que tout soit initialisé, j'obtiens une exception de pointeur nul lorsque j'appelle doStuff()

Utilisez des objets de promesse

Cela fonctionnerait probablement. Le seul inconvénient partout où j'appelle MyService, je devrai savoir que doStuff () renvoie une promesse et tout le code devra nous theninteragir avec la promesse. Je préfère simplement attendre que mes données soient de retour avant de charger mon application.

Bootstrap manuel

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

Global Javascript Var Je pourrais envoyer mon JSON directement à une variable Javascript globale:

HTML:

<script type="text/javascript" src="data.js"></script>

data.js:

var dataForMyService = { 
// myData here
};

Il serait alors disponible lors de l'initialisation MyService:

myModule.service('MyService', function() {
    var myData = dataForMyService;
    return {
        doStuff: function () {
            return myData.getSomeData();
        }
    };
});

Cela fonctionnerait aussi, mais j'ai une variable javascript globale qui sent mauvais.

Ce sont mes seules options? L'une de ces options est-elle meilleure que les autres? Je sais que c'est une question assez longue, mais je voulais montrer que j'ai essayé d'explorer toutes mes options. Tout conseil serait grandement apprécié.

testing123
la source
angular - bootstrap parcourt de manière asynchrone le code pour extraire les données d'un serveur avec $http, puis enregistre les données dans un service, puis amorce une application.
Steven Wexler

Réponses:

327

Avez-vous regardé $routeProvider.when('/path',{ resolve:{...}? Cela peut rendre l'approche de la promesse un peu plus propre:

Exposez une promesse à votre service:

app.service('MyService', function($http) {
    var myData = null;

    var promise = $http.get('data.json').success(function (data) {
      myData = data;
    });

    return {
      promise:promise,
      setData: function (data) {
          myData = data;
      },
      doStuff: function () {
          return myData;//.getSomeData();
      }
    };
});

Ajoutez resolveà votre configuration d'itinéraire:

app.config(function($routeProvider){
  $routeProvider
    .when('/',{controller:'MainCtrl',
    template:'<div>From MyService:<pre>{{data | json}}</pre></div>',
    resolve:{
      'MyServiceData':function(MyService){
        // MyServiceData will also be injectable in your controller, if you don't want this you could create a new promise with the $q service
        return MyService.promise;
      }
    }})
  }):

Votre contrôleur ne sera pas instancié avant que toutes les dépendances ne soient résolues:

app.controller('MainCtrl', function($scope,MyService) {
  console.log('Promise is now resolved: '+MyService.doStuff().data)
  $scope.data = MyService.doStuff();
});

J'ai fait un exemple sur plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview

joakimbl
la source
1
Merci beaucoup pour votre réponse! Cela fonctionnerait pour moi si je n'avais pas déjà de service dans la carte de résolution qui utilise MyService. J'ai mis à jour votre plongeur avec ma situation: plnkr.co/edit/465Cupaf5mtxljCl5NuF?p=preview . Existe-t-il un moyen de faire en sorte que MyOtherService attende que MyService soit initialisé?
testing123
2
Je suppose que j'enchaînerais les promesses dans MyOtherService - j'ai mis à jour le plongeur avec l'enchaînement et quelques commentaires - à quoi cela ressemble-t-il? plnkr.co/edit/Z7dWVNA9P44Q72sLiPjW?p=preview
joakimbl
3
J'ai essayé cela et j'ai quand même rencontré des problèmes parce que j'ai des directives et d'autres contrôleurs (le contrôleur que j'utilise avec $ routeProvider gère les éléments de navigation principaux et secondaires ... c'est-à-dire 'MyOtherService') qui doivent attendre jusqu'à ce que 'MyService ' est résolu. Je continuerai d'essayer et de mettre à jour cela avec tout le succès que j'ai. Je souhaite juste qu'il y ait un crochet en angulaire où je pourrais attendre le retour des données avant d'initialiser mes contrôleurs et directives. Merci encore pour votre aide. Si j'avais un contrôleur principal qui enveloppait tout cela aurait fonctionné.
testing123
43
Une question ici - comment allez-vous attribuer la resolveproprié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 n'est pas chargé via le routage. Dans un tel cas, comment retarderait-on alors l'instanciation du contrôleur?
callmekatootie
19
Et si vous n'utilisiez pas le routage? Cela revient presque à dire que vous ne pouvez pas écrire une application angulaire avec des données asynchrones à moins d'utiliser le routage. La manière recommandée pour obtenir des données dans une application est de les charger de manière asynchrone, mais dès que vous avez plus d'un contrôleur et que vous lancez des services, BOOM, c'est impossible.
wired_in
88

Basé sur la solution de Martin Atkins, voici une solution pure angulaire complète et concise:

(function() {
  var initInjector = angular.injector(['ng']);
  var $http = initInjector.get('$http');
  $http.get('/config.json').then(
    function (response) {
      angular.module('config', []).constant('CONFIG', response.data);

      angular.element(document).ready(function() {
          angular.bootstrap(document, ['myApp']);
        });
    }
  );
})();

Cette solution utilise une fonction anonyme auto-exécutable pour obtenir le service $ http, demander la configuration et l'injecter dans une constante appelée CONFIG lorsqu'elle sera disponible.

Une fois complètement, nous attendons que le document soit prêt, puis amorçons l'application Angular.

Il s'agit d'une légère amélioration par rapport à la solution de Martin, qui a reporté la récupération de la configuration jusqu'à ce que le document soit prêt. Pour autant que je sache, il n'y a aucune raison de retarder l'appel $ http pour cela.

Tests unitaires

Remarque: J'ai découvert que cette solution ne fonctionne pas bien lors des tests unitaires lorsque le code est inclus dans votre app.jsfichier. La raison en est que le code ci-dessus s'exécute immédiatement lorsque le fichier JS est chargé. Cela signifie que le framework de test (Jasmine dans mon cas) n'a pas la possibilité de fournir une implémentation simulée de $http.

Ma solution, dont je ne suis pas entièrement satisfait, a été de déplacer ce code dans notre index.htmlfichier, de sorte que l'infrastructure de test unitaire Grunt / Karma / Jasmine ne le voit pas.

JBCP
la source
1
La règle telle que «ne pollue pas la portée globale» ne doit être suivie que dans la mesure où elle améliore notre code (moins complexe, plus facile à maintenir, plus sûr, etc.). Je ne vois pas comment cette solution est meilleure que de simplement charger les données dans une seule variable globale. Qu'est-ce que je rate?
david004
4
Il vous permet d'utiliser le système d'injection de dépendances d'Angular pour accéder à la constante 'CONFIG' dans les modules qui en ont besoin, mais vous ne risquez pas d'encombrer d'autres modules qui n'en ont pas. Par exemple, si vous avez utilisé une variable de configuration globale, il est possible qu'un autre code tiers recherche également la même variable.
JBCP
1
Je suis un débutant angulaire, voici quelques notes sur la façon dont j'ai obtenu la dépendance du module de configuration résolu dans mon application: gist.github.com/dsulli99/0be3e80db9b21ce7b989 ref: tutorials.jenkov.com/angularjs/… Merci pour cette solution.
dps
7
Il est mentionné dans un commentaire dans l'une des autres solutions de bootstrap manuelles ci-dessous, mais en tant que débutant angulaire qui ne l'a pas repéré, je peux simplement souligner que vous devez supprimer votre directive ng-app dans votre code html pour que cela fonctionne correctement - il remplace le bootstrap automatique (via ng-app) par cette méthode manuelle. Si vous ne retirez pas l'application ng, l'application peut fonctionner, mais vous verrez diverses erreurs de fournisseurs inconnus dans la console.
IrishDubGuy
49

J'ai utilisé une approche similaire à celle décrite par @XMLilley mais je voulais avoir la possibilité d'utiliser les services AngularJS comme $httppour charger la configuration et faire une initialisation supplémentaire sans utiliser d'API de bas niveau ou jQuery.

L'utilisation resolvesur les routes n'était pas non plus une option car j'avais besoin que les valeurs soient disponibles sous forme de constantes au démarrage de mon application, même en module.config()blocs.

J'ai créé une petite application AngularJS qui charge la configuration, les définit comme constantes sur l'application réelle et l'amorce.

// define the module of your app
angular.module('MyApp', []);

// define the module of the bootstrap app
var bootstrapModule = angular.module('bootstrapModule', []);

// the bootstrapper service loads the config and bootstraps the specified app
bootstrapModule.factory('bootstrapper', function ($http, $log, $q) {
  return {
    bootstrap: function (appName) {
      var deferred = $q.defer();

      $http.get('/some/url')
        .success(function (config) {
          // set all returned values as constants on the app...
          var myApp = angular.module(appName);
          angular.forEach(config, function(value, key){
            myApp.constant(key, value);
          });
          // ...and bootstrap the actual app.
          angular.bootstrap(document, [appName]);
          deferred.resolve();
        })
        .error(function () {
          $log.warn('Could not initialize application, configuration could not be loaded.');
          deferred.reject();
        });

      return deferred.promise;
    }
  };
});

// create a div which is used as the root of the bootstrap app
var appContainer = document.createElement('div');

// in run() function you can now use the bootstrapper service and shutdown the bootstrapping app after initialization of your actual app
bootstrapModule.run(function (bootstrapper) {

  bootstrapper.bootstrap('MyApp').then(function () {
    // removing the container will destroy the bootstrap app
    appContainer.remove();
  });

});

// make sure the DOM is fully loaded before bootstrapping.
angular.element(document).ready(function() {
  angular.bootstrap(appContainer, ['bootstrapModule']);
});

Voyez-le en action (en utilisant $timeoutau lieu de $http) ici: http://plnkr.co/edit/FYznxP3xe8dxzwxs37hi?p=preview

MISE À JOUR

Je recommanderais d'utiliser l'approche décrite ci-dessous par Martin Atkins et JBCP.

MISE À JOUR 2

Parce que j'en avais besoin dans plusieurs projets, je viens de publier un module bower qui s'en occupe: https://github.com/philippd/angular-deferred-bootstrap

Exemple qui charge des données depuis le back-end et définit une constante appelée APP_CONFIG sur le module AngularJS:

deferredBootstrapper.bootstrap({
  element: document.body,
  module: 'MyApp',
  resolve: {
    APP_CONFIG: function ($http) {
      return $http.get('/api/demo-config');
    }
  }
});
philippd
la source
11
deferredBootstrapper est la voie à suivre
fatlinesofcode
44

Le cas «bootstrap manuel» peut accéder aux services angulaires en créant manuellement un injecteur avant le bootstrap. Cet injecteur initial sera autonome (ne sera attaché à aucun élément) et n'inclura qu'un sous-ensemble des modules chargés. Si vous n'avez besoin que des services Angular de base, il suffit de charger ng, comme ceci:

angular.element(document).ready(
    function() {
        var initInjector = angular.injector(['ng']);
        var $http = initInjector.get('$http');
        $http.get('/config.json').then(
            function (response) {
               var config = response.data;
               // Add additional services/constants/variables to your app,
               // and then finally bootstrap it:
               angular.bootstrap(document, ['myApp']);
            }
        );
    }
);

Vous pouvez, par exemple, utiliser le module.constantmécanisme pour rendre les données disponibles pour votre application:

myApp.constant('myAppConfig', data);

Celui-ci myAppConfigpeut désormais être injecté comme tout autre service, et en particulier il est disponible lors de la phase de configuration:

myApp.config(
    function (myAppConfig, someService) {
        someService.config(myAppConfig.someServiceConfig);
    }
);

ou, pour une application plus petite, vous pouvez simplement injecter la configuration globale 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 bloqueront le bootstrap de l'application, et donc bloqueront la compilation / liaison du modèle, il est sage d'utiliser la ng-cloakdirective pour empêcher le modèle non analysé de s'afficher pendant le travail. Vous pouvez également fournir une sorte d'indication de chargement dans le DOM, en fournissant du code HTML qui s'affiche uniquement jusqu'à ce que AngularJS s'initialise:

<div ng-if="initialLoad">
    <!-- initialLoad never gets set, so this div vanishes as soon as Angular is done compiling -->
    <p>Loading the app.....</p>
</div>
<div ng-cloak>
    <!-- ng-cloak attribute is removed once the app is done bootstrapping -->
    <p>Done loading the app!</p>
</div>

J'ai créé un exemple complet et fonctionnel de cette approche sur Plunker, en chargeant la configuration à partir d'un fichier JSON statique comme exemple.

Martin Atkins
la source
Je ne pense pas que vous ayez besoin de reporter le $ http.get () jusqu'à ce que le document soit prêt.
JBCP
@JBCP oui, vous avez raison de dire que cela fonctionne aussi bien si vous échangez les événements afin que nous n'attendions pas que le document soit prêt avant le retour de la réponse HTTP, avec l'avantage de pouvoir éventuellement commencer le HTTP demander plus rapidement. Seul l'appel d'amorçage doit attendre que le DOM soit prêt.
Martin Atkins
2
J'ai créé un module bower avec votre approche: github.com/philippd/angular-deferred-bootstrap
philippd
@MartinAtkins, je viens de découvrir que votre excellente approche ne fonctionne pas avec Angular v1.1 +. Il semble que les premières versions d'Angular ne comprennent tout simplement pas "alors" jusqu'à ce que l'application soit démarrée. Pour le voir dans votre Plunk, remplacez l'URL angulaire par code.angularjs.org/1.1.5/angular.min.js
vkelman
16

J'ai eu le même problème: j'aime l' resolveobjet, mais cela ne fonctionne que pour le contenu de ng-view. Et si vous avez des contrôleurs (pour la navigation de niveau supérieur, disons) qui existent en dehors de ng-view et qui doivent être initialisés avec des données avant même que le routage ne commence? Comment éviter de tourner autour du serveur juste pour que cela fonctionne?

Utilisez un bootstrap manuel et une constante angulaire . Un XHR naïf vous récupère vos données, et vous amorcez angulairement dans son rappel, qui traite de vos problèmes asynchrones. Dans l'exemple ci-dessous, vous n'avez même pas besoin de créer une variable globale. Les données renvoyées n'existent que dans une portée angulaire en tant qu'injectable et ne sont même pas présentes à l'intérieur des contrôleurs, des services, etc., sauf si vous les injectez. (Tout comme vous injecteriez la sortie de votre resolveobjet dans le contrôleur pour une vue routée.) Si vous préférez ensuite interagir avec ces données en tant que service, vous pouvez créer un service, injecter les données, et personne ne sera jamais plus sage .

Exemple:

//First, we have to create the angular module, because all the other JS files are going to load while we're getting data and bootstrapping, and they need to be able to attach to it.
var MyApp = angular.module('MyApp', ['dependency1', 'dependency2']);

// Use angular's version of document.ready() just to make extra-sure DOM is fully 
// loaded before you bootstrap. This is probably optional, given that the async 
// data call will probably take significantly longer than DOM load. YMMV.
// Has the added virtue of keeping your XHR junk out of global scope. 
angular.element(document).ready(function() {

    //first, we create the callback that will fire after the data is down
    function xhrCallback() {
        var myData = this.responseText; // the XHR output

        // here's where we attach a constant containing the API data to our app 
        // module. Don't forget to parse JSON, which `$http` normally does for you.
        MyApp.constant('NavData', JSON.parse(myData));

        // now, perform any other final configuration of your angular module.
        MyApp.config(['$routeProvider', function ($routeProvider) {
            $routeProvider
              .when('/someroute', {configs})
              .otherwise({redirectTo: '/someroute'});
          }]);

        // And last, bootstrap the app. Be sure to remove `ng-app` from your index.html.
        angular.bootstrap(document, ['NYSP']);
    };

    //here, the basic mechanics of the XHR, which you can customize.
    var oReq = new XMLHttpRequest();
    oReq.onload = xhrCallback;
    oReq.open("get", "/api/overview", true); // your specific API URL
    oReq.send();
})

Maintenant, votre NavDataconstante existe. Allez-y et injectez-le dans un contrôleur ou un service:

angular.module('MyApp')
    .controller('NavCtrl', ['NavData', function (NavData) {
        $scope.localObject = NavData; //now it's addressable in your templates 
}]);

Bien sûr, l'utilisation d'un objet XHR nu supprime un certain nombre de subtilités qui $httpou JQuery prendrait soin de vous, mais cet exemple fonctionne sans dépendances spéciales, du moins pour un simple get. Si vous voulez un peu plus de puissance pour votre demande, chargez une bibliothèque externe pour vous aider. Mais je ne pense pas qu'il soit possible d'accéder aux $httpoutils angulaires ou autres dans ce contexte.

( Poste lié à SO )

XML
la source
8

Ce que vous pouvez faire dans votre .config pour l'application est de créer l'objet de résolution pour l'itinéraire et dans la fonction, passez dans $ q (objet de promesse) et le nom du service dont vous dépendez, et résolvez la promesse dans le fonction de rappel pour le $ http dans le service comme ceci:

CONFIGURATION D'ITINÉRAIRE

app.config(function($routeProvider){
    $routeProvider
     .when('/',{
          templateUrl: 'home.html',
          controller: 'homeCtrl',
          resolve:function($q,MyService) {
                //create the defer variable and pass it to our service
                var defer = $q.defer();
                MyService.fetchData(defer);
                //this will only return when the promise
                //has been resolved. MyService is going to
                //do that for us
                return defer.promise;
          }
      })
}

Angular ne rendra pas le modèle ou rendra le contrôleur disponible jusqu'à ce que defer.resolve () ait été appelé. Nous pouvons le faire à notre service:

UN SERVICE

app.service('MyService',function($http){
       var MyService = {};
       //our service accepts a promise object which 
       //it will resolve on behalf of the calling function
       MyService.fetchData = function(q) {
             $http({method:'GET',url:'data.php'}).success(function(data){
                 MyService.data = data;
                 //when the following is called it will
                 //release the calling function. in this
                 //case it's the resolve function in our
                 //route config
                 q.resolve();
             }
       }

       return MyService;
});

Maintenant que MyService a les données affectées à sa propriété de données et que la promesse dans l'objet de résolution d'itinéraire a été résolue, notre contrôleur pour l'itinéraire prend vie, et nous pouvons affecter les données du service à notre objet contrôleur.

MANETTE

  app.controller('homeCtrl',function($scope,MyService){
       $scope.servicedata = MyService.data;
  });

Désormais, toutes nos liaisons dans le cadre du contrôleur pourront utiliser les données provenant de MyService.

rosée
la source
Je vais essayer ça quand j'aurai plus de temps. Cela ressemble à ce que d'autres essayaient de faire dans ngModules.
testing123
1
J'aime cette approche et je l'ai utilisée auparavant, mais j'essaie actuellement de savoir comment le faire de manière propre lorsque j'ai plusieurs itinéraires, chacun pouvant ou non dépendre de données prélues. Des réflexions là-dessus?
ivarni
btw, je penche pour que chaque service qui nécessite des données prérécupérées fasse la demande lors de son initialisation et retourne une promesse puis configure des objets de résolution avec les services requis par les différentes routes. Je souhaite juste qu'il y ait une manière moins verbeuse.
ivarni
1
@dewd C'est ce que je visais, mais je préférerais de beaucoup qu'il y ait un moyen de simplement dire "aller chercher tout ce truc d'abord quelle que soit la route qui est chargée" sans avoir à répéter mes blocs de résolution. Ils ont tous quelque chose dont ils dépendent. Mais ce n'est pas énorme, ça se sentirait juste un peu plus SEC :)
ivarni
2
c'est le chemin que j'ai fini par emprunter sauf que je devais faire resolveun objet avec une propriété comme fonction. il a donc fini par êtreresolve:{ dataFetch: function(){ // call function here } }
aron.duby
5

J'ai donc trouvé une solution. J'ai créé un service angularJS, nous l'appellerons MyDataRepository et j'ai créé un module pour celui-ci. Je sers ensuite ce fichier javascript depuis mon contrôleur côté serveur:

HTML:

<script src="path/myData.js"></script>

Du côté serveur:

@RequestMapping(value="path/myData.js", method=RequestMethod.GET)
public ResponseEntity<String> getMyDataRepositoryJS()
{
    // Populate data that I need into a Map
    Map<String, String> myData = new HashMap<String,String>();
    ...
    // Use Jackson to convert it to JSON
    ObjectMapper mapper = new ObjectMapper();
    String myDataStr = mapper.writeValueAsString(myData);

    // Then create a String that is my javascript file
    String myJS = "'use strict';" +
    "(function() {" +
    "var myDataModule = angular.module('myApp.myData', []);" +
    "myDataModule.service('MyDataRepository', function() {" +
        "var myData = "+myDataStr+";" +
        "return {" +
            "getData: function () {" +
                "return myData;" +
            "}" +
        "}" +
    "});" +
    "})();"

    // Now send it to the client:
    HttpHeaders responseHeaders = new HttpHeaders();
    responseHeaders.add("Content-Type", "text/javascript");
    return new ResponseEntity<String>(myJS , responseHeaders, HttpStatus.OK);
}

Je peux ensuite injecter MyDataRepository partout où j'en ai besoin:

someOtherModule.service('MyOtherService', function(MyDataRepository) {
    var myData = MyDataRepository.getData();
    // Do what you have to do...
}

Cela a très bien fonctionné pour moi, mais je suis ouvert à toute rétroaction si quelqu'un en a. }

testing123
la source
J'aime votre approche modulaire. J'ai constaté que $ routeScope est disponible pour le service demandant des données et vous pouvez lui affecter des données dans le rappel $ http.success. Cependant, l'utilisation de $ routeScope pour des éléments non globaux crée une odeur et les données doivent vraiment être affectées à la portée $ du contrôleur. Malheureusement, je pense que votre approche, bien qu'innovante, n'est pas idéale (respect cependant pour trouver quelque chose qui vous convient). Je suis juste sûr qu'il doit y avoir une réponse côté client uniquement qui attend en quelque sorte les données et permet l'attribution à la portée. La recherche continue!
rosée
Au cas où cela serait utile à quelqu'un, j'ai récemment vu différentes approches en regardant les modules que d'autres personnes ont écrits et ajoutés au site Web ngModules. Lorsque j'aurai plus de temps, je devrai commencer à utiliser l'un d'entre eux, ou comprendre ce qu'ils ont fait et l'ajouter à mes trucs.
testing123
2

En outre, vous pouvez utiliser les techniques suivantes pour provisionner votre service globalement, avant l'exécution des contrôleurs réels: https://stackoverflow.com/a/27050497/1056679 . Il suffit de résoudre vos données globalement puis de les transmettre à votre service en runbloc par exemple.

Slava Fomin II
la source
1

Vous pouvez utiliser JSONPpour charger de manière asynchrone des données de service. La demande JSONP sera effectuée lors du chargement initial de la page et les résultats seront disponibles avant le démarrage de votre application. De cette façon, vous n'aurez pas à gonfler votre routage avec des résolutions redondantes.

Votre html ressemblerait à ceci:

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script>

function MyService {
  this.getData = function(){
    return   MyService.data;
  }
}
MyService.setData = function(data) {
  MyService.data = data;
}

angular.module('main')
.service('MyService', MyService)

</script>
<script src="/some_data.php?jsonp=MyService.setData"></script>
Hégémon
la source
-1

Le moyen le plus simple de récupérer n'importe quel répertoire initialize utilise ng-init.

Mettez simplement la portée div ng-init où vous voulez récupérer les données init

index.html

<div class="frame" ng-init="init()">
    <div class="bit-1">
      <div class="field p-r">
        <label ng-show="regi_step2.address" class="show-hide c-t-1 ng-hide" style="">Country</label>
        <select class="form-control w-100" ng-model="country" name="country" id="country" ng-options="item.name for item in countries" ng-change="stateChanged()" >
        </select>
        <textarea class="form-control w-100" ng-model="regi_step2.address" placeholder="Address" name="address" id="address" ng-required="true" style=""></textarea>
      </div>
    </div>
  </div>

index.js

$scope.init=function(){
    $http({method:'GET',url:'/countries/countries.json'}).success(function(data){
      alert();
           $scope.countries = data;
    });
  };

REMARQUE: vous pouvez utiliser cette méthodologie si vous n'avez pas le même code plus d'un endroit.

Roni
la source
Il n'est pas conseillé d'utiliser ngInit selon les documents: docs.angularjs.org/api/ng/directive/ngInit
fodma1