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

相关推荐
陆枫Larry1 小时前
Astro 是什么
前端
是立不是利3 小时前
写给设计师的 CSS 博客美学指南
前端·css
默_笙3 小时前
🏝 Docker 就是"房地产开发":从施工图纸到小区物业的容器化指南
前端·javascript
计算机魔术师3 小时前
Rohan Paul 谈用时间跨度衡量智能体能力,并引用 OpenAI 自动化研究实习生里程碑
前端
kyriewen3 小时前
我手写了个极简版 React Router——才搞懂 v6 为什么砍了这么多 API
前端·javascript·程序员
a1117764 小时前
Shirone 二次元博客主题 开源
前端·开源
IT_陈寒4 小时前
Python的切片赋值把我坑惨了,这不是bug是特性
前端·人工智能·后端
掘金酱4 小时前
【社区公告】致每一位掘友:关于这次调整, 想再说几句
前端·人工智能
CoderLiu4 小时前
程序化工具调用(PTC)与动态工作流引擎:深入大模型工具调用的架构演进与实践
前端·人工智能·后端
码农胖大海4 小时前
我的第一个产品,只有一段提示词
前端·ai编程·产品