Dalam skenario ini, saya menampilkan daftar siswa (larik) ke tampilan dengan ngFor
:
<li *ngFor="#student of students">{{student.name}}</li>
Sangat menyenangkan bahwa itu diperbarui setiap kali saya menambahkan siswa lain ke daftar.
Namun, ketika saya memberikan pipe
kepada filter
dengan nama mahasiswa,
<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
Itu tidak memperbarui daftar sampai saya mengetik sesuatu di bidang pemfilteran nama siswa.
Ini tautan ke plnkr .
Hello_world.html
<h1>Students:</h1>
<label for="newStudentName"></label>
<input type="text" name="newStudentName" placeholder="newStudentName" #newStudentElem>
<button (click)="addNewStudent(newStudentElem.value)">Add New Student</button>
<br>
<input type="text" placeholder="Search" #queryElem (keyup)="0">
<ul>
<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
</ul>
sortir_by_name_pipe.ts
import {Pipe} from 'angular2/core';
@Pipe({
name: 'sortByName'
})
export class SortByNamePipe {
transform(value, [queryString]) {
// console.log(value, queryString);
return value.filter((student) => new RegExp(queryString).test(student.name))
// return value;
}
}
pure:false
Pipa Anda, danchangeDetection: ChangeDetectionStrategy.OnPush
Komponen Anda..test()
fungsi filter Anda. Itu karena, jika pengguna memasukkan string yang menyertakan karakter arti khusus seperti:*
atau+
dll, kode Anda akan rusak. Saya pikir Anda harus menggunakan.includes()
atau keluar dari string kueri dengan fungsi khusus.pure:false
dan membuat pipa Anda stateful akan memperbaiki masalah tersebut. Mengubah ChangeDetectionStrategy tidak diperlukan.Jawaban:
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
danfactor
adalah masukan.Untuk pertanyaan ini
"#student of students | sortByName:queryElem.value"
,,students
danqueryElem.value
adalah masukan, dan pipasortByName
adalah tanpa kewarganegaraan / murni.students
adalah larik (referensi).students
tidak berubah - maka pipa stateless / pure tidak dijalankan.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()
:Meskipun ini berhasil,
addNewStudent()
metode kami tidak harus diimplementasikan dengan cara tertentu hanya karena kami menggunakan pipa. Kami ingin menggunakanpush()
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: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 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 mendapatkanExpression ... has changed after it was checked
kesalahan itu dengan pengaturan ini. Ini karena sebuahonPush
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 denganonPush
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 denganonPush
.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.
sumber
onPush
deteksi perubahan plnkr.co/edit/gRl0Pt9oBO6038kCXZPk?p=previewSeperti yang ditunjukkan Eric Martinez di komentar, menambahkan
pure: false
kePipe
dekorator Anda dan dekoratorchangeDetection: ChangeDetectionStrategy.OnPush
AndaComponent
akan memperbaiki masalah Anda. Berikut adalah plunkr yang berfungsi. Mengganti keChangeDetectionStrategy.Always
, juga berfungsi. Inilah alasannya.Menurut panduan angular2 pada pipa :
Adapun
ChangeDetectionStrategy
, secara default, semua binding diperiksa setiap siklus. Ketikapure: false
pipa ditambahkan, saya yakin metode deteksi perubahan berubah dariCheckAlways
menjadiCheckOnce
karena alasan kinerja. DenganOnPush
, binding untuk Komponen hanya diperiksa saat properti input berubah atau saat peristiwa dipicu. Untuk informasi lebih lanjut tentang detektor perubahan, bagian penting dariangular2
, lihat tautan berikut:sumber
pure: false
pipa ditambahkan, saya yakin metode deteksi perubahan berubah dari CheckAlways ke CheckOnce" - yang tidak sesuai dengan apa yang Anda kutip dari dokumen. Pemahaman saya adalah sebagai berikut: input pipa stateless diperiksa setiap siklus secara default. Jika ada perubahan, metode pipatransform()
dipanggil untuk (kembali) menghasilkan output. Metode pipa statefultransform()
dipanggil setiap siklus, dan keluarannya diperiksa untuk perubahan. Lihat juga stackoverflow.com/a/34477111/215945 .OnPush
(yaitu, komponen ditandai sebagai "tidak dapat diubah"), keluaran pipa yang stateful tidak harus "distabilkan" - yaitu, tampaknyatransform()
metode dijalankan hanya sekali (mungkin semua deteksi perubahan untuk komponen dijalankan hanya sekali). Bandingkan itu dengan jawaban @ pixelbits, di manatransform()
metode ini dipanggil beberapa kali, dan itu harus stabil (menurut jawaban lain dari pixelbits ), maka perlu menggunakan referensi array yang sama.transform
hanya dipanggil saat data baru didorong alih-alih beberapa kali hingga keluaran stabil, bukan?Demo Plunkr
Anda tidak perlu mengubah ChangeDetectionStrategy. Menerapkan Pipe stateful sudah cukup untuk membuat semuanya berfungsi.
Ini adalah pipa stateful (tidak ada perubahan lain yang dilakukan):
sumber
Expression 'students | sortByName:queryElem.value in HelloWorld@7:6' has changed after it was checked. Previous value: '[object Object],[object Object],[object Object]'. Current value: '[object Object],[object Object],[object Object]' in [students | sortByName:queryElem.value in HelloWorld@7:6]
Dari dokumentasi sudut
Pipa murni dan tidak murni
Ada dua kategori pipa: murni dan tidak murni. Pipa murni secara default. Setiap pipa yang Anda lihat sejauh ini murni. Anda membuat pipa tidak murni dengan menyetel benderanya yang murni ke false. Anda bisa membuat FlyingHeroesPipe tidak murni seperti ini:
@Pipe({ name: 'flyingHeroesImpure', pure: false })
Sebelum melakukan itu, pahami perbedaan antara murni dan tidak murni, dimulai dengan pipa murni.
Pipa murni Angular mengeksekusi pipa murni hanya jika mendeteksi perubahan murni ke nilai input. Perubahan murni adalah perubahan ke nilai input primitif (String, Angka, Boolean, Simbol) atau referensi objek yang diubah (Tanggal, Array, Fungsi, Objek).
Angular mengabaikan perubahan dalam objek (komposit). Ini tidak akan memanggil pipa murni jika Anda mengubah bulan masukan, menambah larik masukan, atau memperbarui properti objek masukan.
Ini mungkin tampak membatasi tetapi juga cepat. Pemeriksaan referensi objek cepat — jauh lebih cepat daripada pemeriksaan mendalam untuk perbedaan — jadi Angular dapat dengan cepat menentukan apakah ia dapat melewati eksekusi pipa dan pembaruan tampilan.
Untuk alasan ini, pipa murni lebih disukai bila Anda dapat menyesuaikan diri dengan strategi deteksi perubahan. Jika tidak bisa, Anda bisa menggunakan pipa yang tidak murni.
sumber
Daripada melakukan pure: false. Anda dapat menyalin dalam dan mengganti nilai dalam komponen dengan this.students = Object.assign ([], NEW_ARRAY); di mana NEW_ARRAY adalah larik yang dimodifikasi.
Ini berfungsi untuk sudut 6 dan harus bekerja untuk versi sudut lainnya juga.
sumber
Solusi: Impor Pipa secara manual dalam konstruktor dan panggil metode transformasi menggunakan pipa ini
Sebenarnya Anda bahkan tidak membutuhkan pipa
sumber
Tambahkan parameter ekstra ke pipa, dan ubah tepat setelah perubahan array, dan bahkan dengan pipa murni, daftar akan diperbarui
biarkan item item | pipa: param
sumber
Dalam kasus penggunaan ini saya menggunakan file Pipe in ts untuk pemfilteran data. Jauh lebih baik untuk kinerja, daripada menggunakan pipa murni. Gunakan di ts seperti ini:
sumber
untuk membuat pipa yang tidak murni mahal harganya. jadi jangan membuat pipa yang tidak murni, tetapi ubah referensi variabel data dengan membuat salinan data saat mengubah data dan menetapkan ulang referensi salinan dalam variabel data asli.
sumber