浅析Angular19 自定义表单控件

网友投稿 362 2023-02-20


浅析Angular19 自定义表单控件

1 需求

当开发者需要一个特定的表单控件时就需要自己开发一个和默认提供的表单控件用法相似的控件来作为表单控件;自定义的表单控件必须考虑模型和视图之间的数据怎么进行交互

2 官方文档 -> 点击前往

Angular为开发者提供了ControlValueAccessor接口来辅助开发者构建自定义的表单控件,开发者只需要在自定义表单控件类中实现ControlValueAccessor接口中的方法就可以实现模型和视图之间的数据交互

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

该方法用于将值写入到自定义表单控件中的元素;

这个参数值(obj)是使用这个自定义表单控件的组件通过模板表单或者响应式表单的数据绑定传过来的;

在自定义表单控件的类中只需要将这个值(obj)赋值给一个成员变量即可,自定义表单控件的视图就会通过属性绑定显示出来这个值

2.2 registerOnChange

registerOnChange(fn: any): void

自定义表单控件的数据发生变化时会触发registerOnChange方法,该方用于如何处理自定义表单控件数据的变化;

registerOnChange方法接收的参数(fn)其实是一个方法,该方法负责处理变化的数LTOLo据

当自定义控件数据变化时就会自动调用fn执行的方法,但是通常的做法是自定义一个方法 propagateChange 让自定义的方法指向fn,这样当数据变化时只需要调用 propagateChange 就可以对变化的数据进行处理

2.3 registerOnTouched

registerOnTouched(fn: any): void

表单控件被触摸时会触发registerOnTouched方法,具体细节待更新......2018-1-31 11:18:33

2.4 setDisabledState

setDisabledState(isDisabled: boolean)?: void

待更新......2018-1-31 11:19:30

3 编程步骤

3.1 创建自定义表单控件组件


增加

add

减少

remove

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 功能描述

点击增加按钮时当前计数会增加1,点击减少按钮时当前计数会剪1

3.1.2 直接在其他组件中使用时会报错

报错信息如下:

错误信息是说我们我们使用的组件还不是一个表单控件

3.2 如何让组件变成一个表单控件组件

3.2.1 实现 ControlValueAccessor 接口

export class CounterComponent implements OnInit, ControlValueAccessor {

countNumber: number = 0;

constructor() { }

ngOnInit() {

}

onIncrease() {

this.countNumber++;

}

onDecrease() {

this.countNumber--;

}

/**将数据从模型传输到视图 */

writeValue(obj: any): void {

}

/**将数据从视图传播到模型 */

registerOnChange(fn: any): void {

}

registerOnTouched(fn: any): void {

}

setDisabledState?(isDisabled: boolean): void {

}

}

3.2.2 指定依赖信息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 implements OnInit, ControlValueAccessor {

countNumber: number = 0;

constructor() { }

ngOnInit() {

}

onIncrease() {

this.countNumber++;

}

onDecrease() {

this.countNumber--;

}

/**将数据从模型传输到视图 */

writeValue(obj: any): void {

}

/**将数据从视图传播到模型 */

registerOnChange(fn: any): void {

}

registerOnTouched(fn: any): void {

}

setDisabledState?(isDisabled: boolean): void {

}

}

3.2.3 待修复bug

虽然可以正常运行,但是表单控件中的元素接受不到使用表单控件那个组件中表单模型传过来的数据,表单控件变化的数据也无法回传到使用表单控件那个组件中的表单模型中去;简而言之,就是模型和视图之间无法进行数据交互

3.3 实习那模型和试图的数据交互

3.3.1 模型到视图

重构自定义表单控件类中的 writeValue 方法

技巧01:writeValue 方法中的参数是使用自定义表单控件的那个组件通过表单的数据绑定传进来的

3.3.2 视图到模型

》自定义一个方法来处理自定义表单控件中的变化数据

propagateChange = (_: any) => {};

》重构自定义表单控件类中的 registerOnChange 方法

/**将数据从视图传播到模型 */

registerOnChange(fn: any): void {

this.propagateChange = fn;

}

》在数据变化的地方调用那个自定义的方法

3.4 自定义表单控件组件代码汇总


增加

add

减少

remove

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(() => CouLTOLonterComponent),

multi: true

}

]

})

export class CounterComponent implements OnInit, ControlValueAccessor {

countNumber: number = 0;

propagateChange = (_: any) => {};

constructor() { }

ngOnInit() {

}

onIncrease() {

this.countNumber++;

this.propagateChange(this.countNumber);

}

onDecrease() {

this.countNumber--;

this.propagateChange(this.countNumber);

}

/**将数据从模型传输到视图 */

writeValue(obj: any): void {

this.countNumber = obj;

}

/**将数据从视图传播到模型 */

registerOnChange(fn: any): void {

/**fn其实是一个函数,当视图中的数据改变时就会调用fn指向的这个函数,从而达到将数据传播到模型的目的 */

this.propagateChange = fn; // 将fn的指向赋值给this.propagateChange,在需要将改变的数据传到模型时只需要调用this.propagateChange方法即可

}

registerOnTouched(fn: any): void {

}

setDisabledState?(isDisabled: boolean): void {

}

}

3.5 使用自定义表单控件的那个组件的代码汇总

技巧01:如果自定义表单控件和使用自定义表单控件的组件都在不在同一个模块时需要对自定义表单控件对应组件进行导出和导入操作

outerCounterValue value: {{outerCounterValue}}



{{form.value | json}}




@163.com


姓名为:{{name.value}}

done

测试

鼠标放上去

{{taskList.name}}

使用了ngNonBindable的标签,会将该标签里面的元素内容全部都看做时纯文本

{{taskLists | json }}

← 这是{{taskLists | json }}渲染的内容

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 '../..http:///service/quote.service';

@Component({

selector: 'app-test01',

templateUrl: './test01.component.html',

styleUrls: ['./test01.component.scss']

})

export class Test01Component implements 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: '进行中'},

{label: 2, name: '已完成'}

];

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.myValidator], []],

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('提交');

}

onTestClick() {

// this.data = this.testForm.get('email').value;

// console.log(this.testForm.getError);

console.log(this.testForm.controls['email']);

}

onTestLogin() {

console.log(this.loginForm.value);

if (this.loginForm.valid) {

console.log('登陆数据合法');

} else {

console.log('登陆数据不合法');

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 初始化效果展示


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:包含python接口测试例的词条
下一篇:maven下载jar包改用阿里云maven库的方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~