Prolog
Mari kita bicara tentang formulir reaktif dalam sudut, pelajari untuk kontrol kustom cara membuat, menggunakan, dan memvalidasinya. Artikel ini mengasumsikan bahwa Anda sudah terbiasa dengan kerangka sudut, tetapi ingin menyelami lebih dalam spesifiknya. Semoga sukses, mari kita mulai.
Formulir yang Didorong Reaktif dan Templat
Beberapa kata untuk memastikan kita berada di halaman yang sama. Angular memiliki dua jenis formulir:
Formulir yang Didorong Templat dan
Bentuk Reaktif .
Formulir yang Didorong Templat adalah formulir berbasis
pengikatan dua arah (hai, angularjs). Kami menentukan bidang di kelas (misalnya, nama pengguna), di html di tag input kami mengikat [(nilai)] = "nama pengguna", dan ketika nilai input berubah, nilai nama pengguna berubah. Pada 2011, itu adalah sihir sialan! OK, tapi ada nuansanya. Membangun bentuk kompleks dengan cara ini akan ... sulit.
Formulir Reaktif adalah alat yang mudah digunakan untuk membangun formulir yang sederhana dan kompleks. Mereka memberi tahu kami, "buat instance kelas bentuk (
FormGroup ), berikan contoh kontrol (
FormControl ) padanya dan berikan saja ke html, dan saya akan melakukan sisanya." Ok, mari kita coba:
class UsefullComponent { public control = new FormControl(''); public formG = new FormGroup({username: control}); }
<form [formGroup]="formG"> <input type="text" formControlName="username"> </form>
Akibatnya, kami mendapatkan bentuk reaktif, dengan blackjack dan ... yah, Anda mengerti, dengan segala fasilitasnya. Sebagai contoh,
formG.valueChanges akan memberi kita (aliran) perubahan form yang dapat
diobservasi . Anda juga dapat menambahkan kontrol baru, menghapus yang sudah ada, mengubah aturan validasi, mendapatkan nilai formulir (
formG.value ) dan banyak lagi. Dan semua ini bekerja dengan satu instance dari
formG .
Dan agar tidak secara manual membuat instance dari kelas di atas setiap kali, pengembang angular memberi kami
FormBuilder yang nyaman dengan bantuan yang contohnya di atas dapat ditulis ulang seperti ini:
class UsefullComponent { public formG: FormGroup; constructor(private fb: FormBuilder) { this.formG = fb.group({ name: ''
Kontrol khusus
Angular memberi tahu kami "bro, jika Anda tidak memiliki kontrol asli yang cukup (input, tanggal, nomor, dan lainnya), atau jika Anda tidak menyukainya sama sekali untuk sesuatu, inilah alat sederhana namun sangat kuat untuk membuat sendiri." Kontrol kustom dapat digunakan bahkan dengan reaktif, bahkan dengan formulir yang digerakkan template, dan mereka diimplementasikan menggunakan arahan (terkejut!). Mengetahui bahwa komponennya adalah arahan dengan templat (apa yang Anda butuhkan), kami menulis:
import { Component, Input } from '@angular/core'; @Component({ selector: 'counter-control', template: ` <button (click)="down()">Down</button> {{ value }} <button (click)="up()">Up</button> ` }) class CounterControlComponent { @Input() value = 0; up() { this.value++; } down() { this.value - ; } }
Seperti arahan apa pun, ini harus dinyatakan dalam modul (agar artikel tidak tumbuh, kami akan menghilangkan nuansa tersebut).
Sekarang kita dapat menggunakan komponen yang dibuat:
import { Component } from '@angular/core'; @Component({ selector: 'parent-component', template: ` <counter-control></counter-control> ` }) class ParentComponent {}
Itu semua bekerja tentu saja, tetapi di mana bentuknya ?! Setidaknya digerakkan oleh template ....
Jangan panik, semuanya akan terjadi setelah memenuhi
ControlValueAccessor .
Mengontrol nilai accessor
Agar mekanisme sudut untuk berinteraksi dengan kontrol khusus Anda, Anda memerlukan kontrol ini untuk mengimplementasikan antarmuka spesifik. Antarmuka ini disebut
ControlValueAccessor . Ini adalah contoh polimorfisme dari OOP yang cukup bagus, ketika objek kita (dalam hal ini, kontrol) mengimplementasikan antarmuka, dan objek lain (bentuk sudut) berinteraksi dengan objek kita melalui antarmuka ini.
Berkat
ControlValueAccessor , kami memiliki satu cara untuk bekerja dengan kontrol, yang, secara kebetulan, tidak hanya digunakan untuk membuat kontrol khusus. Sudut di bawah kap juga menggunakan antarmuka ini. Untuk apa? Dan hanya untuk membawa tampilan seragam perilaku kontrol asli. Misalnya, dalam input, nilainya terkandung dalam atribut nilai, di kotak centang, nilainya ditentukan melalui atribut yang dicentang. Dengan demikian, setiap jenis kontrol memiliki ControlValueAccessor sendiri: DefaultValueAccessor untuk input dan textarea, CheckboxControlValueAccessor untuk checkbox,
RadioControlValueAccessor untuk tombol radio, dan sebagainya.
Tapi apa yang dilakukan sudut menggunakan
ControlValueAccessor ? Semuanya cukup sederhana, ia menulis nilai dari model ke DOM (tampilan), dan juga meningkatkan acara kontrol perubahan ke
FormGroup dan arahan lainnya.
Sekarang kita telah belajar tentang
ControlValueAccessor , kita dapat menerapkannya pada kendali kita.
Mari kita lihat interface-nya:
interface ControlValueAccessor { writeValue(value: any): void registerOnChange(fn: any): void registerOnTouched(fn: any): void }
writeValue (value: any) - dipanggil ketika sumber (
FormControl baru ('Saya nilai default') ) atau nilai baru di atas
control.setValue ('Saya puas nilai') diatur .
registerOnChange (fn: any) - metode yang mendefinisikan handler yang harus dipanggil ketika nilai berubah (fn adalah panggilan balik yang akan memberi tahu formulir bahwa nilai telah berubah dalam kontrol ini).
registerOnTouched (fn: any) - mendefinisikan callback yang dipanggil pada acara
blur (kontrol ditandai dengan
disentuh )
import { Component, Input } from '@angular/core'; import { ControlValueAccessor } from '@angular/forms'; @Component({ selector: 'counter-control', template: ` <button (click)="down()">Down</button> {{ value }} <button (click)="up()">Up</button> ` }) class CounterControlComponent implements ControlValueAccessor { @Input() value = 0; onChange(_: any) {} up() { this.value++; } down() { this.value - ; } writeValue(value: any) { this.value = value; } registerOnChange(fn) { this.onChange = fn; } registerOnTouched() {} }
Agar formulir induk menyadari perubahan dalam kontrol, kita perlu memanggil metode
onChange untuk setiap perubahan dalam nilai nilai. Agar tidak menulis panggilan
onChange di setiap metode (atas dan ke bawah), kami menerapkan bidang nilai melalui getter dan setter:
Saat ini, sudut tidak tahu bahwa komponen yang mengimplementasikan
ControlValueAccessor harus dianggap sebagai kontrol khusus, mari beri tahu dia tentang hal itu:
import { Component, Input, forwardRef } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; @Component({ … providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CounterControlComponent), multi: true }] }) class CounterControlComponent implements ControlValueAccessor { … }
Di bagian kode ini, kami mengatakan kepada sudut "Saya ingin mendaftarkan kontrol kustom baru (
berikan: NG_VALUE_ACCESSOR ), gunakan yang ada (saat ini komponen kami akan diinisialisasi) contoh komponen (
useExisting: forwardRef (() => CounterControlComponent )"
nilai)) .
multi: true menunjukkan bahwa mungkin ada beberapa dependensi dengan token tersebut (
NG_VALUE_ACCESSOR ).
Bukan hanya dibuat
Sudah waktunya untuk zayuzat kontrol kustom kami. Jangan lupa untuk menambahkan
FormsModule / ReactiveFormsModule ke impor modul tempat kontrol ini digunakan.
Gunakan dalam Formulir Berbasis Template
Semuanya sederhana di sini, dengan menggunakan pengikatan dua arah melalui
ngModel kita mendapatkan perubahan tampilan ketika model berubah dan sebaliknya:
import { Component } from '@angular/core'; @Component({ selector: 'parent-component', template: ` <counter-control [(ngModel)]="controlValue"></counter-control> ` }) class ParentComponent { controlValue = 10; }
Gunakan dalam Formulir Reaktif
Seperti yang dinyatakan di awal artikel, buat instance dari bentuk reaktif melalui
FormBuilder ,
render dalam html dan nikmati:
import { Component, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; @Component({ selector: 'parent-component', template: ` <form [formGroup]="form"> <counter-control formControlName="counter"></counter-control> </form> ` }) class ParentComponent implements OnInit { form: FormGroup; constructor(private fb: FormBuilder) {} ngOnInit() { this.form = this.fb.group({ counter: 5 }); } }
Sekarang ini adalah bentuk reaktif lengkap dengan kontrol kustom kami, sementara semua mekanisme bekerja sama dengan kontrol asli (apakah saya menyebutkan polimorfisme?). Agar tidak memuat artikel saat ini, kami akan berbicara tentang memvalidasi dan menampilkan kesalahan kontrol khusus di artikel berikutnya.
Bahan:
Artikel tentang kontrol khusus dari Pascal Precht.
Dari bentuk
dermaga di sudut.
Serangkaian artikel tentang rxjs.