angular自定义form表单元素-checkList

2023-11-10

实际使用form的时候,最外层的form的某个表单元素可能是个组合的,这种情况如果是可多场景复用的,最好封装一个表单元素。本文以组合复选框为例来说明下自定义表单元素的过程

实现效果

展示效果

在这里插入图片描述
在这里插入图片描述

html

<form nz-form [formGroup]="form">
  <nz-form-item nz-row>
    <nz-form-label style="width: 120px;" nzRequired>{{ label }}</nz-form-label>
    <nz-form-control [nzSpan]="18" [nzErrorTip]="'至少选择一个类型'">
      <app-checkbox
        [data]="checkList"
        formControlName="type"
      ></app-checkbox>
    </nz-form-control>
  </nz-form-item>
  <div nz-row>
    <div nz-col [nzSpan]="18" style="margin-left: 120px;">
      <button nz-button nzType="primary" (click)="onVerify()">校验</button>
    </div>
  </div>
</form>
<div>
  {{formValueStr}}
</div>

ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormControl, FormGroup } from '@angular/forms';
import { FormUtils } from '../../utils/form-utils';
import { CheckItem } from '../checkbox/checkbox.component';

@Component({
  selector: 'app-form-test',
  templateUrl: './form-test.component.html',
  styleUrls: ['./form-test.component.scss']
})
export class FormTestComponent implements OnInit {

  form!: FormGroup;
  label = '支出类型';
  checkList: CheckItem[] = [
    { label: '交通', value: '1' },
    { label: '日常', value: '2' },
    { label: '旅游', value: '3' },
    { label: '理财', value: '4' }
  ];
  get formValueStr(): string{
    return JSON.stringify(this.form.value);
  }

  constructor(
    private fb: FormBuilder,
  ) { }

  ngOnInit(): void {
    this.form = this.fb.group({
      type: [
        ['1'],
        (c: FormControl) => {
          console.log('value', c.value);
          return c.value && c.value.length > 0 ? null : { required: '123' };
        }
      ]
    });
  }

  onVerify(): void {
    FormUtils.verifyState(this.form);
  }
}

用到的API

ControlValueAccessor - 定义一个接口,该接口充当 Angular 表单 API 和 DOM 中的原生元素之间的桥梁。

interface ControlValueAccessor {
  // 当请求从模型到视图的编程更改时,表单 API 会调用此方法以写入视图。
  writeValue(obj: any): void

  // 注册一个回调函数,该控件的值在 UI 中更改时将调用该回调函数。
  registerOnChange(fn: any): void

  // 注册一个在初始化时由表单 API 调用的回调函数,以在失焦时更新表单模型。
  registerOnTouched(fn: any): void

  // 当控件状态更改为 “DISABLED” 或从 “DISABLED” 更改时,表单 API 要调用的函数。
  // 根据其状态,它会启用或禁用适当的 DOM 元素。
  setDisabledState(isDisabled: boolean)?: void
}

checkList组件

html

<div [formGroup]="form">
  <label
      *ngFor="let item of data;let i = index;"
      nz-checkbox
      [nzValue]="item.value"
      [formControlName]="i"
    >{{ item.label }}</label>
</div>

ts

import { ChangeDetectionStrategy, Component, forwardRef, Input, OnInit } from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms';
export interface CheckItem {
  label: string;
  value: string;
  checked?: boolean;
}

@Component({
  selector: 'app-checkbox',
  templateUrl: './checkbox.component.html',
  styleUrls: ['./checkbox.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CheckboxComponent),
      multi: true,
    },
  ],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckboxComponent implements ControlValueAccessor, OnInit {

  @Input() data: CheckItem[] = [];
  form: FormGroup = new FormGroup({});

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

  constructor(
    private fb: FormBuilder,
  ) { }

  reGroupValues(values: CheckItem[]) {
    let data: any[] = this.data.map((item, index) => {
      return {
        ...item,
        checked: values[index],
      };
    }).filter(item => item.checked).map(item => item.value);
    return data;
  }

  resetCpValue(values: any) {
    const cpValue: any = {};
    this.data.forEach((item, index) => {
      cpValue[index] = values.indexOf(item.value) > -1 ? true : false;
    });
    return cpValue;
  }

  ngOnInit() {
    const options: any = {};
    this.data.forEach((item, index) => {
      options[index] = [!item.checked ? false : true];
    });
    this.form = this.fb.group(options);
    // 监听控件的每一个元素change事件,然后调用 _onChange
    this.form.valueChanges.subscribe(values => {
      this._onChange(this.reGroupValues(values));
    });
  }

  // 向控件form中写入values-form通过setValue,patchValue给form赋值的时候
  writeValue(values: any) {
    if (!values) {
      return;
    }
    const cpValue: any = {};
    this.data.forEach((item, index) => {
      cpValue[index] = values.indexOf(item.value) > -1 ? true : false;
    });
    this.form.setValue(cpValue);
  }

  // 注册空间change事件-用于外部form对该控件的监听
  registerOnChange(fn: any) {
    this._onChange = fn;
  }

  registerOnTouched(fn: any) {
    // 此组件是否触碰可以不设置,无实际意思
    // 如果是封装的输入元素可以设置此操作,并在组件中某些输入blur事件中调用 _onTouched 来进行触发
    // eg this._onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    // 因为当前组件为check的组合,每个元素都可以进行disable设置,故此可以调用form的方法
    if (isDisabled) {
      this.form.disable();
    } else {
      this.form.enable();
    }
  }
}

至此可以实现本文开头截图中的效果

FormUtils工具

import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms';

export enum MarkType {
  DIRTY = 'dirty',
  PRISTINE = 'pristine',
}

export class FormUtils {
  static markAs(control: AbstractControl, markType: MarkType) {
    if (control instanceof FormControl && !control.dirty) {
      control.markAsDirty();
      control.updateValueAndValidity();
    } else if (control instanceof FormArray) {
      control.controls.forEach(item => {
        this.markAs(item, markType);
      });
    } else if (control instanceof FormGroup) {
      Object.keys(control.controls).forEach(key => {
        this.markAs(control.controls[key], markType);
      });
    }
  }

  static verifyState(control: AbstractControl) {
    FormUtils.markAs(control, MarkType.DIRTY);
  }

  static resetState(control: AbstractControl) {
    FormUtils.markAs(control, MarkType.PRISTINE);
  }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

angular自定义form表单元素-checkList 的相关文章

随机推荐

  • 使用vector对数据进行排序(动态排序)

    排序思路 头函数 algorithm 中有一个函数是 upper bound start end value 它可以返回区间 start end 中第一个大于等于 value 的值的位置 再加上 vector 中自带的插入函数 insert
  • 电脑安装了lattice diamond后,再安装lattice radiant,若出现lattice radiant license checkout failed如何解决?

    我电脑安装了lattice diamond 再安装lattice radiant 设置完环境变量后 发现lattice radiant仍然会报错 如下图 检查环境变量和license都并没有什么错误 但是就是一直会出现以下情况 后面如何解决
  • UML 类图

    1 概述 目录 1 概述 1 1 UML概念 1 2 类图的概念 2 类的表示方式 2 1 普通类 2 2 抽象类 2 3 接口 3 类与类关系的表示 3 1 关联关系 Association 3 1 1 单向关联 3 1 2 双向关联 3
  • 【小程序】如何实现从底部弹出对话框

    前面两篇两篇文章介绍了如何在小程序中实现上下滑动效果以及如何用 Canvas 绘制一张图片 这一篇作为前两篇的延续 介绍如何从底部弹出一个对话框 相比而言 底部弹出对话框的功能比较通用 因此非常适合定义成组件 component 先来看一下
  • 【学习记录贴】Vue+Element-UI富文本编辑框及插入图片

    本贴会涉及以下几个技术点 Vue Element UI实现富文本编辑框 以及文本编辑框中事件拦截 插入图片 Element UI限制上传图片后 隐藏上传按钮 官网上是没有这个方法的 可以通过上传到指定张数后隐藏上传按钮来实现 form表单验
  • MyBatis-Plus删除操作知识点总结

    系列文章目录 Mybatis Plus知识点 MyBatis MyBatis Plus的基础运用 心态还需努力呀的博客 CSDN博客 Mybatis Plus SpringBoot结合运用 心态还需努力呀的博客 CSDN博客MyBaits
  • VScode自由切换输出结果窗口,输出到“终端”和“调试控制台”

    Author xiaozhu sai 软件 Visual Studio Code 点击右边的齿轮按钮 打开launch json文件 注意 console 属性即可 具体见一下代码 使用 IntelliSense 了解相关属性 悬停以查看现
  • C++ sort函数自定义排序规则

    在使用vector容器时经常要进行排序 使用排序函数sort非常方便 但是之前都是简单调用sort v begin v end 没有自定义排序规则使用sort函数的额第三个参数 下面对sort总一个简单总结 头文件 include
  • 计算机网络第2章(物理层)

    B站视频 计算机网络微课堂 有字幕无背景音乐版 网址 https www bilibili com video BV1c4411d7jb p 61 目录 2 1 物理层的基本概念 2 2 物理层下面的传输媒体 导引型传输媒体 非导引型传输媒
  • Vue弹窗传值

    场景 点击新增后 需要将这个页面的分类Id传到弹窗页面 新增的时候绑定这个分类 步骤 1 列表页面中弹窗标签中绑定 classifyId this classify
  • 演唱会为什么总是抢不到票?用Python做一个自动抢票脚本!想看谁的就看谁的!

    大麦网 是中国综合类现场娱乐票务营销平台 业务覆盖演唱会 话剧 音乐剧 体育赛事等领域 但是因为票数有限 还有黄牛们不能丢了饭碗 所以导致了 很多人都抢不到票 那么 今天带大家用Python来制作一个自动抢票的脚本小程序 文章末尾看运行效果
  • Java 基于文本界面的《员工管理系统》

    一 代码实现 1 设计分析 该管理系统使用了5个包 Package 类似于文件夹 1 bean 包含员工类 Employee 2 main 主程序的入口 3 service 主要是 业务逻辑层 的功能实现 4 util 存放工具类 此处存放
  • 【springmvc系】利用RequestBodyAdviceAdapter做接口鉴权

    需求 有个简单的需求 对于第三方接口我们需要做个简单的鉴权机制 这边使用的是非对称性加密的机制 我们提供三方公钥 他们通过公钥对接口json报文使用加密后的报文请求 我们通过对接收过来的请求某一个加密报文字段来进行RSA解密校验 考虑到日后
  • hashmap原理_HashMap原理jdk7和jdk8的区别

    一 hashMap的jdk1 7和jdk1 8区别 1 实现方式 jdk7中使用数组 链表来实现 jdk8使用的数组 链表 红黑树 2 新节点插入到链表是的插入顺序不同 jdk7插入在头部 jdk8插入在尾部jdk7中 如何在头部插入 看a
  • [Hadoop3.3.1]:Unable to load native hadoop library for your platform

    需求 linux已经启动了hadoop集群 想要在windows中用java对文件进行下载操作 错误提示 找不到winutils exe hadoop dll没有设置原因 Hadoop访问windows本地文件系统 要求Windows上的本
  • SQL Server数据库进阶

    批处理 将多条SQL语句作为一个整体去编译 生成一个执行计划 然后执行 为了将一个脚本分为多个批处理 可使用GO语句 GO语句的特点 GO语句必须自成一行 只有注释可以在同一行上 它使得自脚本的开始部分或者最近一个GO语句以后的所有语句编译
  • elementUI中,实现一个单元格内显示两行数据,并用其中一个数据进行排序。

    最近在公司中 有这样一个需求 表格中 一个单元格里面显示两行数据 并且可以使用其中一行进行排序 其中数据的样式也要实时变动 类似于下图 这样的话 elementUI中自带的prop就不适合了 所以 需要展示两行数据的地方 我们就用插槽来解决
  • 重叠社区发现-UEOC算法(unfold and extract overlapping communities)学习笔记

    本文提出了一种基于马尔可夫动力学模型的发现节点共享社区的算法UEOC 在UEOC方法中 为了检测出所有的自然群落 将马尔可夫随机游动方法与一种新的约束策略相结合 该策略基于相应的退火网络 21 用于展开每个群落 然后 利用一个借助电导的截止
  • 【Python编程入门】环境搭建

    作为一门跨平台的高级编程语言 Python可以运行在几乎所有主流的操作系统中 这也意味着 只要我们在本机电脑安装配置完Python环境后 便可以轻松愉快的学习Python语言了 这是一门值得大部分人学习的计算机编程语言知识 关于Python
  • angular自定义form表单元素-checkList

    实际使用form的时候 最外层的form的某个表单元素可能是个组合的 这种情况如果是可多场景复用的 最好封装一个表单元素 本文以组合复选框为例来说明下自定义表单元素的过程 实现效果 展示效果 html