Saya memiliki layanan AngularJS yang ingin saya inisialisasi dengan beberapa data asinkron. Sesuatu seperti ini:
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();
}
};
});
Jelas ini tidak akan berhasil karena jika sesuatu mencoba menelepon doStuff()
sebelum myData
kembali saya akan mendapatkan pengecualian null pointer. Sejauh yang saya tahu dari membaca beberapa pertanyaan lain yang diajukan di sini dan di sini saya punya beberapa pilihan, tetapi tidak ada yang tampak sangat bersih (mungkin saya kehilangan sesuatu):
Setup Service dengan "run"
Saat menyiapkan aplikasi saya lakukan ini:
myApp.run(function ($http, MyService) {
$http.get('data.json').success(function (data) {
MyService.setData(data);
});
});
Maka layanan saya akan terlihat seperti ini:
myModule.service('MyService', function() {
var myData = null;
return {
setData: function (data) {
myData = data;
},
doStuff: function () {
return myData.getSomeData();
}
};
});
Ini berfungsi beberapa waktu tetapi jika data asinkron terjadi lebih lama dari yang dibutuhkan untuk semuanya untuk diinisialisasi, saya mendapatkan pengecualian null pointer ketika saya menelepon doStuff()
Gunakan benda janji
Ini mungkin akan berhasil. Satu-satunya downside di mana-mana saya sebut MyService saya harus tahu bahwa doStuff () mengembalikan janji dan semua kode harus kita then
untuk berinteraksi dengan janji itu. Saya lebih suka menunggu sampai myData kembali sebelum memuat aplikasi saya.
Bootstrap Manual
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 Saya bisa mengirim JSON saya langsung ke variabel Javascript global:
HTML:
<script type="text/javascript" src="data.js"></script>
data.js:
var dataForMyService = {
// myData here
};
Maka akan tersedia saat menginisialisasi MyService
:
myModule.service('MyService', function() {
var myData = dataForMyService;
return {
doStuff: function () {
return myData.getSomeData();
}
};
});
Ini akan berhasil juga, tetapi kemudian saya memiliki variabel javascript global yang baunya buruk.
Apakah ini satu-satunya pilihan saya? Apakah salah satu opsi ini lebih baik daripada yang lain? Saya tahu ini adalah pertanyaan yang cukup panjang, tetapi saya ingin menunjukkan bahwa saya telah mencoba mengeksplorasi semua opsi saya. Bimbingan apa pun akan sangat dihargai.
sumber
$http
, lalu menyimpan data dalam suatu layanan, lalu mem-bootstrap aplikasi.Jawaban:
Apakah Anda sudah melihatnya
$routeProvider.when('/path',{ resolve:{...}
? Itu bisa membuat pendekatan janji sedikit lebih bersih:Paparkan janji dalam layanan Anda:
Tambahkan
resolve
ke konfigurasi rute Anda:Pengontrol Anda tidak akan instantiasi sebelum semua dependensi diselesaikan:
Saya telah membuat contoh di plnkr: http://plnkr.co/edit/GKg21XH0RwCMEQGUdZKH?p=preview
sumber
resolve
properti ke controller yang tidak disebutkan dalam$routeProvider
. Sebagai contoh<div ng-controller="IndexCtrl"></div>
,. Di sini, pengontrol disebutkan secara eksplisit dan tidak dimuat melalui perutean. Dalam kasus seperti itu, bagaimana mungkin seseorang menunda instantiation dari controller itu?Berdasarkan solusi Martin Atkins, berikut ini adalah solusi Angular murni lengkap dan ringkas:
Solusi ini menggunakan fungsi anonim yang dijalankan sendiri untuk mendapatkan layanan $ http, meminta konfigurasi, dan menyuntikkannya ke dalam konstanta yang disebut CONFIG saat tersedia.
Setelah sepenuhnya, kami menunggu hingga dokumen siap dan kemudian bootstrap aplikasi Angular.
Ini sedikit peningkatan dari solusi Martin, yang ditangguhkan mengambil konfigurasi sampai setelah dokumen siap. Sejauh yang saya tahu, tidak ada alasan untuk menunda panggilan $ http untuk itu.
Pengujian Unit
Catatan: Saya menemukan solusi ini tidak berfungsi dengan baik ketika unit-testing ketika kode dimasukkan dalam
app.js
file Anda . Alasan untuk ini adalah bahwa kode di atas berjalan segera ketika file JS dimuat. Ini berarti kerangka uji (Jasmine dalam kasus saya) tidak memiliki kesempatan untuk memberikan implementasi tiruan$http
.Solusi saya, yang saya tidak sepenuhnya puas dengan, adalah memindahkan kode ini ke
index.html
file kami , sehingga infrastruktur uji unit Grunt / Karma / Jasmine tidak melihatnya.sumber
Saya menggunakan pendekatan yang mirip dengan yang dijelaskan oleh @XMLilley tetapi ingin memiliki kemampuan untuk menggunakan layanan AngularJS suka
$http
memuat konfigurasi dan melakukan inisialisasi lebih lanjut tanpa menggunakan API tingkat rendah atau jQuery.Menggunakan
resolve
rute juga bukan pilihan karena saya membutuhkan nilai yang tersedia sebagai konstanta ketika aplikasi saya dimulai, bahkan dalammodule.config()
blok.Saya membuat aplikasi AngularJS kecil yang memuat konfigurasi, menetapkan mereka sebagai konstanta pada aplikasi aktual dan bootstraps-nya.
Lihat dalam aksi (
$timeout
alih-alih menggunakan$http
) di sini: http://plnkr.co/edit/FYznxP3xe8dxzwxs37hi?p=previewMEMPERBARUI
Saya akan merekomendasikan untuk menggunakan pendekatan yang dijelaskan di bawah ini oleh Martin Atkins dan JBCP.
PEMBARUAN 2
Karena saya membutuhkannya di beberapa proyek, saya baru saja merilis modul bower yang menangani ini: https://github.com/philippd/angular-deferred-bootstrap
Contoh yang memuat data dari back-end dan menetapkan konstanta yang disebut APP_CONFIG pada modul AngularJS:
sumber
Kasing "manual bootstrap" dapat memperoleh akses ke layanan Angular dengan secara manual membuat injektor sebelum bootstrap. Injektor awal ini akan berdiri sendiri (tidak melekat pada elemen apa pun) dan hanya menyertakan sebagian dari modul yang dimuat. Jika yang Anda butuhkan adalah layanan inti Angular, itu cukup untuk memuat saja
ng
, seperti ini:Anda dapat, misalnya, menggunakan
module.constant
mekanisme untuk membuat data tersedia untuk aplikasi Anda:Ini
myAppConfig
sekarang dapat disuntikkan sama seperti layanan lain, dan khususnya itu tersedia selama tahap konfigurasi:atau, untuk aplikasi yang lebih kecil, Anda bisa menyuntikkan konfigurasi global langsung ke layanan Anda, dengan mengorbankan penyebaran pengetahuan tentang format konfigurasi di seluruh aplikasi.
Tentu saja, karena operasi async di sini akan memblokir bootstrap aplikasi, dan dengan demikian memblokir kompilasi / penautan templat, sebaiknya gunakan
ng-cloak
arahan untuk mencegah agar templat yang tidak diuraikan muncul selama pekerjaan. Anda juga dapat memberikan semacam indikasi memuat di DOM, dengan menyediakan beberapa HTML yang hanya ditampilkan hingga AngularJS diinisialisasi:Saya membuat contoh yang lengkap dan berfungsi dari pendekatan ini pada Plunker, memuat konfigurasi dari file JSON statis sebagai contoh.
sumber
Saya punya masalah yang sama: Saya suka
resolve
objeknya, tapi itu hanya berfungsi untuk konten ng-view. Bagaimana jika Anda memiliki pengontrol (untuk nav tingkat atas, katakanlah) yang ada di luar ng-view dan yang perlu diinisialisasi dengan data sebelum perutean bahkan mulai terjadi? Bagaimana kita menghindari mucking di sisi server hanya untuk membuatnya bekerja?Gunakan bootstrap manual dan konstanta sudut . XHR yang naif memberi Anda data Anda, dan Anda bootstrap secara bersudut dalam panggilan baliknya, yang berkaitan dengan masalah async Anda. Dalam contoh di bawah ini, Anda bahkan tidak perlu membuat variabel global. Data yang dikembalikan hanya ada dalam lingkup sudut sebagai injeksi, dan bahkan tidak ada di dalam pengontrol, layanan, dll. Kecuali Anda menyuntikkannya. (Seperti halnya Anda akan menyuntikkan output
resolve
objek Anda ke controller untuk tampilan yang dirutekan.) Jika Anda lebih memilih untuk kemudian berinteraksi dengan data itu sebagai layanan, Anda dapat membuat layanan, menyuntikkan data, dan tidak ada yang akan lebih bijaksana .Contoh:
Sekarang,
NavData
konstanta Anda ada. Teruskan dan menyuntikkannya ke pengontrol atau layanan:Tentu saja, menggunakan objek XHR telanjang menghapus sejumlah kebaikan yang akan
$http
atau JQuery rawat untuk Anda, tetapi contoh ini berfungsi tanpa ketergantungan khusus, setidaknya untuk yang sederhanaget
. Jika Anda ingin sedikit lebih banyak kekuatan untuk permintaan Anda, muat perpustakaan eksternal untuk membantu Anda. Tapi saya tidak berpikir itu mungkin untuk mengakses alat sudut$http
atau lainnya dalam konteks ini.(SO terkait posting )
sumber
Yang dapat Anda lakukan adalah di .config Anda untuk aplikasi ini adalah membuat objek tekad untuk rute dan dalam fungsi lulus dalam $ q (objek janji) dan nama layanan yang Anda andalkan, dan selesaikan janji di fungsi callback untuk $ http dalam layanan seperti:
CONFIG ROUTE
Angular tidak akan merender template atau membuat controller tersedia sampai defer.resolve () dipanggil. Kami dapat melakukannya dalam layanan kami:
LAYANAN
Sekarang MyService memiliki data yang ditetapkan untuk properti datanya, dan janji dalam objek resolusinya telah diselesaikan, pengontrol kami untuk rute tersebut menjadi nyata, dan kami dapat menetapkan data dari layanan ke objek pengontrol kami.
CONTROLLER
Sekarang semua pengikatan kami dalam lingkup controller akan dapat menggunakan data yang berasal dari MyService.
sumber
resolve
objek dengan properti menjadi fungsinya. jadi akhirnya menjadiresolve:{ dataFetch: function(){ // call function here } }
Jadi saya menemukan solusinya. Saya membuat layanan angularJS, kami akan menyebutnya MyDataRepository dan saya membuat modul untuk itu. Saya kemudian menyajikan file javascript ini dari pengontrol sisi server saya:
HTML:
Sisi server:
Saya kemudian dapat menyuntikkan MyDataRepository di mana pun saya membutuhkannya:
Ini bekerja dengan baik untuk saya, tetapi saya terbuka untuk umpan balik jika ada yang punya. }
sumber
Selain itu, Anda dapat menggunakan teknik berikut untuk menyediakan layanan Anda secara global, sebelum pengendali yang sebenarnya dijalankan: https://stackoverflow.com/a/27050497/1056679 .
run
Misalnya , atasi data Anda secara global dan berikan ke layanan Anda di blok misalnya.sumber
Anda dapat menggunakan
JSONP
untuk memuat data layanan secara tidak sinkron. Permintaan JSONP akan dilakukan selama pemuatan halaman awal dan hasilnya akan tersedia sebelum aplikasi Anda dimulai. Dengan cara ini Anda tidak perlu mengasapi perutean Anda dengan resolusi berlebihan.Anda html akan terlihat seperti ini:
sumber
Cara termudah untuk mengambil inisialisasi direktori use ng-init.
Cukup masukkan lingkup div ng-init di mana Anda ingin mengambil data init
index.html
index.js
sumber