步骤:
- 编写正常子组件一样,先编写好
- 实现相应的接口
- 调用相应表单控件
import {ChangeDetectionStrategy, Component, EventEmitter, forwardRef, Input, Output} from '@angular/core';
//引入相应的表单文件
import {ControlValueAccessor, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR} from '@angular/forms';
@Component({
selector: 'app-image-list-select',
templateUrl: './image-list-select.component.html',
styleUrls: ['./image-list-select.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ImageListSelectComponent),//向前引用
multi: true, //多对一
},
{
provide: NG_VALIDATORS, //验证器注册
useExisting: forwardRef(() => ImageListSelectComponent),
multi: true,
}
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ImageListSelectComponent implements ControlValueAccessor {
selected: string;
@Input() title = '选择封面:';
@Input() items: string[] = [];
@Input() cols = 8;
@Input() rowHeight = '64px';
@Input() itemWidth = '80px';
@Input() useSvgIcon = false;
@Output('itemChange') itemChange = new EventEmitter<string>();
// 这里是做一个空函数体,真正使用的方法在 registerOnChange 中
// 由框架注册,然后我们使用它把变化发回表单
// 注意,和 EventEmitter 尽管很像,但发送回的对象不同
private propagateChange = (_: any) => {};
// 写入控件值,调用者通过setValue写入的位置
public writeValue(obj: any) {
if (obj && obj !== '') {
this.selected = obj;
}
}
// 当表单控件值改变时,函数 fn 会被调用
// 这也是我们把变化 emit 回表单的机制
public registerOnChange(fn: any) {
this.propagateChange = fn;
}
// 验证表单,验证结果正确返回 null 否则返回一个验证结果对象
public validate(c: FormControl) {
return this.selected ? null : {
imageListSelect: {
valid: false,
},
};
}
// 这里没有使用,用于注册 touched 状态
public registerOnTouched() {
}
// 列表元素选择发生改变触发
onChange(i) {
this.selected = this.items[i];
// 更新表单,这样表单通过get可以获取到最新值
this.propagateChange(this.items[i]);
this.itemChange.emit(this.items[i]);
}
}
调用控件代码展示
<app-image-list-select
[useSvgIcon]="true"
[cols]="6"
[title]="'选择头像:'"
[items]="items"
formControlName="avatar"
></app-image-list-select>
IpList表单控件模板
import { Component, Input, OnInit, forwardRef } from "@angular/core";
import { compact, concat, uniq, difference } from "lodash";
import {
ControlValueAccessor,
NG_VALIDATORS,
NG_VALUE_ACCESSOR
} from "@angular/forms";
@Component({
selector: "agent-install-iplist",
templateUrl: "./agent-install-iplist.component.html",
styleUrls: ["./agent-install-iplist.component.scss"],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AgentInstallIplistComponent), // 向前引用
multi: true // 多对一
},
{
provide: NG_VALIDATORS, // 验证器注册
useExisting: forwardRef(() => AgentInstallIplistComponent),
multi: true
}
]
})
export class AgentInstallIplistComponent
implements OnInit, ControlValueAccessor {
@Input() allAgentIPArray; // agent 安装 ip 列表
@Input() inputIpList;
allIpList: string[] = []; // 全部Ip列表
trueIpList: string[] = []; // 正确Ip列表
falseIpList: string[] = []; // 错误IP列表
hasFalseIp = false; // 控制是否有错误IP,如果有的话,隐藏安装按钮
IpReg = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
constructor() {}
ngOnInit() {
this.ipCheck(this.allAgentIPArray);
}
// 这里是做一个空函数体,真正使用的方法在 registerOnChange 中
// 由框架注册,然后我们使用它把变化发回表单
// 注意,和 EventEmitter 尽管很像,但发送回的对象不同
private propagateChange = (_: any) => {};
// 写入控件值,调用者通过setValue写入的位置
public writeValue(obj: string | null) {
if (obj !== null && obj !== "") {
this.classIpMain(obj);
}
}
// 当表单控件值改变时,函数 fn 会被调用
// 这也是我们把变化 emit 回表单的机制
public registerOnChange(fn: any) {
this.propagateChange = fn;
}
// 验证表单,验证结果正确返回 null, 否则返回一个验证结果对象
public validate() {
return !this.hasFalseIp && this.trueIpList.length > 0
? null
: {
hasFalseIp: {
valid: false
}
};
}
// 这里没有使用,用于注册 touched 状态
public registerOnTouched() {}
// IP分类函数,入口为字符串,输出是正确,错误,全部IP
classIpMain(ipString: string) {
const list = this.ipCut(ipString);
const removeEmpty = compact(list);
this.ipCheck(removeEmpty);
}
// IP分割
ipCut(ipString: string) {
return ipString.split(/[\s\n,]/);
}
// IP分类
ipCheck(listArr: string[]) {
listArr.forEach(current => {
if (this.IpReg.test(current)) {
this.addTrueList(current);
} else {
this.addFalseList(current);
}
});
this.upDataAllIpList();
}
// 更新总IP列表 + 更新一些状态值
upDataAllIpList() {
this.allIpList = concat(this.trueIpList, this.falseIpList);
this.hasFalseIp = this.falseIpList.length > 0;
this.propagateChange(this.trueIpList);
}
// 清空IP列表
clearAllIpList() {
this.trueIpList = [];
this.falseIpList = [];
this.upDataAllIpList();
}
// 添加进正确列表
addTrueList(ip: string) {
this.trueIpList.unshift(ip);
this.trueIpList = uniq(this.trueIpList);
}
// 添加进错误列表
addFalseList(ip: string) {
this.falseIpList.unshift(ip);
this.falseIpList = uniq(this.falseIpList);
}
// 删除错误Ip
deleteFalseIp(fIp: string) {
this.falseIpList = difference(this.falseIpList, [fIp]);
this.upDataAllIpList();
}
// 删除正确Ip
deleteTrueIp(tIp: string) {
this.trueIpList = difference(this.trueIpList, [tIp]);
this.upDataAllIpList();
}
// 编辑错误Ip列表
editIp(oldVal: string, newVal: string) {
if (newVal === oldVal || newVal === "") {
return false;
}
if (this.IpReg.test(newVal)) {
this.addTrueList(newVal);
} else {
this.addFalseList(newVal);
}
this.deleteFalseIp(oldVal);
this.deleteTrueIp(oldVal);
}
}