angular select2源码解析_Angular 组件库 NG-NEST 源码解析:Form 表单组件
前言
NG-NEST介绍
今天我们来看一下 Form 表单组件是如何实现的:
功能分析
- 由不同的表单控件组成(输入框、选择器、单选框、多选框等)
- 控件的禁用、必填、正则验证等状态
- 标题和控件位置,局部分类,栅格布局等
代码分析
lib/ng-nest/ui/form
├── docs md 文档
├── examples 示例
├── style 样式 @mixin 和 样式参数定义
├── control.component.html
├── control.component.ts 控件组件(映射不同类型的表单组件)
├── form.component.html
├── form.component.scss
├── form.component.ts
├── form.component.spec.ts 测试文件,测试模式下直接访问对应的关键子调试单个组件
├── form.module.ts 组件模块,声明模块中的视图,依赖的模块,以及导出的视图
├── form.property.ts 组件属性( Input 输入和 Ouput 输出参数),以及相关的类型定义,文档中的组件参数说明通过此处生成
├── index.ts
├── package.json ng-packagr 配置,单独引入组件
└── public-api.ts 组件文件以及API
我们先看 form.component.html 文件:
<!--
使用 formGroup 指令指定一个表单对象,并指定相关样式
controlsType 用来区分 controls 中是否包含了行的分类,通过 ngSwitch 来指定不同的模板
-->
<form#formclass="x-form"[formGroup]="formGroup"[style.width]="width"[style.padding-bottom.rem]="controlsType === 'controls' ? this.space : 0"[ngClass]="classMap"
><ng-container [ngSwitch]="controlsType"><ng-container *ngSwitchCase="'controls'"><ng-container *ngTemplateOutlet="controlsTemp; context: { controls: controls }"> </ng-container></ng-container><ng-container *ngSwitchCase="'rows'"><ng-container *ngTemplateOutlet="rowsTemp; context: { rows: controls }"></ng-container></ng-container></ng-container>
</form><!--
行模板,先加载行分类,在加载行中对应的控件
通过行参数 hidden 可以隐藏整行
-->
<ng-template #rowsTemp let-rows="rows"><ng-container *ngFor="let row of rows"><x-row [hidden]="row.hidden"><ng-container *ngTemplateOutlet="titleTemp; context: { row: row }"></ng-container><ng-container *ngTemplateOutlet="controlsTemp; context: { controls: row.controls }"></ng-container></x-row></ng-container>
</ng-template><!-- 行中控件 -->
<ng-template #controlsTemp let-controls="controls"><x-row [space]="space"><x-col[style.padding-top.rem]="space"[span]="!control.span ? span : control.span"*ngFor="let control of controls"[hidden]="control.hidden"><x-control [option]="control"></x-control></x-col></x-row>
</ng-template><!-- 行标题模板 -->
<ng-template #titleTemp let-row="row"><label class="x-form-title"><x-icon *ngIf="row.icon" [type]="row.icon"></x-icon><span>{{ row.title }}</span></label>
</ng-template>
对应的 form.component.ts 文件:
export class XFormComponent extends XFormProperty implements OnInit {// 用来判断传递的控件参数中是否包含行controlsType: 'controls' | 'rows';// 用来存储所有的控件组件controlComponents: { [property: string]: XFormControlComponent } = {};// 用来存储所有的控件类型controlTypes: { [property: string]: XFormControlType } = {};constructor(public cdr: ChangeDetectorRef, public configService: XConfigService) {super();}ngOnChanges(changes: SimpleChanges) {XIsChange(changes.disabled) && this.setDisabled();}ngOnInit() {this.setControls();this.setClassMap();}ngAfterViewInit() {this.setDisabled();}setControls() {if (this.controls && this.controls.length > 0) {this.controlsType = this.controls[0].controls ? 'rows' : 'controls';}}setClassMap() {this.classMap[`${XFormPrefix}-${this.controlsType}`] = true;}// 通过表单禁用/启用所有控件,并处理相关的必填、正则验证setDisabled() {if (Object.keys(this.controlComponents).length === 0) return;if (this.disabled) {for (let key in this.controlComponents) {let [control, type] = [this.controlComponents[key], this.controlTypes[key]];control.disabled = true;control.required = false;delete control.pattern;type.setValidators();control.formControlChanges();}} else {for (let key in this.controlComponents) {let [control, type] = [this.controlComponents[key], this.controlTypes[key]];control.disabled = type.disabled as XBoolean;control.required = type.required as XBoolean;control.pattern = type.pattern as RegExp | RegExp[];type.setValidators();control.formControlChanges();}}this.formGroup.updateValueAndValidity();}// 获取表单中控件的验证信息getValidatorMessages(): string[] {let result: string[] = [];if (this.formGroup.valid) return result;else {const eachControls = (array: XFormControlOption[]) => {for (const ctr of array) {const formCtr = this.formGroup.controls[ctr.id] as XFormControl;if (formCtr && formCtr.invalid) {result = [...result, ...(formCtr.messages as string[])];}}};if (this.controlsType === 'rows') {for (const row of this.controls as XFormRow[]) {eachControls(row.controls);}} else {eachControls(this.controls as XFormControlOption[]);}}return result;}
}
form 组件主要是用来组织表单结构,并提供了全局禁用的方式以及获取验证信息的方法。
接下来我们看控件组件 control.component.html :
<div class="x-control" [formGroup]="form?.formGroup"><ng-container [ngSwitch]="option?.control"><ng-container *ngSwitchCase="'input'"><x-input [formControlName]="option.id" (clearEmit)="option.clearClick && option.clearClick($event)"></x-input></ng-container><ng-container *ngSwitchCase="'select'"><x-select [formControlName]="option.id"></x-select></ng-container>......<ng-container *ngSwitchCase="'find'"><x-find [formControlName]="option.id"></x-find></ng-container></ng-container>
</div>
- form 对应我们的表单父组件
- 使用 ngSwitch 来指定不同的组件
- 在使用各种组件的时候只定义了 formControlName 的名字,并且只对输出参数做了处理,输入参数在对应的 ts 文件里面直接映射进去
export class XControlComponent extends XControlProperty implements OnInit, AfterViewInit, OnDestroy {// 控件参数@Input() option: XFormControlOption;// 通过 FormControlName 获取对应的控件@ViewChild(FormControlName, { static: false }) control: FormControlName;// 共享属性,可以通过表单参数只指定一次,单个控件中不需要再指定private _sharedProps = ['span', 'direction', 'justify', 'align', 'labelWidth', 'labelAlign'];// 改变的属性,此处可以调用参数中的 change 事件来触发组件的更新 private _changeProps = ['label', ...this._sharedProps];// 控件类型private _control: XFormControlType;// 验证类型private _validatorFns: ValidatorFn[] = [];private _unSubject = new Subject();// 根据 option 创建的 FormControl 对象 private _formControl: FormControl;constructor(@Host() @Optional() public form: XFormComponent, public cdr: ChangeDetectorRef, public configService: XConfigService) {super();}ngOnInit() {// 共享属性设置this.setProps();// label 后缀设置this.option.label = `${this.option.label}${this.form.labelSuffix}`;// 创建控件类型this._control = this.createControl(this.option);// 创建控件对象this._formControl = new FormControl(this._control.value);// 验证设置this.setValidators();// 订阅状态变化事件,用来设置显示信息(主要正则验证或错误信息)this._formControl.statusChanges.pipe(takeUntil(this._unSubject)).subscribe((x) => {this.setMessages(x);});// 定义类型中的验证事件this._control.setValidators = () => this.setValidators();// formGroup 中添加表单对象this.form.formGroup.addControl(this._control.id, this._formControl);// 定义参数中的 change 事件this.option.change = () => {this._changeProps.forEach((x: string) => {if (this.control.valueAccessor && this.option[x]) {(this.control.valueAccessor as any)[x] = this.option[x];}});this.form.controlComponents[this._control.id].formControlChanges();};}ngAfterViewInit() {// 映射具体控件中的输入参数// valueAccessor 指向我们事件的表单控件对象,比如 XInputComponent、XSelectComponentObject.assign(this.control.valueAccessor, this._control);// 把当前控件注册到 form 父组件中,并执行对应组件改变事件this.form.controlTypes[this._control.id] = this._control;this.form.controlComponents[this._control.id] = this.control.valueAccessor as XFormControlComponent;this.form.controlComponents[this._control.id].formControlChanges();}ngOnDestroy() {this._unSubject.next();this._unSubject.unsubscribe();}// 控件验证setValidators() {this._validatorFns = [];// 禁用if (this._control.disabled || this.form.disabled) {this._formControl.disable();} else {this._formControl.enable();}// 必填if (this._control.required && !this.form.disabled) {this._validatorFns = [...this._validatorFns, Validators.required];}// 正则if (this._control.pattern) {this.setPattern();}// 重新设置并更新验证信息this._formControl.setValidators(this._validatorFns);this._formControl.updateValueAndValidity();}// 设置共享属性setProps() {for (let prop of this._sharedProps) {if (XIsEmpty(this.option[prop])) this.option[prop] = (this.form as any)[prop];}}// 设置正则验证setPattern() {if (Array.isArray(this._control.pattern)) {for (const pt of this._control.pattern) {this._validatorFns = [...this._validatorFns, Validators.pattern(pt)];}} else {this._validatorFns = [...this._validatorFns, Validators.pattern(this._control.pattern as RegExp)];}}// 设置正则验证的信息getPatternMsg(pattern: string) {if (Array.isArray(this._control.pattern)) {return (this._control.message as Array<any>)[this._control.pattern.findIndex((x) => String(x) === pattern)];} else {return this._control.message;}}setMessages(state: 'INVALID' | 'VALID' | 'DISABLED') {let control: XFormControl = this._formControl;if (state === 'INVALID' && this._formControl.errors !== null) {for (const key in control.errors) {if (key === 'required') {control.messages = [`${this._control.label} 必填`];} else if (key === 'pattern') {control.messages = [`${this._control.label} ${this.getPatternMsg(control.errors[key].requiredPattern)}`];}}} else if (state === 'VALID') {control.messages = [];}}// 创建对应类型的控件createControl(option: XFormControlOption) {switch (option.control) {case 'input':return new XInputControl(option as XInputControlOption);case 'select':return new XSelectControl(option as XSelectControlOption);case 'checkbox':return new XCheckboxControl(option as XCheckboxControlOption);case 'radio':return new XRadioControl(option as XRadioControlOption);case 'switch':return new XSwitchControl(option as XSwitchControlOption);case 'rate':return new XRateControl(option as XRateControlOption);case 'date-picker':return new XDatePickerControl(option as XDatePickerControlOption);case 'time-picker':return new XTimePickerControl(option as XTimePickerControlOption);case 'input-number':return new XInputNumberControl(option as XInputNumberControlOption);case 'slider-select':return new XSliderSelectControl(option as XSliderSelectControlOption);case 'cascade':return new XCascadeControl(option as XCascadeControlOption);case 'color-picker':return new XColorPickerControl(option as XColorPickerControlOption);case 'find':return new XFindControl(option as XFindControlOption);default:return new XInputControl(option as XInputControlOption);}}
}
总结
以上就是 Form 表单组件的解析,控件组件主要用来映射具体的表单组件(输入框、选择框、多选框、单选框等),并在此基础上提供了通用属性以及方法。
下一次将介绍 国际化 实现原理:
欢迎 Star 了解最新信息
https://github.com/NG-NEST/ng-nestgithub.com
angular select2源码解析_Angular 组件库 NG-NEST 源码解析:Form 表单组件相关推荐
- 【实习小tip】多层dialog弹窗遮罩问题、elementUI的form表单组件的select框在只读的情况下没办法拿到传来的数据、从弹窗子组件获取数据后需要刷新页面
解决elementui多层dialog弹窗遮罩问题 弹窗套娃出现了整个屏幕都是遮罩层的问题,需要鼠标点击一下才能正常. 在弹窗组件代码上加上 append-to-body 就可以了,表示这个弹窗是嵌在 ...
- 使用vue表单验证库async-validator封装Form表单组件
src/components/data/seller/create/contract.vue <template><create-portlet title="合同信息&q ...
- Django Form表单组件
Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否 ...
- 通过json配置生成form表单,vue3+ts+elementPlus,form表单组件封
子组件 //src\components\form\index.vue <script setup lang="ts"> import { PropType, ref, ...
- 如何用JavaScript操作form表单组件?
一.用JavaScript操作按钮: <!DOCTYPE html> <html><head><meta charset="UTF-8"& ...
- Django之form表单组件、cookie与session
---恢复内容开始--- Form表单组件 引例: 先来看一个注册的例子,全部用的是reg函数来实现的. views.py文件 def reg(request):errors = {'username ...
- 从Antd 源码到自我实现之 Form表单
前言 Antd 中的组件大部分基于蚂蚁金服的组件库 react-component.antd 与 react-component 都是开源项目,阅读其源码可以给我们带来很多收益,比如: 了解各式各样的 ...
- UI组件库Form表单_数字类型验证之坑实现数字框
目录 Input 输入框 实现数字框封装使用 项目需求 : 使用的饿了么组件库的 input 框 , 但是想要 实现用户只能输入 数字 的功能 , So 看到了数字类型的验证 (缺点 :不能阻止用户输 ...
- 基于dumi和antd封装的易用组件库(每个人都应该有自己的组件库)
首先说下哈,不是造轮子,从来也不提倡造轮子- 作为一名前端程序员,个人感觉轮子已经够多了,留下来的时间不如摸鱼,比如发发沸点.逛逛掘金,或者下班了陪陪女朋友 最近发现了一个好的工具 dumi 突然心血 ...
最新文章
- oracle11g分区表按时间自动创建
- 【转载】Unix编程艺术——Unix哲学
- 查看oracle自定义函数,Oracle自定义函数查询数据字典项
- python 将实例用作属性_将类实例用作类属性、描述符和属性
- 缓存-分布式锁-Redisson简介整合
- 个人企业作品网站导航页源码
- 题目1198:a+b
- 安装brew_MacBook Pro安装Homebrew慢的问题解决方案
- Linux中用yum安装MySQL方法
- oracle database 10g rman备份与恢复pdf,Oracle Database10g RMAN备份与恢复
- 华为21天云计算培训
- SECS半导体通信委员会参考书
- iMazing v2021绿色便携版iOS设备数据管理工具
- 蓝牙相关学习:5.BLE协议属性协议层(ATT)
- python 学术论文,python论文_python 论文_python
- python xgb模型 预测_如何使用XGBoost模型进行时间序列预测
- ssh mysql jsp码头船只出行及配套货柜码放管理系统的设计与实现
- 【SDN】软件定义硬件
- eclipse配置opencv和javacv环境
- [从头读历史] 第275节 诗经 秦风