An Analysis of Angular19 custom form controls and angular19
1 Requirement
When developers need a specific form control, they need to develop a control similar to the default form control usage as a form control; the custom form control must consider how the data between the model and the view interacts.
2. Official documentation->Click to go
Angular provides the ControlValueAccessor interface to help developers build custom form controls, developers only need to implement methods in the ControlValueAccessor interface in the custom form control class to realize data interaction between the model and view.
interface ControlValueAccessor { writeValue(obj: any): void registerOnChange(fn: any): void registerOnTouched(fn: any): void setDisabledState(isDisabled: boolean)?: void}
2.1 writeValue
writeValue(obj: any): void
This method is used to write values to elements in the custom form control;
This parameter value (obj) is transmitted by using the custom form control component through the data binding of the template form or response form;
In the class of the custom form control, you only need to assign this value (obj) to a member variable. The view of the custom form control will display this value through property binding.
2.2 registerOnChange
registerOnChange(fn: any): void
The registerOnChange method is triggered when the data of the custom form control changes. This method is used to process the data changes of the custom form control;
The parameter (fn) received by the registerOnChange method is actually a method that processes the changed data.
The fn execution method is automatically called when the custom control data changes. However, a custom method propagateChange directs the custom method to fn, in this way, you only need to call propagateChange to process the changed data.
2.3 registerOnTouched
registerOnTouched(fn: any): void
The registerOnTouched method is triggered when the form control is touched. The details are to be updated... 11:18:33
2.4 setDisabledState
setDisabledState(isDisabled: boolean)?: void
To be updated... 11:19:30
3 programming steps
3.1 create a custom form Control Component
<Div>
HTML
import { Component, OnInit } from '@angular/core';import { ControlValueAccessor } from '@angular/forms';@Component({ selector: 'app-counter', templateUrl: './counter.component.html', styleUrls: ['./counter.component.scss']})export class CounterComponent implements OnInit { countNumber: number = 0; constructor() { } ngOnInit() { } onIncrease() { this.countNumber++; } onDecrease() { this.countNumber--; }}
3. 1.1 Function Description
When you click the Add button, the current count increases by 1. When you click the reduce button, the current count is cut by 1.
3.1.2 when used directly in other components, an error is reported.
The error message is as follows:
The error message indicates that the <app-counter> component we use is not a Form Control.
3.2 how to turn the <app-counter> component into a form Control Component
3.2.1 implement the ControlValueAccessor Interface
Export class CounterComponent implements OnInit, ControlValueAccessor {countNumber: number = 0; constructor () {} ngOnInit () {} onIncrease () {this. countNumber ++;} onDecrease () {this. countNumber --;}/** transfers data from the model to the view */writeValue (obj: any ): void {}/** transmits data from the view to the model */registerOnChange (fn: any): void {} registerOnTouched (fn: any): void {} setDisabledState? (IsDisabled: boolean): void {}}
3.2.2 specify the dependency information providers
Import {Component, OnInit, forwardRef} from '@ angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from' @ angular/forms'; @ Component ({selector: 'app-counter ', templateUrl :'. /counter.component.html ', styleUrls :['. /counter. component. scss'], providers: [{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef () => CounterComponent), multi: true}]}) export class CounterComponent impleme CNT OnInit, ControlValueAccessor {countNumber: number = 0; constructor () {} ngOnInit () {} onIncrease () {this. countNumber ++;} onDecrease () {this. countNumber --;}/** transfers data from the model to the view */writeValue (obj: any ): void {}/** transmits data from the view to the model */registerOnChange (fn: any): void {} registerOnTouched (fn: any): void {} setDisabledState? (IsDisabled: boolean): void {}}
3.2.3 bugs to be fixed
Although it can run normally, the elements in the Form Control cannot accept the data transmitted from the form model in the component using the form control, the changed data of the form control cannot be uploaded back to the form model of the component using the form control. In short, data interaction between the model and the view cannot be performed.
3.3 interworking with the model and attempted data
3.3.1 model to view
Refactor the writeValue method in the custom form control class
Tip 01: the parameters in the writeValue method are transmitted by using the component of the custom form control through the data binding of the form.
3.3.2 view to Model
To process the changed data in the custom form control.
propagateChange = (_: any) => {};
Reconstructs the registerOnChange method in the custom form control class.
/** Spread data from the view to the model */registerOnChange (fn: any): void {this. propagateChange = fn ;}
Call the custom method where data changes
3.4 custom form control component code Summary
<Div>
HTML
Import {Component, OnInit, forwardRef} from '@ angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from' @ angular/forms'; @ Component ({selector: 'app-counter ', templateUrl :'. /counter.component.html ', styleUrls :['. /counter. component. scss'], providers: [{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef () => CounterComponent), multi: true}]}) export class CounterComponent impleme CNT OnInit, ControlValueAccessor {countNumber: number = 0; propagateChange = (_: any) =>{}; constructor () {} ngOnInit () {} onIncrease () {this. countNumber ++; this. propagateChange (this. countNumber);} onDecrease () {this. countNumber --; this. propagateChange (this. countNumber);}/** transmit data from the model to the view */writeValue (obj: any): void {this. countNumber = obj;}/** spread data from the view to the model */registerOnChange (fn: any): v Oid {/** fn is actually a function. When the data in the view changes, the fn point is called to spread data to the model. */this. propagateChange = fn; // assign the fn point to this. propagateChange: you only need to call this when transferring the changed data to the model. the propagateChange method can be} registerOnTouched (fn: any): void {} setDisabledState? (IsDisabled: boolean): void {}}
3.5 code Summary of the component using the custom form Control
Tip 01: If the custom form control and the components using the custom form control are not in the same module, you must export and import the corresponding components of the custom form control.
<Div class = "panel-primary"> <div class = "panel-heading"> panel template </div> <div class = "panel-body">
HTML
Import {Component, OnInit, HostListener, Inject} from '@ angular/core'; import {FormControl, FormGroup, FormBuilder, Validators} from' @ angular/forms '; import {Http} from '@ angular/http'; import {QuoteService} from '.. /.. /service/quote. service '; @ Component ({selector: 'app-test01', templateUrl :'. /test01.component.html ', styleUrls :['. /test01.component. scss ']}) export class Test01Component impleme CNT OnInit {countNumber: number = 9; outerCounterValue: number = 5; ngif = true; loginForm: FormGroup; testForm: FormGroup; data: any; name: FormControl = new FormControl (); desc: string = 'Hello boy '; taskLists = [{label: 1, name:' In Progress '}, {label: 2, name: 'finished'}]; constructor (private formBuilder: FormBuilder, private http: Http, @ Inject ('base _ config') private baseConfig, private quoteService: QuoteService) {} ngOnInit () {this. testForm = new FormGroup ({email: new FormControl ('', [Validators. required, Validators. minLength (4)], []), password: new FormControl ('', [Validators. required], [])}); this. name. valueChanges. debounceTime (500 ). subscribe (value => alert (value); this. loginForm = this. formBuilder. group ({username: ['', [Validators. required, Validators. minLength (4), this. myValidat Or], [], userpwd: ['', [Validators. required, Validators. minLength (6)], []}); this. quoteService. test (). subscribe (resp => console. log (resp);} onChangeNgifValue () {if (this. ngif = false) {this. ngif = true;} else {this. ngif = false ;}@ HostListener ('keyup. enter') onTestNgModelClick () {alert ('Submit');} onTestClick () {// this. data = this. testForm. get ('email '). value; // console. log (this. test Form. getError); console. log (this. testForm. controls ['email ']);} onTestLogin () {console. log (this. loginForm. value); if (this. loginForm. valid) {console. log ('valid login data');} else {console. log ('invalid login data'); console. log (this. loginForm. controls ['username']. errors); console. log (this. loginForm. get ('userpwd '). errors) ;}} myValidator (fc: FormControl): {[key: string]: any} {const valid = fc. value = 'admin '; Return valid? Null: {myValidator: {requiredUsername: 'admin', actualUsername: fc. value }};}}
3.6 display of initialization results