Angular使用ControlValueAccessor创建自定义表单控件

下面是详细讲解"Angular使用ControlValueAccessor创建自定义表单控件"的完整攻略。

概述

在 Angular 应用中,表单控件是很常见的组件,但是有时候我们需要自定义一些表单控件,如何实现呢?

Angular 提供了一种轻松自定义表单控件的方式,使用 ControlValueAccessor 接口。

ControlValueAccessor 是一个接口,它为我们自定义表单控件提供了规范和顺畅的用户体验。

接下来我们将介绍如何通过 ControlValueAccessor 接口来创建自定义表单控件。

步骤

第一步:定义自定义表单控件

我们需要定义一个自定义表单控件,这个控件需要实现 ControlValueAccessor 接口。

最简单的示例就是一个文本输入框组件。

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-custom-input',
  template: `
    <input type="text" [(ngModel)]="value">
  `,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputComponent),
    multi: true
  }]
})
export class CustomInputComponent implements ControlValueAccessor {
  value: string;
  onChange: (_: any) => {};
  onTouched: () => {};

  writeValue(value: any): void {
    this.value = value;
    this.onChange(this.value);
  }
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    // 控制是否被禁用
  }
}

解释一下上述代码:

  • @Component:指定组件自定义的选择器、模板和提供商。
  • template:使用 ngModel 指令来创建一个文本输入框。
  • NG_VALUE_ACCESSOR:定义提供商,allowing the component to inject itself into the providers array. By doing this, we are allowing other components or directives to retrieve and access our component instances.

  • forwardRef:使用 forwardRef,我们可以解决在类中直接访问类名的问题。

  • multi:告诉 Angular 选择器中的 provider 是一个 token,可以有多个提供者。如果我们只有一个提供者,则可以省略 multi。

  • ControlValueAccessor 接口中的几个方法:

    • writeValue:当表单控件的值发生变化时调用的方法。我们需要将传递进来的 value 值赋值给我们的 value 属性,并调用 onChange 方法。
    • registerOnChange:注册一个回调方法,当表单控件的值发生变化时,调用这个方法。
    • registerOnTouched:注册一个回调方法,当表单控件失去焦点时调用它。
    • setDisabledState:为表单控件提供一个禁用的状态。这是可选的,如果你的表单控件不需要禁用状态,则可以忽略掉这个方法。

第二步:使用自定义表单控件

现在我们已经创建了一个自定义的表单控件,接下来让我们来使用它。

同样地,我们给出一个示例,一个使用自定义表单控件的表单组件。

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-form',
  template: `
    <form [formGroup]="form">
      <app-custom-input formControlName="input"></app-custom-input>
    </form>`
})
export class FormComponent {
  form: FormGroup;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      input: ''
    });
  }
}

在上面的代码中我们引入了两个模块:

  • FormBuilder:它会帮助我们创建表单模型的实例。
  • FormGroup:它会创建一个组件实例,我们可以通过它来访问表单控件的值。

下面是表单控件的初始化:

  • 在模板中使用 app-custom-input 组件。
  • 在 ts 中初始化 FormGroup。注意,我们的表单控件名称必须与 formControlName 匹配。

至此,我们已经创建了一个自定义表单控件,并使用它来创建了一个表单组件。

示例

示例一:工资计算器

接下来我们通过一个工资计算器来示范实际应用 ControlValueAccessor。

工资计算器包含两个自定义控件组件:一个是输入工资的输入框,另一个是计算后的税后工资显示。

首先,我们来定义并实现输入工资的组件。

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-salary-input',
  template: `
    <input type="number" [(ngModel)]="value" (blur)="onTouched()">
  `,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => SalaryInputComponent),
    multi: true
  }]
})
export class SalaryInputComponent implements ControlValueAccessor {
  value: number;
  onChange: (_: any) => {};
  onTouched: () => {};

  writeValue(value: any): void {
    this.value = value;
    this.onChange(this.value);
  }
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    // 控制是否被禁用
  }
}

注意,上述代码中使用了类型为 number 的 ngModel 指令。

接下来,让我们来定义计算后的税后工资显示组件。

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-net-income',
  template: `
    <div>{{value}}</div>
  `
})
export class NetIncomeComponent {
  @Input() value: number;
}

在这个组件中,我们使用了 @Input 装饰器将计算后的税后工资显示出来,没有使用 ControlValueAccessor 接口。

接下来是应用这两个组件的组件。

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-salary-calculator',
  template: `
    <form [formGroup]="form">
      <app-salary-input formControlName="salary"></app-salary-input>
      <app-net-income [value]="netIncome"></app-net-income>
    </form>
  `
})
export class SalaryCalculatorComponent {
  form: FormGroup;
  netIncome: number = 0;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      salary: ''
    });

    this.form.get('salary').valueChanges.subscribe((value) => {
      const INCOME_TAX_RATE = 0.2; // 薪资税率
      const INCOME_TAX_ACCUMULATOR = 3500; // 快递补贴

      const taxBefore = value * INCOME_TAX_RATE;
      const taxAfter = taxBefore - INCOME_TAX_ACCUMULATOR;

      this.netIncome = value - taxAfter;
    });
  }
}

在这个组件中,我们使用了 FormBuilder 创建了一个 FormGroup,使用和之前创建自定义表单控件时一样的方式来初始化表单控件。

然后监听 salary 控制器值的变化,每当 salary 变化时,变化数值会经过计算后赋值给 netIncome。

示例二:星级评价

接下来我们再看一个自定义表单控件的示例:一个星级评价组件。

首先我们需要在模板中放置五个图片,用来表示评价星级。

import { Component, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-star-rating',
  template: `
    <div>
      <img *ngFor="let star of stars" [src]="starImg(star)" (click)="setValue(star)">
    </div>
  `,
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => StarRatingComponent),
    multi: true
  }]
})
export class StarRatingComponent implements ControlValueAccessor {
  stars = [1, 2, 3, 4, 5];
  value: number;
  onChange: (_: any) => {};
  onTouched: () => {};

  writeValue(value: any): void {
    this.value = value;
    this.onChange(this.value);
  }
  registerOnChange(fn: any): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    // 控制是否被禁用
  }

  starImg(star: number): string {
    return this.value >= star ? '../assets/star-filled.svg' : '../assets/star-empty.svg';
  }

  setValue(star: number) {
    this.writeValue(star);
  }
}

在这个组件中:

  • 我们使用了 *ngFor 来遍历 stars 数组,生成五个 img 元素。这里的 img 是我们的评价星级。
  • 用于值绑定的 [(ngModel)],是在 setValue 方法中赋值的。

然后我们来看看如何在组件中使用星级评价组件。

import { Component } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-home',
  template: `
    <form [formGroup]="form">
      <app-star-rating formControlName="rating"></app-star-rating>
    </form>
  `
})
export class HomeComponent {
  form: FormGroup;
  rating: number;

  constructor(private fb: FormBuilder) {
    this.form = this.fb.group({
      rating: ''
    });

    this.form.get('rating').valueChanges.subscribe((value) => {
      console.log(`You've selected ${value} star(s)`);
    });
  }
}

我们将自己定义的控件引入到模板中,并将它绑定到 formControlName 上。

最后我们还使用了 valueChanges 事件订阅器监听控件值的变化。

这就是如何通过 ControlValueAccessor 接口来创建自定义表单控件的详细攻略。希望对大家有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Angular使用ControlValueAccessor创建自定义表单控件 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • 详解Nginx中的Rewrite的重定向配置与实践

    详解Nginx中的Rewrite的重定向配置与实践 在Nginx中,Rewrite模块提供了强大的重定向功能,可以通过配置文件对URL进行重写和重定向。本攻略将详细介绍Nginx中Rewrite的配置和实践,并提供两个示例说明。 1. Rewrite配置语法 Rewrite配置语法如下: rewrite regex replacement [flag]; r…

    other 2023年7月29日
    00
  • C++全面覆盖内存管理知识讲解

    C++全面覆盖内存管理知识讲解 1. 引言 在C++编程中,内存管理是一个非常重要的主题。正确地管理内存可以提高程序的性能和可靠性。本攻略将全面介绍C++中的内存管理知识,包括动态内存分配、内存泄漏、智能指针等。 2. 动态内存分配 在C++中,可以使用new和delete关键字来进行动态内存分配和释放。动态内存分配允许程序在运行时根据需要分配和释放内存。 …

    other 2023年8月1日
    00
  • android布局——单复选框(今天上课的内容总结下)

    Android布局——单复选框 单复选框是Android布局中经常使用的UI组件,它们可以让用户选择或确定某些选项,进而影响App的行为。在本篇文章中,我们将详细介绍单复选框的使用方法及布局技巧。 单选框 单选框(RadioButton)是一组互斥的选项,用户只能选择其中的一项。单选框通过RadioGroup容器进行布局,RadioGroup容器内的Radi…

    其他 2023年3月28日
    00
  • CSS 多列布局问题简单解决方案

    关于“CSS 多列布局问题简单解决方案”的完整攻略,我这里将会从以下几个方面进行讲解: 概述多列布局问题 使用 CSS column 实现多列布局 使用 Flexbox 实现多列布局 使用 Grid 实现多列布局 示例说明 1. 概述多列布局问题 在网页布局中,我们经常需要实现多列布局的效果,但由于不同浏览器对多列布局的支持程度不一致,这给前端工程师带来了很…

    other 2023年6月26日
    00
  • 魔兽世界7.3.5防骑怎么堆属性 wow7.35防骑配装属性优先级攻略

    魔兽世界7.3.5防骑怎么堆属性 在魔兽世界中,防骑是一个重要的职业之一,如果你想在战斗中表现得更加出色,那么你需要了解更多的防骑配装属性及优先级。 防骑配装属性优先级攻略 1. 爆击率 在防骑中,爆击率是非常重要的一个属性,可以提高你的输出和生存能力。因为爆击能够触发额外的效果,比如触发技能或增加伤害等。 2. 全能 全能是魔兽世界中一种比较万能的属性,它…

    other 2023年6月27日
    00
  • 易语言实现PC端登陆微信的代码

    易语言实现PC端登录微信的代码攻略 1. 准备工作 在开始编写代码之前,需要确保已经安装了易语言开发环境,并且熟悉基本的易语言编程知识。 2. 导入相关模块 首先,我们需要导入一些易语言的系统模块,以便后续使用。在本例中,我们需要导入网络操作和窗口操作模块。 导入模块 网络操作 导入模块 窗口操作 3. 创建登录窗口 接下来,我们需要创建一个登录窗口,用于用…

    other 2023年7月29日
    00
  • vue开发者工具下载

    Vue开发者工具下载 Vue是一种流行的JavaScript框架,可用于构建大型的单页应用。在开发Vue应用过程中,Vue开发者工具是一个非常实用的工具,它可以帮助开发者进行调试和性能优化等工作。在本篇文章中,我们将介绍如何下载和安装Vue开发者工具。 下载Vue开发者工具 Vue开发者工具可以在官方网站上免费下载,官方网站的地址是 https://chro…

    其他 2023年3月28日
    00
  • 如何自定义 Illustrator 工作区

    下面是自定义 Illustrator 工作区的完整攻略: 1. 打开 Illustrator 并进入“工作区”模式 打开 Illustrator 后,在菜单栏中找到“窗口”(Window)选项,点击下拉菜单中的“工作区”(Workspace),然后选择“新建工作区”(New Workspace)即可进入自定义工作区模式。 2. 自定义工作区布局 在自定义工作…

    other 2023年6月25日
    00
合作推广
合作推广
分享本页
返回顶部