Yang $scope
Anda lihat disuntikkan ke pengontrol bukanlah layanan (seperti bagian lain yang dapat diinjeksi), tetapi merupakan objek Scope. Banyak objek lingkup dapat dibuat (biasanya secara prototipikal mewarisi dari lingkup induk). Akar dari semua cakupan adalah $rootScope
dan Anda dapat membuat cakupan anak baru menggunakan $new()
metode cakupan apa pun (termasuk $rootScope
).
Tujuan dari Scope adalah untuk "merekatkan" presentasi dan logika bisnis aplikasi Anda. Tidak masuk akal untuk memasukkan a $scope
ke dalam sebuah layanan.
Layanan adalah objek tunggal yang digunakan (antara lain) untuk berbagi data (misalnya di antara beberapa pengontrol) dan umumnya merangkum potongan kode yang dapat digunakan kembali (karena mereka dapat dimasukkan dan menawarkan "layanan" mereka di bagian mana pun dari aplikasi Anda yang membutuhkannya: pengontrol, arahan, filter, layanan lain, dll.).
Saya yakin, berbagai pendekatan akan berhasil untuk Anda. Salah satunya adalah:
Karena StudentService
bertugas menangani data siswa, Anda dapat StudentService
menyimpan sejumlah siswa dan membiarkannya "membagikan" dengan siapa pun yang mungkin tertarik (misalnya Anda $scope
). Ini bahkan lebih masuk akal, jika ada tampilan / pengontrol / filter / layanan lain yang perlu memiliki akses ke info itu (jika tidak ada saat ini, jangan kaget jika mereka segera mulai bermunculan).
Setiap kali siswa baru ditambahkan (menggunakan metode layanan save()
), larik siswa layanan itu sendiri akan diperbarui dan setiap objek lain yang berbagi larik itu akan diperbarui secara otomatis juga.
Berdasarkan pendekatan yang dijelaskan di atas, kode Anda akan terlihat seperti ini:
angular.
module('cfd', []).
factory('StudentService', ['$http', '$q', function ($http, $q) {
var path = 'data/people/students.json';
var students = [];
// In the real app, instead of just updating the students array
// (which will be probably already done from the controller)
// this method should send the student data to the server and
// wait for a response.
// This method returns a promise to emulate what would happen
// when actually communicating with the server.
var save = function (student) {
if (student.id === null) {
students.push(student);
} else {
for (var i = 0; i < students.length; i++) {
if (students[i].id === student.id) {
students[i] = student;
break;
}
}
}
return $q.resolve(student);
};
// Populate the students array with students from the server.
$http.get(path).then(function (response) {
response.data.forEach(function (student) {
students.push(student);
});
});
return {
students: students,
save: save
};
}]).
controller('someCtrl', ['$scope', 'StudentService',
function ($scope, StudentService) {
$scope.students = StudentService.students;
$scope.saveStudent = function (student) {
// Do some $scope-specific stuff...
// Do the actual saving using the StudentService.
// Once the operation is completed, the $scope's `students`
// array will be automatically updated, since it references
// the StudentService's `students` array.
StudentService.save(student).then(function () {
// Do some more $scope-specific stuff,
// e.g. show a notification.
}, function (err) {
// Handle the error.
});
};
}
]);
Satu hal yang harus Anda perhatikan saat menggunakan pendekatan ini adalah jangan pernah menetapkan ulang larik layanan, karena komponen lain (misalnya cakupan) akan tetap merujuk ke larik asli dan aplikasi Anda akan rusak.
Misalnya untuk menghapus array di StudentService
:
/* DON'T DO THAT */
var clear = function () { students = []; }
/* DO THIS INSTEAD */
var clear = function () { students.splice(0, students.length); }
Lihat juga, demo singkat ini .
UPDATE KECIL:
Beberapa kata untuk menghindari kebingungan yang mungkin timbul saat berbicara tentang menggunakan layanan, tetapi tidak membuatnya dengan service()
fungsinya.
Mengutip dokumen tentang$provide
:
Sebuah sudut layanan adalah objek tunggal yang diciptakan oleh pabrik layanan . Pabrik layanan ini adalah fungsi yang, pada gilirannya, dibuat oleh penyedia layanan . The penyedia layanan adalah fungsi konstruktor. Ketika dibuat, mereka harus berisi properti yang dipanggil $get
, yang memegang fungsi pabrik layanan .
[...]
... $provide
layanan memiliki metode pembantu tambahan untuk mendaftarkan layanan tanpa menentukan penyedia:
- provider (provider) - mendaftarkan penyedia layanan dengan $ injector
- konstan (obj) - mendaftarkan nilai / objek yang dapat diakses oleh penyedia dan layanan.
- nilai (obj) - mendaftarkan nilai / objek yang hanya dapat diakses oleh layanan, bukan penyedia.
- factory (fn) - mendaftarkan fungsi pabrik layanan, fn, yang akan dibungkus dalam objek penyedia layanan, yang properti $ getnya akan berisi fungsi pabrik yang diberikan.
- service (class) - meregistrasikan fungsi konstruktor, kelas yang akan dibungkus dalam objek penyedia layanan, yang properti $ getnya akan membuat instance objek baru menggunakan fungsi konstruktor yang diberikan.
Pada dasarnya, apa yang dikatakan adalah bahwa setiap layanan Angular terdaftar menggunakan $provide.provider()
, tetapi ada metode "pintasan" untuk layanan yang lebih sederhana (dua di antaranya adalah service()
dan factory()
).
Semuanya "bermuara" pada layanan, jadi tidak ada bedanya metode mana yang Anda gunakan (selama persyaratan untuk layanan Anda dapat dicakup oleh metode itu).
BTW, provider
vs service
vs factory
adalah salah satu konsep yang paling membingungkan untuk pendatang baru Angular, tetapi untungnya ada banyak sumber daya (di SO) untuk mempermudah. (Cari saja.)
(Saya harap itu menyelesaikannya - beri tahu saya jika tidak.)
service
ataufactory
- Anda akan mengakhiri Anda dengan dan layanan Angular . Pastikan Anda memahami cara kerja masing-masing dan apakah itu sesuai dengan kebutuhan Anda.$scope.students
akan kosong, jika panggilan ajax belum selesai? Atau$scope.students
akan terisi sebagian, jika blok kode ini sedang bekerja?students.push(student);
Daripada mencoba mengubah
$scope
dalam layanan, Anda dapat mengimplementasikan a$watch
dalam pengontrol Anda untuk mengawasi properti di layanan Anda untuk perubahan dan kemudian memperbarui properti di$scope
. Berikut adalah contoh yang dapat Anda coba di pengontrol:Satu hal yang perlu diperhatikan adalah bahwa dalam layanan Anda, agar
students
properti dapat terlihat, properti tersebut harus berada di objek Service atauthis
semacamnya:sumber
Nah (yang panjang) ... jika Anda bersikeras untuk memiliki
$scope
akses di dalam layanan, Anda dapat:Buat layanan pengambil / penyetel
Masukkan dan simpan lingkup pengontrol di dalamnya
Sekarang, dapatkan cakupan di dalam layanan lain
sumber
Layanan adalah lajang, dan tidak logis untuk cakupan yang akan dimasukkan dalam layanan (yang memang benar, Anda tidak dapat memasukkan cakupan dalam layanan). Anda dapat meneruskan cakupan sebagai parameter, tetapi itu juga merupakan pilihan desain yang buruk, karena Anda akan memiliki cakupan yang sedang diedit di banyak tempat, sehingga menyulitkan proses debug. Kode untuk menangani variabel lingkup harus masuk ke pengontrol, dan panggilan layanan masuk ke layanan.
sumber
Anda dapat membuat layanan Anda sama sekali tidak mengetahui cakupan, tetapi di pengontrol Anda mengizinkan cakupan untuk diperbarui secara asinkron.
Masalah yang Anda hadapi adalah karena Anda tidak menyadari bahwa panggilan http dibuat secara asinkron, yang berarti Anda tidak mendapatkan nilai secepat mungkin. Misalnya,
Ada cara sederhana untuk menyiasatinya dan itu dengan menyediakan fungsi callback.
Formulir:
Ini menghapus beberapa logika bisnis Anda karena singkatnya dan saya belum benar-benar menguji kodenya, tetapi sesuatu seperti ini akan berhasil. Konsep utamanya adalah meneruskan callback dari pengontrol ke layanan yang akan dipanggil nanti. Jika Anda terbiasa dengan NodeJS, ini adalah konsep yang sama.
sumber
.then
Metode Janji merupakan Anti-Pola .Masuk ke dalam kesulitan yang sama. Saya berakhir dengan yang berikut ini. Jadi di sini saya tidak menyuntikkan objek scope ke pabrik, tetapi menyetel $ scope di controller itu sendiri menggunakan konsep promise yang dikembalikan oleh layanan $ http .
sumber
Kode untuk menangani variabel lingkup harus masuk ke kontroler, dan panggilan layanan masuk ke layanan.
Anda dapat menyuntikkan
$rootScope
untuk tujuan penggunaan$rootScope.$broadcast
dan$rootScope.$on
.Jika tidak, hindari menyuntikkan
$rootScope
. Lihat$rootScope
ada, tetapi bisa digunakan untuk kejahatan .sumber