1. 组件继承ControlValueAccessor,ControlValueAccessor接口需要实现三个必选方法

writeValue()  用于向元素中写入值,获取表单的元素的元素值
registerOnChange()设置一个当控件接受到改变的事件时所要调用的函数;这也是我们把变化 emit 回表单的机制;
registerOnTouched() 设置一个当控件接受到 touch 事件时所要调用的函数
export class ImageListSelectComponent implements ControlValueAccessor {

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

  writeValue(obj: any): void{
this.selectedImg = obj;
}
registerOnChange(fn: (_: any) => void): void {
this._onChange = fn;
} registerOnTouched(fn: any): void {
} }

2.  一个的 token 是 NG_VALUE_ACCESSOR 。这是将控件本身注册到 DI 框架成为一个可以让表单访问其值的控件。

但问题来了,如果在元数据中注册了控件本身,而此时控件仍未创建,这怎么破?这就得用到 forwardRef 了,这个函数允许我们引用一个尚未定义的对象。

另外一个 NG_VALIDATORS 是让控件注册成为一个可以让表单得到其验证状态的控件

providers:[
{
provide:NG_VALUE_ACCESSOR,
useExisting:forwardRef(()=>ImageListSelectComponent),
multi:true
},
{
provide:NG_VALIDATORS,
useExisting:forwardRef(()=>ImageListSelectComponent),
multi:true
}
]

3. 控件的验证器函数(必写方法,否则会报错)

validate(c:FormControl):{[key:string]:any}{
return this.selectedImg ? null :{
imageListNotValid:true
}
}

4. 表单控制器命名formControlName="avatar"

      <app-image-list-select
[cols]="'6'"
[rowHeight]="'1:1'"
[items]='items'
[title]="'选择头像:'"
formControlName="avatar">
</app-image-list-select>

完整代码:

app-image-list-select.component.ts
 import { Component, Input, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, NG_VALIDATORS, FormControl } from '@angular/forms'; @Component({
selector: 'app-image-list-select',
templateUrl: './image-list-select.component.html',
styles: [],
providers:[
{
provide:NG_VALUE_ACCESSOR,
useExisting:forwardRef(()=>ImageListSelectComponent),
multi:true
},
{
provide:NG_VALIDATORS,
useExisting:forwardRef(()=>ImageListSelectComponent),
multi:true
}
]
})
export class ImageListSelectComponent implements ControlValueAccessor { @Input() cols=6;
@Input() rowHeight='1:1';
@Input() items:string[]=[];
@Input() title = '选择';
selectedImg:string; _onChange = (_:any)=>{}; constructor() { } writeValue(obj: any): void{
console.log(obj);
this.selectedImg = obj;
}
registerOnChange(fn: (_: any) => void): void {
this._onChange = fn;
} registerOnTouched(fn: any): void {
} validate(c:FormControl):{[key:string]:any}{
return this.selectedImg ? null :{
imageListNotValid:true
}
} changeSelected(index){
this.selectedImg = this.items[index];
this._onChange(this.selectedImg);
} }
app-image-list-select.component.html
 <div>
<h3>{{title}}</h3>
<mat-icon [svgIcon]='selectedImg'></mat-icon>
</div>
<mat-grid-list [cols]="cols" [rowHeight]="rowHeight">
<mat-grid-tile *ngFor="let item of items;let i = index">
<mat-icon [svgIcon]='item' (click)="changeSelected(i)"></mat-icon>
</mat-grid-tile>
</mat-grid-list>

register.component.html引用自定义表单控件app-image-list-select

 <div class="login-wrap">
<form [formGroup]="myGroup" (ngSubmit)="onSubmit(myGroup,$event)">
<mat-card class="example-card">
<mat-card-header><mat-card-title>注册</mat-card-title></mat-card-header>
<mat-card-content>
<mat-form-field>
<input matInput placeholder="您的email" formControlName="email">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="您的姓名" formControlName="username">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="您的密码" formControlName="password">
</mat-form-field>
<mat-form-field>
<input matInput placeholder="请重新输入密码" formControlName="repassword">
</mat-form-field>
<div>
<app-image-list-select
[cols]="'6'"
[rowHeight]="'1:1'"
[items]='items'
[title]="'选择头像:'"
formControlName="avatar">
</app-image-list-select>
</div>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button type="submit">注册</button>
</mat-card-actions>
</mat-card>
</form>
</div>

register.component.ts

 import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder } from '@angular/forms'; @Component({
selector: 'app-register',
templateUrl: './register.component.html',
styles: [`
mat-form-field{width:100%;}
form{
width: 500px;
margin: 20px auto;
}
`]
})
export class RegisterComponent implements OnInit { myGroup:FormGroup;
items=[];
constructor(private fb:FormBuilder) { } ngOnInit() {
const nums = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
this.items = nums.map(d=> `avatars:svg-${d}`); const img = `avatars:svg-${Math.floor(Math.random()*16).toFixed()}`; this.myGroup = this.fb.group({
email:[],
username:[],
password:[],
repassword:[],
avatar:[img]
});
}
onSubmit({value,valid},ev:Event){
console.log(value);
console.log(valid);
} }

angular 响应式自定义表单控件—注册头像实例的更多相关文章

  1. Angular19 自定义表单控件

    1 需求 当开发者需要一个特定的表单控件时就需要自己开发一个和默认提供的表单控件用法相似的控件来作为表单控件:自定义的表单控件必须考虑模型和视图之间的数据怎么进行交互 2 官方文档 -> 点击前 ...

  2. Angular:自定义表单控件

    分享一个最近写的支持表单验证的时间选择组件. import {AfterViewInit, Component, forwardRef, Input, OnInit, Renderer} from & ...

  3. AngularJS自定义表单控件

    <!doctype html> <html ng-app="myApp"> <head> <script src="G:\\So ...

  4. Angular 从入坑到挖坑 - 表单控件概览

    一.Overview angular 入坑记录的笔记第三篇,介绍 angular 中表单控件的相关概念,了解如何在 angular 中创建一个表单,以及如何针对表单控件进行数据校验. 对应官方文档地址 ...

  5. C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序

    C#中缓存的使用   缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可:  <%@ Outp ...

  6. MVC树控件,mvc中应用treeview,实现复选框树的多层级表单控件

    类似于多层级的角色与权限控制功能,用MVC实现MVC树控件,mvc中应用treeview,实现复选框树的多层级表单控件.最近我们的项目中需要用到树型菜单,以前使用WebForm时,树型菜单有微软提供的 ...

  7. Ideal Forms – 帮助你建立响应式 HTML5 表单

    Ideal Forms 是建立和验证响应式 HTML5 表单的终极框架.它刚刚发布 V3 版本,更小,更快,更具可扩展性.它支持实时验证,完全自适应(适应容器,没有 CSS 媒体查询需要),键盘支持, ...

  8. Vue.js学习 Item9 – 表单控件绑定

    基础用法 可以用 v-model 指令在表单控件元素上创建双向数据绑定.根据控件类型它自动选取正确的方法更新元素.尽管有点神奇,v-model 不过是语法糖,在用户输入事件中更新数据,以及特别处理一些 ...

  9. 表单控件 css的三中引入方式css选择器

    1. 表单控件: 单选框 如果两个单选的name值一样,会产生互斥效果 <p> <!--单选框--> 男<input type="radio" nam ...

随机推荐

  1. Android 经常使用的adb命令

    1.安装APK(假设加 -r 參数,保留已设定数据.又一次安装filename.apk) adb install xxx.apk adb install -r xxx.apk 2.卸载APK(假设加 ...

  2. 在vim中配置python补全,fedora 19

    近期发现python是个不错的语言,值得一学,先配置下环境,让vim具有keyword补全功能,步骤例如以下,我这个是fedora,其它发行版类似 $ su ******** # yum instal ...

  3. 深入理解Android(3)——Eclipse集成javah和NDK-Builder

    在上一篇文章中我们使用了javah工具来生成了native java文件所对应的C++头文件,但是这样生成比较麻烦,我们这一篇来介绍如何在eclipse中集成javah和NDK-Builder. 一. ...

  4. easyui 前端实现分页 复制就能用

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. 【Codeforces Round #426 (Div. 2) A】The Useless Toy

    [Link]:http://codeforces.com/contest/834/problem/A [Description] [Solution] 开个大小为4的常量字符数组; +n然后余4,-n ...

  6. ANSI-X99MAC算法和PBOC的3DES MAC算法

    仅仅要有标准的DES加密和解密算法.类似ANSI-X99MAC算法和PBOC3DES算法就非常好实现.他们都是用DES算法再经过一层算法实现的.实现原理看图就能看明确.3DES算法实现就更简单了.就是 ...

  7. 阿里云 CentOS7.4 环境安装mysql5.7

    1. 删除默认安装的数据库,无所谓的请略过 据说CentOS7.x版本会默认安装mariadb数据库,我有点强迫症,故卸载之: rpm -qa|grep mariadb yum remove mari ...

  8. mahout算法库(四)

    mahout算法库 分为三大块 1.聚类算法 2.协同过滤算法(一般用于推荐) 协同过滤算法也可以称为推荐算法!!! 3.分类算法 算法类 算法名 中文名 分类算法               Log ...

  9. Java学习笔记三

    1.面向过程思想,强调的是过程(即动作,函数):面向对象思想,强调的是对象. 2.类与对象关系:类是对事物的描述(属性和行为-方法),对象是类的实例.对象通过new生成.属性也称成员变量;方法也称成员 ...

  10. Redux简易理解

    1. createStore(相当于vuex的$store) 这才是数据存储仓库,用来存储初和输出的数据,更vuex$store功能一样 作用:  创建一个 Redux store 来以存放应用中所有 ...