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日

相关文章

  • Java结合百度云存储BCS代码分享

    下面我将详细讲解Java结合百度云存储BCS的完整攻略,包含以下几个步骤: 注册百度云账号 要使用百度云存储BCS,首先要注册百度云账号。如果您已经有账号,可以直接进入控制台,新建应用并开启BCS服务。 新建Bucket 在控制台的BCS管理页面中,新建一个Bucket。Bucket相当于一个存储空间,可以用来存放文件。 获取Access Key和Secre…

    other 2023年6月26日
    00
  • 正则表达式 运算符优先级介绍

    正则表达式运算符优先级介绍 在正则表达式中,不同的运算符有不同的优先级。了解运算符优先级对于正确构建和解析正则表达式非常重要。本文将详细介绍正则表达式的运算符优先级。 1. 优先级最高的运算符 最高优先级的运算符是括号()。括号的作用是用于分组,可以改变子表达式的优先级。在括号中的子表达式会先于其他运算符进行计算。 2. 优先级次高的运算符 次高优先级的运算…

    other 2023年6月28日
    00
  • Windows 系统组策略应用全攻略(下)

    完整的Windows 系统组策略应用攻略主要包括以下过程: 1. 理解Windows系统的组策略 在Windows系统中,组策略是一种集中管理多个计算机或用户在其上运行的操作系统的技术。它可以通过中央的组策略对象 (GPO) 库来配置全局策略设置,以及应用于个别计算机或用户的本地策略设置。常用的策略设置包括安全设置、网络设置、应用配置等等。 2. 配置组策略…

    other 2023年6月27日
    00
  • 魔兽世界8.0敏锐贼堆什么属性好 敏锐贼属性收益及选择优先级

    魔兽世界8.0敏锐贼属性选择攻略 1. 敏锐贼属性选择的重要性 敏锐贼(Rogue)作为一个近战物理输出职业,属性的选择对于其输出和生存能力有着重要影响。敏锐贼的主要属性选择包括敏捷(Agility)、暴击(Critical Strike)、急速(Haste)、精通(Mastery)和全能(Versatility)。正确的属性选择能够提升敏锐贼的伤害输出和生…

    other 2023年6月28日
    00
  • 详解Vue项目编译后部署在非网站根目录的解决方案

    下面详解Vue项目编译后部署在非网站根目录的解决方案: 在Vue项目中通过webpack编译后生成的静态页面都在dist目录下,如果要部署在项目根目录下,只需将dist目录下的文件全部复制到项目根目录即可。但有些情况下需要将Vue项目部署到非网站根目录下,这时候需要做一些额外的配置。 下面介绍两种解决方案: 方案1:使用publicPath配置项 在Vue项…

    other 2023年6月27日
    00
  • swipe.js文档

    什么是swipe.js? swipe.js是一个轻量级的JavaScript,用于创建响应式的、可触摸滑动幻灯片。它支持多种滑动效果和自定义选项,可以轻松集成到您的网站或应用程序中。 如何使用swipe.js? 以下是使用swipe.js的步骤: 引入swipe文件。 “`html “` 创建HTML结构。 “`html Slide 1 Slide 2…

    other 2023年5月7日
    00
  • XAML: 自定义控件中事件处理的最佳实践方法

    下面是详细讲解“XAML: 自定义控件中事件处理的最佳实践方法”的完整攻略。 什么是自定义控件? 在 WPF 和 UWP 应用程序中,可以通过自定义控件来创建自己的特定控件。一个自定义控件可以由一个或多个现有控件组成,可以包含额外的属性和方法,以及自己特定的事件。XAML 是一种用于定义 WPF 和 UWP 界面的语言,可以用来创建自定义控件。 为什么需要自…

    other 2023年6月26日
    00
  • iOS14.6正式版固件下载地址 iOS14.6正式版下载

    iOS 14.6正式版固件下载地址 iOS 14.6正式版固件是苹果公司最新发布的操作系统版本,它带来了一些新功能和改进。如果你想下载并安装iOS 14.6正式版固件,下面是一个详细的攻略。 步骤一:备份设备 在开始下载和安装iOS 14.6正式版固件之前,强烈建议你先备份你的设备。这样可以确保你的数据在升级过程中不会丢失。你可以使用iCloud或iTune…

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