angular form 自定义组件

angular 表单里面需要使用 自定义组件时,想要方便的使用formGroupformControlName,需要自定义组件的formControlName属性, 如何去自定义组件的formControlName属性呢?

引入ControlValueAccessor 接口

ControlValueAccessor 接口是angular 表单组件的formControlName属性的依赖注入,ControlValueAccessor 接口需要实现registerOnChangeregisterOnTouchedsetDisabledStatewriteValue方法, 我们这里不需要用到表单的disabled属性,所以可以忽略setDisabledState

我们新建input-number组件 引入NG_VALUE_ACCESSOR, 并写出对应的registerOnChangeregisterOnTouchedwriteValue方法

ts 复制代码
import { Component, OnInit, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-input-number',
  template: `
    <div>
      <span>{{ count }}</span>
    </div>
  `,
  styleUrls: ['./input-number.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputNumberComponent),
      multi: true,
    },
  ],
})
export class InputNumberComponent implements OnInit {
  /**
   * formControlName对应的表单值会在此传入
   */
  writeValue(value: any) {
    if (value) {
      this.count = value;
    }
  }

  /**
   * form表单的更新函数会在此传入,需要调用此函数更新表单的值
   */
  registerOnChange(fn: any) {}

  registerOnTouched(fn: any) {}
}

定义 updateValue 函数

新建 updateValue函数,并且在incrementdecrementwriteValue函数中调用updateValue函数,更新表单的值 该组件显示数字,并且可以点击增加和减少按钮

ts 复制代码
import { Component, OnInit, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-input-number',
  template: `
    <div>
      <button (click)="decrement()">-</button>
      <span>{{ count }}</span>
      <button (click)="increment()">+</button>
    </div>
  `,
  styleUrls: ['./input-number.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputNumberComponent),
      multi: true,
    },
  ],
})
export class InputNumberComponent implements OnInit {
  /**
   * formControlName对应的表单值会在此传入
   */
  writeValue(value: any) {
    if (value) {
      this.count = value;
      this.updateValue(this.count);
    }
  }

  updateValue: (value: any) => void = (_: any) => {};
  updateValueByTouched: (value: any) => void = (_: any) => {};

  /**
   * form表单的更新函数会在此传入,需要调用此函数更新表单的值
   */
  registerOnChange(fn: any) {
    this.updateValue = fn;
  }

  registerOnTouched(fn: any) {
    this.updateValueByTouched = fn;
  }

  increment() {
    this.count++;
    this.updateValue(this.count);
  }

  decrement() {
    this.count--;
    this.updateValue(this.count);
  }
}

为了不在每次更新count值的时候,都去手动调用updateValue函数,我们更新一下数据结构,让其在 count 值改变时,自动调用updateValue函数

ts 复制代码
import { Component, OnInit, forwardRef } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-input-number',
  template: `
    <div>
      <button (click)="decrement()">-</button>
      <span>{{ count }}</span>
      <button (click)="increment()">+</button>
    </div>
  `,
  styleUrls: ['./input-number.component.css'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputNumberComponent),
      multi: true,
    },
  ],
})
export class InputNumberComponent implements OnInit {
  _count: number = 0;

  get count() {
    return this._count;
  }

  set count(value: number) {
    this._count = value;
    this.updateValue(this._count);
  }

  constructor() {}

  ngOnInit() {}

  writeValue(value: any) {
    if (value) {
      this.count = value;
    }
  }

  updateValue: (value: any) => void = (_: any) => {};
  updateValueByTouched: (value: any) => void = (_: any) => {};

  registerOnChange(fn: any) {
    this.updateValue = fn;
  }

  registerOnTouched(fn: any) {
    this.updateValueByTouched = fn;
  }

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}

至此,我们已经写好了自定义的 form 组件

使用自定义组件

响应式表单

下面我们在 form 表单里引入自定义组件,试试看效果

ts 复制代码
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <form [formGroup]="numberForm">
        <app-input-number formControlName="count"></app-input-number>

        <div>{{ numberForm.get('count')?.value }}</div>
      </form>
    </div>
  `,
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  numberForm = this.fb.group({
    count: 5, // 设置初始值
  });

  constructor(private fb: FormBuilder) {}
}

我们在组件下面显示出对应的 form 的表单值,可以看到,我们的自定义组件input-number已经生效了

模板驱动式表单

上面的例子我们是使用 formControlName, 下面我们使用ngModel同样可以

ts 复制代码
import { Component } from '@angular/core';
import { FormBuilder } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <app-input-number [(ngModel)]="count"></app-input-number>
      <div>{{ count }}</div>
    </div>
  `,
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  count = 5;

  constructor(private fb: FormBuilder) {}
}

源码

angular-custom-form-example

相关推荐
胡西风_foxww5 分钟前
【es6复习笔记】rest参数(7)
前端·笔记·es6·参数·rest
m0_748254887 分钟前
vue+elementui实现下拉表格多选+搜索+分页+回显+全选2.0
前端·vue.js·elementui
星就前端叭1 小时前
【开源】一款基于Vue3 + WebRTC + Node + SRS + FFmpeg搭建的直播间项目
前端·后端·开源·webrtc
m0_748234521 小时前
前端Vue3字体优化三部曲(webFont、font-spider、spa-font-spider-webpack-plugin)
前端·webpack·node.js
Web阿成1 小时前
3.学习webpack配置 尝试打包ts文件
前端·学习·webpack·typescript
jwensh2 小时前
【Jenkins】Declarative和Scripted两种脚本模式有什么具体的区别
运维·前端·jenkins
关你西红柿子2 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
益达是我2 小时前
【Chrome】浏览器提示警告Chrome is moving towards a new experience
前端·chrome
济南小草根2 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
聪小陈2 小时前
圣诞节:记一次掘友让我感动的时刻
前端·程序员