Untuk memahami sepenuhnya masalah dan solusi yang mungkin, kita perlu membahas deteksi perubahan sudut - untuk pipa dan komponen.
Deteksi Perubahan Pipa
Pipa tanpa negara / murni
Secara default, pipa tidak memiliki kewarganegaraan / murni. Pipa tanpa status / murni hanya mengubah data masukan menjadi data keluaran. Mereka tidak mengingat apa pun, jadi mereka tidak memiliki properti apa pun - hanya sebuah transform()
metode. Oleh karena itu Angular dapat mengoptimalkan penanganan pipa stateless / murni: jika inputnya tidak berubah, pipa tidak perlu dijalankan selama siklus deteksi perubahan. Untuk pipa seperti {{power | exponentialStrength: factor}}
, power
dan factor
adalah masukan.
Untuk pertanyaan ini "#student of students | sortByName:queryElem.value"
,, students
dan queryElem.value
adalah masukan, dan pipa sortByName
adalah tanpa kewarganegaraan / murni. students
adalah larik (referensi).
- Ketika seorang siswa ditambahkan, referensi array tidak berubah -
students
tidak berubah - maka pipa stateless / pure tidak dijalankan.
- Ketika sesuatu diketik ke dalam input filter,
queryElem.value
berubah, maka pipa stateless / pure dijalankan.
Salah satu cara untuk memperbaiki masalah larik adalah dengan mengubah referensi larik setiap kali seorang siswa ditambahkan - yaitu, membuat larik baru setiap kali seorang siswa ditambahkan. Kami dapat melakukan ini dengan concat()
:
this.students = this.students.concat([{name: studentName}]);
Meskipun ini berhasil, addNewStudent()
metode kami tidak harus diimplementasikan dengan cara tertentu hanya karena kami menggunakan pipa. Kami ingin menggunakan push()
untuk menambah array kami.
Pipa Stateful
Pipa berstatus memiliki status - pipa tersebut biasanya memiliki properti, bukan hanya transform()
metode. Mereka mungkin perlu dievaluasi bahkan jika masukan mereka tidak berubah. Ketika kita menentukan bahwa pipa adalah stateful / non-pure - pure: false
- maka setiap kali sistem deteksi perubahan Angular memeriksa komponen untuk perubahan dan komponen itu menggunakan pipa stateful, itu akan memeriksa output pipa, apakah inputnya berubah atau tidak.
Ini terdengar seperti yang kami inginkan, meskipun kurang efisien, karena kami ingin pipa dieksekusi meskipun students
referensinya tidak berubah. Jika kita membuat pipa menjadi stateful, kita mendapatkan error:
EXCEPTION: Expression 'students | sortByName:queryElem.value in HelloWorld@7:6'
has changed after it was checked. Previous value: '[object Object],[object Object]'.
Current value: '[object Object],[object Object]' in [students | sortByName:queryElem.value
Menurut jawaban @drewmoore , "kesalahan ini hanya terjadi dalam mode pengembang (yang diaktifkan secara default pada beta-0). Jika Anda memanggil enableProdMode()
saat melakukan bootstrap aplikasi, kesalahan tidak akan muncul." The docs untukApplicationRef.tick()
negara:
Dalam mode pengembangan, tick () juga menjalankan siklus deteksi perubahan kedua untuk memastikan bahwa tidak ada perubahan lebih lanjut yang terdeteksi. Jika perubahan tambahan terjadi selama siklus kedua ini, binding di aplikasi memiliki efek samping yang tidak dapat diselesaikan dalam satu kali proses deteksi perubahan. Dalam hal ini, Angular melontarkan kesalahan, karena aplikasi Angular hanya dapat memiliki satu deteksi perubahan yang selama itu semua deteksi perubahan harus diselesaikan.
Dalam skenario kami, saya yakin kesalahan itu palsu / menyesatkan. Kami memiliki pipa stateful, dan hasilnya dapat berubah setiap kali dipanggil - ini dapat memiliki efek samping dan tidak apa-apa. NgFor dievaluasi setelah pipa, jadi seharusnya berfungsi dengan baik.
Namun, kami tidak dapat benar-benar mengembangkan dengan kesalahan ini dilemparkan, jadi salah satu solusinya adalah menambahkan properti array (yaitu, status) ke implementasi pipa dan selalu mengembalikan array itu. Lihat jawaban @ pixelbits untuk solusi ini.
Namun, kita bisa menjadi lebih efisien, dan seperti yang akan kita lihat, kita tidak memerlukan properti array dalam implementasi pipa, dan kita tidak memerlukan solusi untuk deteksi perubahan ganda.
Deteksi Perubahan Komponen
Secara default, pada setiap acara browser, deteksi perubahan Angular melewati setiap komponen untuk melihat apakah itu berubah - input dan template (dan mungkin hal-hal lain?) Dicentang.
Jika kita mengetahui bahwa sebuah komponen hanya bergantung pada properti masukannya (dan kejadian template), dan bahwa properti masukan tidak dapat diubah, kita dapat menggunakan onPush
strategi deteksi perubahan yang jauh lebih efisien . Dengan strategi ini, alih-alih memeriksa setiap peristiwa browser, sebuah komponen hanya diperiksa saat input berubah dan saat peristiwa template dipicu. Dan, tampaknya, kami tidak mendapatkan Expression ... has changed after it was checked
kesalahan itu dengan pengaturan ini. Ini karena sebuah onPush
komponen tidak diperiksa lagi sampai "ditandai" ( ChangeDetectorRef.markForCheck()
) lagi. Jadi binding Template dan keluaran pipa stateful dijalankan / dievaluasi hanya sekali. Pipa stateless / murni masih tidak dieksekusi kecuali inputnya berubah. Jadi kami masih membutuhkan pipa stateful di sini.
Ini adalah solusi yang disarankan @EricMartinez: pipa stateful dengan onPush
deteksi perubahan. Lihat jawaban @ caffinatedmonkey untuk solusi ini.
Perhatikan bahwa dengan solusi ini, transform()
metode tidak perlu mengembalikan larik yang sama setiap kali. Saya merasa agak aneh: pipa stateful tanpa state. Berpikir tentang itu lagi ... pipa stateful mungkin harus selalu mengembalikan array yang sama. Jika tidak, ini hanya dapat digunakan dengan onPush
komponen dalam mode pengembang.
Jadi setelah semua itu, saya rasa saya suka kombinasi jawaban @ Eric dan @ pixelbits: pipa stateful yang mengembalikan referensi array yang sama, dengan onPush
deteksi perubahan jika komponen memungkinkan. Karena pipa stateful mengembalikan referensi larik yang sama, pipa tersebut masih dapat digunakan dengan komponen yang tidak dikonfigurasi dengan onPush
.
Plunker
Ini mungkin akan menjadi idiom Angular 2: jika array memberi makan pipa, dan array mungkin berubah (item dalam array itu, bukan referensi array), kita perlu menggunakan pipa stateful.
pure:false
Pipa Anda, danchangeDetection: ChangeDetectionStrategy.OnPush
Komponen Anda.