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

相关推荐
Vicky&James8 分钟前
WPF到Web的无缝过渡:英雄联盟客户端项目OpenSilver迁移实战
前端·wpf
m0_7482336411 分钟前
RabbitMQ 进阶
android·前端·后端
不想有bug的小菜鸟20 分钟前
vue3使用iframe全屏展示pdf效果
前端·pdf
m0_7482386320 分钟前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
u01005596021 分钟前
前端代理,解决跨域问题讲解
前端
quitv26 分钟前
react脚手架配置别名
前端·javascript·react.js
m0_5287238135 分钟前
前端如何进行性能优化
前端·性能优化
化作繁星37 分钟前
在 Vue 3 中,如何缓存和复用动态组件
前端·vue.js·缓存
一粒沙-1 小时前
iOS 将GIF图分享至微信
前端·ios
graywen1 小时前
从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
前端