AngularJS: paslaugos inicijavimas asinchroniniais duomenimis

Turiu „AngularJS“ paslaugą, kurią noriu inicijuoti kai kuriais asinchroniniais duomenimis. Kažkas panašaus:

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

Akivaizdu, kad tai neveiks, nes jei kažkas bando skambinti doStuff() prieš myData grįžimą, aš gausiu nulinio myData išimtį. Kiek galiu spręsti, perskaitydamas kai kuriuos kitus čia ir čia pateiktus klausimus, turiu keletą galimybių, tačiau nė vienas iš jų neatrodo labai švarus (gal aš kažką trūksta):

Paslaugų nustatymai su „paleisti“

Nustatydami programą, atlikite šiuos veiksmus:

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

Tada mano paslauga atrodys taip:

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

Tai veikia tam tikrą laiką, bet jei asinchroniniai duomenys užtrunka ilgiau, nei reikia inicijuoti, gaunu išimtį su nuliniu žymekliu, kai skambinu doStuff()

Naudokite pažadų objektus

Tai tikriausiai veiks. Vienintelė klaida, kurią aš vadinu „MyService“ visur, yra žinoti, kad „doStuff“ () grąžina pažadą, o visi kodai turės mums bendrauti su pažadu. Norėčiau tiesiog palaukti, kol myData grįš, prieš įkeliant savo prašymą.

Rankinis įkrovos juosta

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

Visuotinis „Javascript Var“, kurį galėčiau siųsti tiesiogiai į pasaulinį „Javascript“ kintamąjį:

HTML:

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

data.js:

 var dataForMyService = { // myData here }; 

Tada jis bus pasiekiamas inicijuojant „ MyService :

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

Tai taip pat veikia, bet tada turiu pasaulinį javascript kintamąjį, kuris kvepia blogai.

Ar tai mano vienintelės galimybės? Ar kai kurios iš šių galimybių yra geresnės nei kitos? Žinau, kad tai gana ilgas klausimas, bet norėjau parodyti, kad bandžiau ištirti visas savo galimybes. Bet koks vadovas būtų labai vertinamas.

429
29 апр. nustatyti testavimas123 Bal 29 2013-04-29 22:25 '13, 22:25 pm 2013-04-29 22:25
@ 10 atsakymų

Jūs $routeProvider.when('/path',{ resolve:{...} į $routeProvider.when('/path',{ resolve:{...} ?

Pateikite savo pažadą:

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

Pridėti resolve dėl sprendimo konfigūracijos:

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

Pateikiau pavyzdį plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview

307
30 апр. joakimbl atsakymas, pateiktas balandžio 30 d 2013-04-30 00:23 '13 prie 0:23 2013-04-30 00:23

Remiantis Martin Atkins sprendimu, čia yra „Angular“ išsamus, glaustas švarus sprendimas:

 (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']); }); } ); })(); 

Šis sprendimas naudoja spontanišką anoniminę funkciją, kad gautų $ http paslaugą, užklausų konfigūraciją ir įvestų į CONFIG konstantą, kai ji tampa prieinama.

Baigę, palauksime, kol dokumentas bus parengtas, tada atsisiųskite kampinę programą.

Tai nedidelis patobulinimas, palyginti su Martin sprendimu, kuris atideda konfigūracijos pasirinkimą, kol dokumentas bus parengtas. Kiek aš žinau, nėra jokios priežasties atidėti „http http“ skambutį.

Įrenginio testavimas

Pastaba app.js kad šis sprendimas neveikia su vieneto testavimu, kai kodas yra įtrauktas į jūsų app.js failą. Taip yra todėl, kad aukščiau nurodytas kodas veikia iškart po JS failo įkėlimo. Tai reiškia, kad bandymų aplinka (Jasmine mano atveju) negali pateikti prototipo $http įgyvendinimo.

Mano sprendimas, kurio aš nesu visiškai patenkintas, buvo perkelti šį kodą į mūsų index.html failą, todėl Grunt / Karma / Jasmine vieneto bandymų infrastruktūra nemato.

85
17 янв. Atsakymą JBCP pateikė sausio 17 d. 2014-01-17 18:04 '14, 18:04 2014-01-17 18:04

Aš naudoju panašų požiūrį į aprašymą, aprašytą @XMLilley, bet norėjau naudoti „AngularJS“ paslaugas, pvz., $http , įkelti konfigūraciją ir toliau inicijuoti, nenaudojant žemo lygio API arba jQuery.

Sprendimų naudojimas maršrutuose taip pat nebuvo įmanomas, nes man reikia vertybių, kurios bus prieinamos kaip konstantos, kai pradės veikti programa, net ir module.config() .

Sukūriau nedidelę „AngularJS“ programą, kuri įkelia konfigūraciją, nustato juos kaip konstantas tikroje programoje ir jas įkelia.

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

Žiūrėkite ją veikiant (naudodami $timeout o ne $http ) čia: http://plnkr.co/edit/FYznxP3xe8dxzwxs37hi?p=preview

UPDATE

Aš rekomenduoju naudoti Martin Atkins ir JBCP toliau aprašytą metodą.

UPDATE 2

Kadangi man reikėjo keliuose projektuose, ką tik išleido bower modulį, kuris rūpinsis šiuo klausimu: https://github.com/philippd/angular-deferred-bootstrap

Pavyzdys, įkeliantis duomenis iš vidaus įrenginio ir nustatantis pastovumą, vadinamą APP_CONFIG, į „AngularJS“ modulį:

 deferredBootstrapper.bootstrap({ element: document.body, module: 'MyApp', resolve: { APP_CONFIG: function ($http) { return $http.get('/api/demo-config'); } } }); 
48
09 нояб. atsakymą pateikė filippas 09 lapkritis 2013-11-09 04:27 '13, 4:27, 2013-11-09 04:27

Rankinis „bootstrap“ atvejis gali pasiekti kampines paslaugas rankiniu būdu sukuriant purkštuvą prieš pakrovimą. Šis pradinis purkštuvas stovės atskirai (nėra prijungtas prie jokių elementų) ir apima tik pakraunamų modulių pogrupį. Jei jums reikia tik pagrindinių kampinių paslaugų, tiesiog atsisiųsti ng , pvz .:

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

Pavyzdžiui, galite naudoti module.constant mechanizmą, kad duomenys būtų prieinami jūsų programai:

 myApp.constant('myAppConfig', data); 

Dabar šį „ myAppConfig galima įvesti tokiu pačiu būdu, kaip ir bet kurią kitą paslaugą, ir, visų pirma, ji pasiekiama konfigūravimo etape:

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

arba mažai programai, galite tiesiog įvesti pasaulinę konfigūraciją tiesiai į savo paslaugą skleisdami žinias apie konfigūracijos formatą visoje programoje.

Žinoma, kadangi čia esančios asinchroninės operacijos blokuoja programos įkėlimą ir tokiu būdu blokuoja šablono sudarymą / susiejimą, patartina naudoti „ ng-cloak direktyvą, kad netinkamas šablonas nebūtų rodomas operacijos metu, taip pat galite pateikti tam tikrą DOM apkrovos indikaciją, pateikiant kai kuriuos HTML rodoma tik tol, kol „AngularJS“ inicijuos

 <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> 

Plunkeryje sukūriau pilną šio metodo darbo pavyzdį, įkėlus konfigūraciją iš statinio JSON failo kaip pavyzdį.

41
08 дек. Atsakymas, kurį pateikė Martin Atkins, gruodžio 08 2013-12-08 04:13 '13, 4:13 2013-12-08 04:13

Turėjau tą pačią problemą: man patinka resolve objektą, bet tai veikia tik ng-view turiniui. Ką daryti, jei turite valdiklius (aukščiausiam lygiui, tarkim, iš tikrųjų), kurie egzistuoja už „ng-view“ ir kad juos reikia inicijuoti duomenimis prieš pradedant maršrutą netgi pradėti? Kaip išvengti serverio pusės, kad šis darbas būtų atliekamas?

Naudokite rankinį pakrovimą ir kampinį konstantą . Rasta XHR gauna jūsų duomenis ir įkeliate kampinį į savo atgalinį ryšį, kuris susijęs su jūsų asinchroninėmis problemomis. Toliau pateiktame pavyzdyje jums net nereikia sukurti pasaulinio kintamojo. Grąžinami duomenys egzistuoja tik švirkščiamame kampiniame regione ir netgi nepateikiami kontrolierių, paslaugų ir kt. Viduje. (Nesvarbu, kaip įvesite resolve objekto išvestį į valdiklį, kad būtų rodomas nukreiptas vaizdas.) Jei norite, kad vėliau su šiais duomenimis bendrautumėte kaip paslaugą, galite sukurti paslaugą, įvesti duomenis ir niekas nebus išmintingesnis.

Pavyzdys:

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

Dabar jūsų pastovus NavData egzistuoja. Eikite į priekį ir įveskite jį į valdiklį arba paslaugą:

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

Žinoma, naudojant pliką XHR objektą pašalinami keletas subtilybių, kurių $http arba JQuery rūpinsis jumis, bet šis pavyzdys veikia be jokių specialių priklausomybių, bent jau paprastam get . Jei norite šiek tiek daugiau galios jūsų užklausai, atsisiųskite išorinę biblioteką, kad padėtų jums. Tačiau nemanau, kad šiame kontekste galima pasiekti kampinį $http arba kitus įrankius.

(Su SO susijęs pranešimas )

13
20 авг. atsakymas pateikiamas XML 20 rug. 2013-08-20 16:33 '13, 16:33 2013-08-20 16:33

Ką galite padaryti savo „.config“ programoje, sukurti maršruto leidimo objektą ir funkciją perduoti $ q (pažadų objektas) ir paslaugos, kurią jūs priklausote, pavadinimą, ir išspręsti pažadą „$ http“ funkcijoje paslaugoje, pvz. :

ROUTE CONFIG

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

Kampiniame kampe nebus rodomas šablonas arba valdiklis nebus pasiekiamas, kol nebus iškviestas defer.resolve (). Tai galime padaryti mūsų paslaugose:

Paslauga

 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 the resolve function in our //route config q.resolve(); } } return MyService; }); 

Dabar, kai „MyService“ turi duomenis, susijusius su šia duomenų savybe, ir pažadas sprendžiant maršruto objektą buvo išspręstas, mūsų maršruto valdytojas yra pradėtas veikti ir mes galime priskirti duomenis iš paslaugos valdytojo objektui.

VALDIKLIS

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

Dabar visi mūsų įsipareigojimai valdytojo srityje galės naudoti „MyService“ sukurtus duomenis.

8
26 мая '13 в 0:41 2013-05-26 00:41 atsakymas buvo pateiktas dewd'ui gegužės 13 d., 13 val. 0:41 2013-05-26 00:41

Taigi, radau sprendimą. Aš sukūriau „AngularJS“ paslaugą, ją vadiname „MyDataRepository“, ir sukūriau jam modulį. Tada paleidžiu šį „JavaScript“ failą iš serverio valdiklio:

HTML:

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

serverio pusė:

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

Tada galiu įterpti „MyDataRepository“, kur man reikia:

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

Jis man atliko puikų darbą, bet aš atveriu bet kokią atsiliepimą, jei kas nors turi. }

5
02 мая '13 в 17:15 2013-05-02 17:15 atsakymas į testavimą123 02 gegužės 13 d. 17:15 2013-05-02 17:15

Be to, jūs galite naudoti šiuos metodus, kad galėtumėte teikti paslaugas visame pasaulyje prieš vykdant tikrus valdiklius: ngn-wiki.ru.site/questions/16089 / .... Paprasčiausiai išspręskite duomenis pasauliniu mastu ir, pvz., Perkelkite jį į savo paslaugą run bloke.

2
25 нояб. atsakymą pateikė Slava Fomin II . 2014-11-25 18:21 '14, 18:21 2014-11-25 18:21

JSONP galite naudoti asinchroniškai įkelti paslaugų duomenis. JSONP užklausa bus pateikta pradinio puslapio įkėlimo metu, o rezultatai bus pasiekiami prieš pradedant paraišką. Taigi nereikia pripildyti maršruto su nereikalingais leidimais.

Jūs, html, atrodys taip:

Lengviausias būdas gauti bet kokį inicijavimą yra naudoti „ng-init“ katalogą.

Tiesiog įdėkite ng-init div sritį, kurioje norite inicijuoti duomenis.

index.html

 <div class="frame" ng-init="init()"> <div class="bit-1"> <div class="field pr"> <label ng-show="regi_step2.address" class="show-hide ct-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; }); }; 

PASTABA: Jūs galite naudoti šią metodiką, jei neturite to paties kodo daugiau nei vienoje vietoje.

-1
16 марта '16 в 16:59 2016-03-16 16:59 atsakymas duotas Roni kovo 16, 16 d. 16:59 2016-03-16 16:59

Kiti klausimai apie „ žymes „ arba „ Ask a question“