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

相关推荐
Json_181790144801 小时前
电商拍立淘按图搜索API接口系列,文档说明参考
前端·数据库
风尚云网1 小时前
风尚云网前端学习:一个简易前端新手友好的HTML5页面布局与样式设计
前端·css·学习·html·html5·风尚云网
木子02041 小时前
前端VUE项目启动方式
前端·javascript·vue.js
GISer_Jing1 小时前
React核心功能详解(一)
前端·react.js·前端框架
捂月1 小时前
Spring Boot 深度解析:快速构建高效、现代化的 Web 应用程序
前端·spring boot·后端
深度混淆2 小时前
实用功能,觊觎(Edge)浏览器的内置截(长)图功能
前端·edge
Smartdaili China2 小时前
如何在 Microsoft Edge 中设置代理: 快速而简单的方法
前端·爬虫·安全·microsoft·edge·社交·动态住宅代理
秦老师Q2 小时前
「Chromeg谷歌浏览器/Edge浏览器」篡改猴Tempermongkey插件的安装与使用
前端·chrome·edge
滴水可藏海2 小时前
Chrome离线安装包下载
前端·chrome
m51272 小时前
LinuxC语言
java·服务器·前端