1 需求
当开发者需要一个特定的表单控件时就需要自己开发一个和默认提供的表单控件用法相似的控件来作为表单控件;自定义的表单控件必须考虑模型和视图之间的数据怎么进行交互
2 官方文档 -> 点击前往
Angular为开发者提供了ControlValueAccessor接口来辅助开发者构建自定义的表单控件,开发者只需要在自定义表单控件类中实现ControlValueAccessor接口中的方法就可以实现模型和视图之间的数据交互
interface ControlValueAccessor { writeValue(obj: any): void registerOnChange(fn: any): void registerOnTouched(fn: any): void setDisabledState(isDisabled: boolean)"htmlcode">writeValue(obj: any): void该方法用于将值写入到自定义表单控件中的元素;
这个参数值(obj)是使用这个自定义表单控件的组件通过模板表单或者响应式表单的数据绑定传过来的;
在自定义表单控件的类中只需要将这个值(obj)赋值给一个成员变量即可,自定义表单控件的视图就会通过属性绑定显示出来这个值
2.2 registerOnChange
registerOnChange(fn: any): void自定义表单控件的数据发生变化时会触发registerOnChange方法,该方用于如何处理自定义表单控件数据的变化;
registerOnChange方法接收的参数(fn)其实是一个方法,该方法负责处理变化的数据
当自定义控件数据变化时就会自动调用fn执行的方法,但是通常的做法是自定义一个方法 propagateChange 让自定义的方法指向fn,这样当数据变化时只需要调用 propagateChange 就可以对变化的数据进行处理
2.3 registerOnTouched
registerOnTouched(fn: any): void表单控件被触摸时会触发registerOnTouched方法,具体细节待更新......2018-1-31 11:18:33
2.4 setDisabledState
setDisabledState(isDisabled: boolean)"color: #ff0000">3 编程步骤3.1 创建自定义表单控件组件
<div> <h4>当前计数为:{{countNumber}}</h4> <br /> <div> <button md-icon-button (click)="onIncrease()"> <span>增加</span> <md-icon>add</md-icon> </button> <span style="margin-left: 30px;"></span> <button md-icon-button (click)="onDecrease()"> <span>减少</span> <md-icon>remove</md-icon> </button> </div> </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 功能描述
点击增加按钮时当前计数会增加1,点击减少按钮时当前计数会剪1
3.1.2 直接在其他组件中使用时会报错
报错信息如下:
错误信息是说我们我们使用的组件<app-counter>还不是一个表单控件
3.2 如何让<app-counter>组件变成一个表单控件组件
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"text-align: center">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"text-align: center">3.3.2 视图到模型
》自定义一个方法来处理自定义表单控件中的变化数据
propagateChange = (_: any) => {};》重构自定义表单控件类中的 registerOnChange 方法
/**将数据从视图传播到模型 */ registerOnChange(fn: any): void { this.propagateChange = fn; }》在数据变化的地方调用那个自定义的方法
3.4 自定义表单控件组件代码汇总
<div> <h4>当前计数为:{{countNumber}}</h4> <br /> <div> <button md-icon-button (click)="onIncrease()"> <span>增加</span> <md-icon>add</md-icon> </button> <span style="margin-left: 30px;"></span> <button md-icon-button (click)="onDecrease()"> <span>减少</span> <md-icon>remove</md-icon> </button> </div> </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 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"text-align: center"><div class="panel panel-primary"> <div class="panel-heading">面板模板</div> <div class="panel-body"> <h3>面板测试内容</h3> </div> <div class="panel-footer">2018-1-22 10:22:20</div> </div> <div class="panel-primary"> <div class="panel-heading">自定义提取表单控件</div> <div class="panel-body"> <form #myForm=ngForm> <app-counter name="counter" [(ngModel)]="countNumber"> </app-counter> </form> <h6>绿线上是自定义提取的表单控件显示的内容</h6> <hr style="border: solid green 2px" /> <h6>绿线下是使用自定义表单控件时表单的实时数据</h6> <h3>表单控件的值为:{{myForm.value | json}}</h3> </div> <div class="panel-footer">2018-1-31 10:09:17</div> </div> <div class="panel-primary"> <div class="panel-heading">提取表单控件</div> <div class="panel-body"> <form #form="ngForm"> <p>outerCounterValue value: {{outerCounterValue}}</p> <app-exe-counter name="counter" [(ngModel)]="outerCounterValue"></app-exe-counter> <br /> <button md-raised-button type="submit">Submit</button> <br /> <div> {{form.value | json}} </div> </form> </div> <div class="panel-footer">2018-1-27 21:51:45</div> </div> <div class="panel panel-primary"> <div class="panel-heading">ngIf指令测试</div> <div class="panel-body"> <button md-rasied-button (click)="onChangeNgifValue()">改变ngif变量</button> <br /> <div *ngIf="ngif; else ngifTrue" > <h4 style="background-color: red; color: white" >ngif变量的值为true</h4> </div> <ng-template #ngifTrue> <h4 style="background-color: black; color: white">ngif变量的值为false</h4> </ng-template> </div> <div class="panel-footer">2018-1-27 16:58:17</div> </div> <div class="panel panel-primary"> <div class="panel-heading">RXJS使用</div> <div class="panel-body"> <h4>测试内容</h4> </div> <div class="panel-footer">2018-1-23 21:14:49</div> </div> <div class="panel panel-primary"> <div class="panel-heading">自定义验证器</div> <div class="panel-body"> <form (ngSubmit)="onTestLogin()" [formGroup]="loginForm"> <md-input-container> <input mdInput placeholder="请输入登录名" formControlName="username" /> </md-input-container> <br /> <md-input-container> <input mdInput placeholder="请输入密码" formControlName="userpwd" /> </md-input-container> <br /> <button type="submit" md-raised-button>登陆</button> </form> </div> <div class="panel-footer">2018-1-23 11:06:01</div> </div> <div class="panel panel-primary"> <div class="panel-heading">响应式表单</div> <div class="panel-body"> <form [formGroup]="testForm"> <md-input-container> <input mdInput type="text" placeholder="请输入邮箱" formControlName="email" /> <span mdSuffix>@163.com</span> </md-input-container> <br /> <md-input-container> <input mdInput type="password" placeholder="请输入密码" formControlName="password" /> </md-input-container> </form> <hr /> <div> <h2>表单整体信息如下:</h2> <h4>表单数据有效性:{{testForm.valid}}</h4> <h4>表单数据为:{{testForm.value | json}}</h4> <h4>获取单个或多个FormControl:{{testForm.controls['email'] }}</h4> <hr /> <h2>email输入框的信息如下:</h2> <h4>有效性:{{testForm.get('email').valid}}</h4> <h4>email输入框的错误信息为:{{testForm.get('email').errors | json}}</h4> <h4>required验证结果:{{testForm.hasError('required', 'email') | json}}</h4> <h4>minLength验证结果:{{ testForm.hasError('minLength', 'email') | json }}</h4> <h4>hello:{{ testForm.controls['email'].errors | json }}</h4> <hr /> <h2>password输入框啊的信息如下:</h2> <h4>有效性:{{testForm.get('password').valid}}</h4> <h4>password输入框的错误信息为:{{testForm.get('password').errors | json }}</h4> <h4>required验证结果:{{testForm.hasError('required', 'password') | json}}</h4> </div> <div> <button nd-rasied-button (click)="onTestClick()">获取数据</button> <h4>data变量:{{data}}</h4> </div> </div> <div class="panel-footer">2018-1-22 15:58:43</div> </div> <div class="panel panel-primary"> <div class="panel-heading">利用响应式编程实现表单元素双向绑定</div> <div class="panel-body"> <md-input-container> <input mdInput placeholder="请输入姓名(响应式双向绑定):" [formControl]="name"/> </md-input-container> <div> 姓名为:{{name.value}} </div> </div> <div class="panel-footer">2018-1-22 11:12:35</div> </div> --> <div class="panel panel-primary"> <div class="panel-heading">模板表单</div> <div class="panel-body"> <md-input-container> <input mdInput placeholder="随便输入点内容" #a="ngModel" [(ngModel)]="desc" name="desc" /> <button type="button" md-icon-button mdSuffix (click)="onTestNgModelClick()"> <md-icon>done</md-icon> </button> </md-input-container> <div> <h3>名为desc的表单控件的值为:{{ a.value }}</h3> </div> </div> <div class="panel-footer">2018-1-22 10:19:31</div> </div> <div class="panel panel-primary"> <div class="panel-heading">md-chekbox的使用</div> <div calss="panel-body"> <div> <md-checkbox #testCheckbox color="primary" checked="true">测试</md-checkbox> </div> <div *ngIf="testCheckbox.checked"> <h2>测试checkbox被选中啦</h2> </div> </div> <div class="panel-footer">2018-1-18 14:02:20</div> </div> <div class="panel panel-primary"> <div class="panel-heading">md-tooltip的使用</div> <div class="panel-body"> <span md-tooltip="重庆火锅">鼠标放上去</span> </div> <div class="panel-footer">2018-1-18 14:26:58</div> </div> <div class="panel panel-primary"> <div class="panel-heading">md-select的使用</div> <div class="panel-body"> <md-select placeholder="请选择目标列表" class="fill-width" style="height: 40px;"> <md-option *ngFor="let taskList of taskLists" [value]="taskList.name">{{taskList.name}}</md-option> </md-select> </div> <div class="panel-footer">2018-1-18 14:26:58</div> </div> <div class="panel panel-primary"> <div class="panel-heading">ngNonBindable指令的使用</div> <div class="panel-body"> <h3>描述</h3> <p>使用了ngNonBindable的标签,会将该标签里面的元素内容全部都看做时纯文本</p> <h3>例子</h3> <p> <span>{{taskLists | json }}</span> <span ngNonBindable>← 这是{{taskLists | json }}渲染的内容</span> </p> </div> <div class="panel-footer">2018-1-19 09:34:26</div> </div>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 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 "text-align: center">广告合作:本站广告合作请联系QQ:858582 申请时备注:广告合作(否则不回)
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!暂无评论...
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]