Teropong isolasi direktif dengan ng-repeat scope di AngularJS

95

Saya memiliki arahan dengan isolate-scope (sehingga saya dapat menggunakan kembali arahan di tempat lain), dan ketika saya menggunakan arahan ini dengan ng-repeat, itu gagal berfungsi.

Saya telah membaca semua dokumentasi dan jawaban Stack Overflow tentang topik ini dan memahami masalahnya. Saya yakin saya telah menghindari semua tipu muslihat biasa.

Jadi saya mengerti bahwa kode saya gagal karena cakupan yang dibuat oleh ng-repeatdirektif. Direktif saya sendiri membuat lingkup-isolasi dan melakukan pengikatan data dua arah ke objek dalam lingkup induk. Direktif saya akan menetapkan nilai objek baru ke variabel terikat ini dan ini berfungsi dengan sempurna ketika direktif saya digunakan tanpa ng-repeat(variabel induk diperbarui dengan benar). Namun, dengan ng-repeat, tugas membuat variabel baru dalam ng-repeatcakupan dan variabel induk tidak melihat perubahannya. Semua ini seperti yang diharapkan berdasarkan apa yang telah saya baca.

Saya juga membaca bahwa ketika ada beberapa arahan pada elemen tertentu, hanya satu ruang lingkup yang dibuat. Dan bahwa a prioritydapat disetel di setiap arahan untuk menentukan urutan penerapan arahan; direktif diurutkan berdasarkan prioritas dan kemudian fungsi kompilasinya dipanggil (cari kata prioritas di http://docs.angularjs.org/guide/directive ).

Jadi saya berharap saya dapat menggunakan prioritas untuk memastikan bahwa arahan saya berjalan terlebih dahulu dan akhirnya membuat lingkup-isolasi, dan ketika ng-repeatdijalankan, itu menggunakan kembali lingkup-isolasi daripada membuat lingkup yang secara prototipikal mewarisi dari lingkup induk. The ng-repeatnegara dokumentasi yang yang berjalan direktif pada tingkat prioritas 1000. Tidak jelas apakah 1itu tingkat prioritas yang lebih tinggi atau tingkat prioritas yang lebih rendah. Ketika saya menggunakan tingkat prioritas 1dalam arahan saya, tidak ada bedanya, jadi saya mencoba 2000. Tapi itu memperburuk keadaan: ikatan dua arah undefinedsaya menjadi dan arahan saya tidak menampilkan apa pun.

Saya telah membuat biola untuk menunjukkan masalah saya . Saya telah mengomentari prioritypengaturan dalam arahan saya. Saya memiliki daftar objek nama dan direktif disebut name-rowyang menunjukkan bidang nama depan dan belakang di objek nama. Ketika nama yang ditampilkan diklik, saya ingin mengatur selectedvariabel dalam lingkup utama. Larik nama, selectedvariabel diteruskan ke name-rowdirektif menggunakan data-binding dua arah.

Saya tahu bagaimana membuat ini bekerja dengan memanggil fungsi di lingkup utama. Saya juga tahu bahwa jika selectedada di dalam objek lain, dan saya mengikat objek luar, semuanya akan bekerja. Tetapi saya tidak tertarik dengan solusi tersebut saat ini.

Sebaliknya, pertanyaan yang saya miliki adalah:

  • Bagaimana cara mencegah ng-repeatpembuatan cakupan yang secara prototipikal mewarisi dari lingkup induk, dan malah membuatnya menggunakan lingkup-isolasi direktif saya?
  • Mengapa tingkat prioritas 2000dalam arahan saya tidak berfungsi?
  • Dengan Batarang, apakah mungkin untuk mengetahui jenis scope yang digunakan?
Deepak Nulu
sumber
1
Biasanya, Anda tidak ingin menggunakan cakupan isolate jika direktif Anda akan digunakan pada elemen yang sama dengan arahan lainnya. Karena Anda membuat properti cakupan Anda sendiri, dan Anda perlu bekerja dengan ng-repeat, saya sarankan menggunakan scope: trueuntuk direktif Anda. Lihat juga (jika Anda belum melakukannya) stackoverflow.com/questions/14914213/… Juga, hanya karena sebuah direktif akan digunakan di banyak tempat tidak berarti kita harus secara otomatis menggunakan lingkup isolasi.
Mark Rajcok
Saya telah membaca banyak jawaban Anda (sangat luar biasa, terima kasih telah menulisnya), tetapi tidak pernah terpikir oleh saya untuk membaca pertanyaan Anda :-). Saya membaca apa yang Anda tautkan. Tampak bagi saya bahwa arahan isolate-scope tidak dapat dicampur dengan arahan lain. Saya setuju dengan sentimen bahwa arahan tersebut adalah komponen dan oleh karena itu tidak perlu dicampur dengan arahan lain. Satu-satunya pengecualian (sejauh ini) bagi saya adalah ng-repeat. Menurut saya, sangat berharga untuk dapat menggabungkan arahan yang berdiri sendiri ng-repeat. Untuk dilanjutkan ...
Deepak Nulu
Lanjutan dari atas ... Jadi jika hanya ada satu direktif dengan ruang lingkup untuk suatu elemen, maka ng-repeattidak boleh memiliki ruang lingkup. ng-repeatmemiliki ruang lingkup masuk akal untuk kasus penggunaan biasa, jadi saya tidak menyarankan itu diubah. Sebagai gantinya, seperti yang saya komentari dalam jawaban Alex Osborn, saya pikir saya akan membuat arahan berulang berdasarkan ng-repeatyang tidak menciptakan ruang lingkupnya sendiri. Ini kemudian dapat digunakan untuk mengulangi arahan yang memiliki cakupan isolatnya sendiri. Untuk dilanjutkan ...
Deepak Nulu
Kode yang mengulang perintah sekarang perlu mengetahui apakah akan menggunakan ng-repeatatau direktif berulang tanpa cakupan kustom. Saya pikir tidak apa-apa bagi "penelepon" untuk mengetahui hal ini, tetapi tidak baik bagi "callee" (perintah yang diulangi) untuk mengetahui apakah itu diulangi atau tidak. Untuk dilanjutkan ...
Deepak Nulu
Menjadi sedikit gila dengan komentar di sini ... :-) ngRepeat harus membuat ruang lingkupnya sendiri. Mengapa Anda merasa membutuhkan ruang lingkup isolasi di sini?
Josh David Miller

Jawaban:

203

Oke, melalui banyak komentar di atas, saya menemukan kebingungannya. Pertama, beberapa poin klarifikasi:

  • ngRepeat tidak memengaruhi cakupan isolasi yang Anda pilih
  • parameter yang dikirimkan ke ngRepeat untuk digunakan pada atribut direktif Anda jangan menggunakan lingkup prototypically-mewarisi
  • alasan direktif Anda tidak berfungsi tidak ada hubungannya dengan lingkup isolate

Berikut adalah contoh dari kode yang sama tetapi dengan perintah yang dihapus:

<li ng-repeat="name in names"
    ng-class="{ active: $index == selected }"
    ng-click="selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

Berikut adalah JSFiddle yang menunjukkan bahwa itu tidak akan berhasil. Anda mendapatkan hasil yang sama persis seperti dalam arahan Anda.

Mengapa tidak berhasil? Karena cakupan di AngularJS menggunakan pewarisan prototipe. Nilai selectedpada lingkup induk Anda adalah primitif . Dalam JavaScript, ini berarti akan ditimpa ketika seorang anak menetapkan nilai yang sama. Ada aturan emas dalam cakupan AngularJS: nilai model harus selalu ada .di dalamnya. Artinya, mereka tidak boleh primitif. Lihat jawaban SO ini untuk informasi lebih lanjut.


Berikut adalah gambaran dari tampilan awal scope.

masukkan deskripsi gambar di sini

Setelah mengklik item pertama, cakupannya sekarang akan terlihat seperti ini:

masukkan deskripsi gambar di sini

Perhatikan bahwa selectedproperti baru telah dibuat pada cakupan ngRepeat. Cakupan pengontrol 003 tidak diubah.

Anda mungkin bisa menebak apa yang terjadi saat kita mengklik item kedua:

masukkan deskripsi gambar di sini


Jadi masalah Anda sebenarnya tidak disebabkan oleh ngRepeat sama sekali - ini disebabkan oleh pelanggaran aturan emas di AngularJS. Cara untuk memperbaikinya adalah dengan menggunakan properti objek:

$scope.state = { selected: undefined };
<li ng-repeat="name in names"
    ng-class="{ active: $index == state.selected }"
    ng-click="state.selected = $index">
    {{$index}}: {{name.first}} {{name.last}}
</li>

Ini adalah JSFiddle kedua yang menunjukkan bahwa ini juga berfungsi.

Berikut adalah tampilan cakupan awalnya:

masukkan deskripsi gambar di sini

Setelah mengklik item pertama:

masukkan deskripsi gambar di sini

Di sini, lingkup pengontrol terpengaruh, seperti yang diinginkan.

Juga, untuk membuktikan bahwa ini masih akan bekerja dengan direktif Anda dengan lingkup terisolasi (karena, sekali lagi, ini tidak ada hubungannya dengan masalah Anda), berikut adalah JSFiddle untuk itu juga, tampilan harus mencerminkan objek. Anda akan mencatat bahwa satu-satunya perubahan yang diperlukan adalah menggunakan objek, bukan primitif .

Cakupan awalnya:

masukkan deskripsi gambar di sini

Cakupan setelah mengklik item pertama:

masukkan deskripsi gambar di sini

Sebagai kesimpulan: sekali lagi, masalah Anda bukan pada lingkup isolate dan bukan pada cara kerja ngRepeat. Masalah Anda adalah bahwa Anda melanggar aturan yang diketahui menyebabkan masalah ini. Model di AngularJS harus selalu memiliki ..

Josh David Miller
sumber
Eksperimen saya dengan arahan dengan cakupan isolasi dan pengikatan data 2 arah membuat saya percaya bahwa .aturan emas hanya diperlukan untuk cakupan yang memiliki warisan prototipe. Dengan isolate-scopes, variabel yang Anda minati didefinisikan dalam scope: {}definisi di direktif, dan oleh karena itu variabel tersebut akan ada di lingkup isolate dari awal. Juga, mereka tidak menutupi variabel induk karena lingkup-isolate tidak secara prototipikal mewarisi dari lingkup induk. yaitu tidak ada ruang lingkup "induk" untuk ditutup.
Deepak Nulu
1
Seharusnya juga mengatakan: direktif Anda tidak membuat lingkup anak, tetapi dapat dengan mudah digunakan dalam konteks yang membutuhkan lingkup anak. ngRepeat adalah salah satu kasus. Begitu juga dengan transklusi. Percayalah - gunakan ..
Josh David Miller
1
Diskusi di Grup Google AngularJS tempat @JoshDavidMiller akhirnya bisa menghilangkan kebingungan saya!
Deepak Nulu
2
Dari mana kalian mendapatkan semua diagram keren ini? Saya telah melihat pengguna lain memposting ini juga.
Asad Saeeduddin
3
@Asad, saya baru saja memasang alat di GitHub, ini disebut Peri $ scope .
Mark Rajcok
6

Tanpa secara langsung mencoba menghindari menjawab pertanyaan Anda, sebagai gantinya lihatlah biola berikut:

http://jsfiddle.net/dVPLM/

Poin kuncinya adalah bahwa alih-alih mencoba melawan dan mengubah perilaku konvensional Angular, Anda dapat menyusun arahan Anda untuk bekerja ng-repeatdaripada mencoba menimpanya.

Di template Anda:

    <name-row 
        in-names-list="names"
        io-selected="selected">
    </name-row>

Dalam arahan Anda:

    template:
'        <ul>' +      
'            <li ng-repeat="name in inNamesList" ng-class="activeClass($index)" >' +
'                <a ng-click="setSelected($index)">' +
'                    {{$index}} - {{name.first}} {{name.last}}' +
'                </a>' +
'            </li>' +
'        </ul>'

Menanggapi pertanyaan Anda:

  • ng-repeat akan membuat ruang lingkup, Anda seharusnya tidak mencoba mengubah ini.
  • Prioritas dalam direktif bukan hanya urutan eksekusi - lihat: AngularJS: Bagaimana kompilator HTML mengatur urutan kompilasi?
  • Di Batarang, jika Anda memeriksa tab kinerja, Anda dapat melihat ekspresi terikat untuk setiap cakupan, dan memeriksa apakah ini sesuai dengan harapan Anda.
Alex Osborn
sumber
Terima kasih atas tanggapan yang cepat. Jika apa yang saya coba bertentangan dengan arus, saya agak sedih (AngularJS tetap merupakan kerangka kerja yang luar biasa). Arahan dimaksudkan untuk menjadi komponen yang dapat digunakan kembali. Saat ditulis, penulis tidak perlu khawatir apakah akan digunakan dengan an ng-repeatatau tidak. Mungkin saat pertama kali ditulis, tidak pernah digunakan dengan ng-repeat. Dan suatu saat di masa mendatang, ini mungkin digunakan dengan ng-repeat, dan pada saat itu, seharusnya berfungsi tanpa penulisan ulang. Semoga rilis AngularJS di masa mendatang akan memungkinkan hal ini.
Deepak Nulu
Saya ingin mengklarifikasi komentar saya di atas. Saya pikir adalah mungkin untuk menulis arahan yang tidak mengkhawatirkan apakah itu digunakan dengan ng-repeatatau tidak. Tetapi tampaknya saya harus meneruskan fungsi ke direktif sehingga dapat memodifikasi variabel dalam lingkup induk, alih-alih dapat mengubah pengikatan dua arah dalam lingkup direktif itu sendiri. Pengikatan dua arah dan arahan yang dapat digunakan kembali adalah dua hal favorit saya tentang AngularJS dan ng-repeatterbukti menjadi lalat dalam salep. Mungkin saya bisa menulis ng-repeatpadanan yang tidak membuat ruang lingkupnya sendiri.
Deepak Nulu