Kontrol Kustom di Sudut

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: '' //  new FormControl() !! }) } } 

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:

 // … class CounterControlComponent implements ControlValueAccessor { private _value; get value() { return this._value; } @Input() set value(val) { this._value = val; this.onChange(this._value); } onChange(_: any) {} up() { this.value++; } down() { this.value - ; } // … } 

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.

Source: https://habr.com/ru/post/id481424/


All Articles