Saya memiliki elemen khusus:
<div formControlName="surveyType">
<div *ngFor="let type of surveyTypes"
(click)="onSelectType(type)"
[class.selected]="type === selectedType">
<md-icon>{{ type.icon }}</md-icon>
<span>{{ type.description }}</span>
</div>
</div>
Ketika saya mencoba menambahkan formControlName, saya mendapatkan pesan kesalahan:
Galat ERROR: Tidak ada accessor nilai untuk kontrol formulir dengan nama: 'surveyType'
Saya mencoba menambahkan ngDefaultControl
tanpa hasil. Sepertinya itu karena tidak ada input / pilih ... dan saya tidak tahu harus berbuat apa.
Saya ingin mengikat klik saya ke formControl ini agar ketika seseorang mengklik seluruh kartu yang akan mendorong 'ketik' saya ke formControl. Apa itu mungkin?
Jawaban:
Anda
formControlName
hanya dapat menggunakan arahan yang menerapkanControlValueAccessor
.Implementasikan antarmuka
Jadi, untuk melakukan apa yang Anda inginkan, Anda harus membuat komponen yang mengimplementasikan
ControlValueAccessor
, yang berarti menerapkan tiga fungsi berikut :writeValue
(memberi tahu Angular cara menulis nilai dari model ke tampilan)registerOnChange
(mendaftarkan fungsi penangan yang dipanggil saat tampilan berubah)registerOnTouched
(mendaftarkan pawang untuk dipanggil ketika komponen menerima acara sentuh, berguna untuk mengetahui apakah komponen telah difokuskan).Daftarkan penyedia
Kemudian, Anda harus memberi tahu Angular bahwa arahan ini adalah
ControlValueAccessor
(antarmuka tidak akan memotongnya karena dilucuti dari kode ketika TypeScript dikompilasi ke JavaScript). Anda melakukan ini dengan mendaftarkan penyedia .Penyedia harus menyediakan
NG_VALUE_ACCESSOR
dan menggunakan nilai yang ada . Anda juga perlu diforwardRef
sini. Catatan yangNG_VALUE_ACCESSOR
harus menjadi penyedia multi .Misalnya, jika arahan khusus Anda bernama MyControlComponent, Anda harus menambahkan sesuatu di sepanjang baris berikut di dalam objek yang diteruskan ke
@Component
dekorator:Pemakaian
Komponen Anda siap digunakan. Dengan formulir yang digerakkan templat ,
ngModel
penjilidan sekarang akan berfungsi dengan baik.Dengan formulir reaktif , Anda sekarang dapat menggunakan dengan benar
formControlName
dan kontrol formulir akan berperilaku seperti yang diharapkan.Sumber daya
sumber
Saya pikir Anda harus menggunakan
formControlName="surveyType"
padainput
dan bukan padadiv
sumber
<ng-content>
komponen pembungkus dan membiarkan komponen induk yang mendefinisikanformControls
cukup masukkan <input> di dalam <wrapper>Kesalahan berarti, bahwa Angular tidak tahu apa yang harus dilakukan ketika Anda memakai
formControl
adiv
. Untuk memperbaikinya, Anda memiliki dua opsi.formControlName
elemen, yang didukung oleh Angular di luar kotak. Mereka adalah:input
,textarea
danselect
.ControlValueAccessor
antarmuka. Dengan melakukannya, Anda memberi tahu Angular "cara mengakses nilai kendali Anda" (karena itu namanya). Atau secara sederhana: Apa yang harus dilakukan, ketika Anda meletakkanformControlName
elemen, itu tidak secara alami memiliki nilai yang terkait dengannya.Sekarang, mengimplementasikan
ControlValueAccessor
antarmuka bisa sedikit menakutkan pada awalnya. Terutama karena tidak ada dokumentasi yang bagus tentang ini di luar sana dan Anda perlu menambahkan banyak boilerplate ke kode Anda. Jadi izinkan saya mencoba untuk memecah ini dalam beberapa langkah mudah diikuti.Pindahkan kontrol formulir Anda ke komponennya sendiri
Untuk menerapkannya
ControlValueAccessor
, Anda perlu membuat komponen baru (atau arahan). Pindahkan kode yang terkait dengan kontrol formulir Anda di sana. Seperti ini juga akan mudah digunakan kembali. Memiliki kontrol yang sudah ada di dalam komponen mungkin menjadi alasan di tempat pertama, mengapa Anda perlu mengimplementasikanControlValueAccessor
antarmuka, karena jika tidak, Anda tidak akan dapat menggunakan komponen kustom Anda bersama-sama dengan bentuk Angular.Tambahkan boilerplate ke kode Anda
Menerapkan
ControlValueAccessor
antarmuka cukup verbose, inilah boilerplate yang menyertainya:Jadi apa yang dilakukan masing-masing bagian?
ControlValueAccessor
antarmukaControlValueAccessor
antarmukaonChange
danonTouch
dengan implementasinya sendiri selama runtime, sehingga Anda dapat memanggil fungsi-fungsi tersebut. Jadi poin ini penting untuk dipahami: Anda tidak perlu mengimplementasikan onChange dan onTouch sendiri (selain dari implementasi kosong awal). Satu-satunya hal yang Anda lakukan dengan (c) adalah membiarkan Angular melampirkan fungsinya sendiri ke kelas Anda. Mengapa? Sehingga Anda kemudian dapat memanggil yangonChange
danonTouch
metode yang disediakan oleh angular pada waktu yang tepat. Kita akan melihat bagaimana ini bekerja di bawah.writeValue
metode ini bekerja di bagian selanjutnya, ketika kita mengimplementasikannya. Saya telah meletakkannya di sini, jadi semua properti yang diperlukanControlValueAccessor
diimplementasikan dan kode Anda masih dikompilasi.Terapkan writeValue
Apa yang
writeValue
dilakukan, adalah melakukan sesuatu di dalam komponen kustom Anda, ketika kontrol formulir diubah di luar . Jadi misalnya, jika Anda telah menamai komponen kontrol formulir kustomapp-custom-input
Anda dan Anda akan menggunakannya dalam komponen induk seperti ini:kemudian
writeValue
akan terpicu setiap kali komponen induk mengubah nilaimyFormControl
. Ini bisa misalnya selama inisialisasi form (this.form = this.formBuilder.group({myFormControl: ""});
) atau pada form resetthis.form.reset();
.Apa yang biasanya ingin Anda lakukan jika nilai kontrol formulir berubah di luar, adalah menulisnya ke variabel lokal yang mewakili nilai kontrol formulir. Misalnya, jika Anda
CustomInputComponent
berputar di sekitar kontrol bentuk berbasis teks, itu bisa terlihat seperti ini:dan dalam html dari
CustomInputComponent
:Anda juga bisa menulisnya langsung ke elemen input seperti yang dijelaskan dalam dokumen Angular.
Sekarang Anda telah menangani apa yang terjadi di dalam komponen Anda ketika sesuatu berubah di luar. Sekarang mari kita lihat ke arah yang lain. Bagaimana Anda memberi tahu dunia luar ketika ada sesuatu yang berubah di dalam komponen Anda?
Memanggil di Ubah
Langkah selanjutnya adalah memberi tahu komponen induk tentang perubahan di dalam Anda
CustomInputComponent
. Di sinilahonChange
danonTouch
fungsi dari (c) dari atas ikut bermain. Dengan memanggil fungsi-fungsi itu, Anda dapat memberi tahu pihak luar tentang perubahan di dalam komponen Anda. Untuk menyebarkan perubahan nilai ke luar, Anda perlu memanggil onChange dengan nilai baru sebagai argumen . Misalnya, jika pengguna mengetik sesuatu diinput
bidang di komponen khusus Anda, Anda menelepononChange
dengan nilai yang diperbarui:Jika Anda memeriksa implementasi (c) dari atas lagi, Anda akan melihat apa yang terjadi: Angular terikat penerapannya sendiri ke
onChange
properti kelas. Implementasi itu mengharapkan satu argumen, yang merupakan nilai kontrol yang diperbarui. Apa yang Anda lakukan sekarang adalah Anda memanggil metode itu dan dengan demikian membiarkan Angular tahu tentang perubahan itu. Angular sekarang akan maju dan mengubah nilai formulir di luar. Ini adalah bagian penting dari semua ini. Anda memberi tahu Angular kapan harus memperbarui kontrol formulir dan dengan nilai apa dengan menelepononChange
. Anda telah memberinya sarana untuk "mengakses nilai kontrol".Ngomong-ngomong: Nama
onChange
itu dipilih oleh saya. Anda dapat memilih apa pun di sini, misalnyapropagateChange
atau serupa. Namun apa pun nama Anda, itu akan menjadi fungsi yang sama yang mengambil satu argumen, yang disediakan oleh Angular dan yang terikat ke kelas Anda denganregisterOnChange
metode selama runtime.Memanggil onTouch
Karena kontrol formulir dapat "disentuh", Anda juga harus memberikan sudut kepada Angular cara untuk memahami ketika kontrol bentuk kustom Anda disentuh. Anda dapat melakukannya, Anda dapat menebaknya, dengan memanggil
onTouch
fungsi. Jadi untuk contoh kami di sini, jika Anda ingin tetap mematuhi bagaimana Angular melakukannya untuk kontrol bentuk out-of-the-box, Anda harus memanggilonTouch
ketika bidang input kabur:Sekali lagi,
onTouch
adalah nama yang dipilih oleh saya, tetapi apa fungsi sebenarnya disediakan oleh Angular dan tidak membutuhkan argumen. Yang masuk akal, karena Anda hanya memberi tahu Angular, bahwa kontrol bentuk telah disentuh.Menyatukan semuanya
Jadi, bagaimana itu terlihat ketika datang bersama-sama? Seharusnya terlihat seperti ini:
Lebih banyak contoh
Formulir Bersarang
Perhatikan bahwa Access Control Value BUKAN alat yang tepat untuk grup formulir bersarang. Untuk grup formulir bersarang Anda bisa menggunakan
@Input() subform
saja. Access Control Value dimaksudkan untuk membungkuscontrols
, bukangroups
! Lihat contoh ini cara menggunakan input untuk formulir bersarang: https://stackblitz.com/edit/angular-nested-forms-input-2Sumber
sumber
Bagi saya itu disebabkan oleh atribut "berganda" pada kontrol input terpilih karena Angular memiliki ValueAccessor yang berbeda untuk jenis kontrol ini.
Dan di dalam template gunakan seperti ini
Detail lebih lanjut, ref Documents Resmi
sumber