当前位置: 首页 > news >正文

如何使用 ControlValueAccessor 在 Angular 中创建自定义表单控件

简介

在 Angular 中创建表单时,有时您希望拥有一个不是标准文本输入、选择或复选框的输入。通过实现 ControlValueAccessor 接口并将组件注册为 NG_VALUE_ACCESSOR,您可以将自定义表单控件无缝地集成到模板驱动或响应式表单中,就像它是一个原生输入一样!

!Rating Input 组件示例的动画 GIF,选择不同数量的星星。

在本文中,您将把一个基本的星级评分输入组件转换为 ControlValueAccessor

先决条件

要完成本教程,您需要:

  • 本地安装 Node.js,您可以按照《如何安装 Node.js 并创建本地开发环境》进行操作。
  • 一些设置 Angular 项目和使用 Angular 组件的基础知识可能会有所帮助。

本教程已在 Node v16.4.2、npm v7.18.1、angular v12.1.1 上进行验证。

步骤 1 — 设置项目

首先,创建一个新的 RatingInputComponent

可以使用 @angular/cli 完成此操作:

ng generate component rating-input --inline-template --inline-style --skip-tests --flat --prefix

这将向应用程序的 declarations 中添加新组件,并生成一个 rating-input.component.ts 文件:


import { Component, OnInit } from '@angular/core';@Component({selector: 'rating-input',template: `<p>rating-input works!</p>`,styles: []
})
export class RatingInputComponent implements OnInit {constructor() { }ngOnInit(): void {}}

添加模板、样式和逻辑:


import { Component } from '@angular/core';@Component({selector: 'rating-input',template: `<span*ngFor="let starred of stars; let i = index"(click)="rate(i + (starred ? (value > i + 1 ? 1 : 0) : 1))"><ng-container *ngIf="starred; else noStar">⭐</ng-container><ng-template #noStar>·</ng-template></span>`,styles: [`span {display: inline-block;width: 25px;line-height: 25px;text-align: center;cursor: pointer;}`]
})
export class RatingInputComponent {stars: boolean[] = Array(5).fill(false);get value(): number {return this.stars.reduce((total, starred) => {return total + (starred ? 1 : 0);}, 0);}rate(rating: number) {this.stars = this.stars.map((_, i) => rating > i);}
}

我们可以获取组件的 value(从 05),并通过调用 rate 函数或点击所需的星级来设置组件的值。

您可以将组件添加到应用程序中:


<rating-input></rating-input>

然后运行应用程序:

ng serve

并在 Web 浏览器中进行交互。

这很棒,但我们不能只是将此输入添加到表单中并期望一切立即正常工作。我们需要将其设置为 ControlValueAccessor

步骤 2 — 创建自定义表单控件

为了使 RatingInputComponent 表现得就像是一个原生输入(因此是一个真正的自定义表单控件),我们需要告诉 Angular 如何做一些事情:

  • 写入输入的值 - writeValue
  • 注册一个函数,告诉 Angular 输入的值何时发生变化 - registerOnChange
  • 注册一个函数,告诉 Angular 输入已被触摸 - registerOnTouched
  • 禁用输入 - setDisabledState

这四个内容构成了 ControlValueAccessor 接口,它是表单控件和原生元素或自定义输入组件之间的桥梁。一旦我们的组件实现了该接口,我们需要告诉 Angular 通过提供它作为 NG_VALUE_ACCESSOR 来使用它。

在代码编辑器中重新查看 rating-input.component.ts,并进行以下更改:


import { Component, forwardRef, HostBinding, Input } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';@Component({selector: 'rating-input',template: `<span*ngFor="let starred of stars; let i = index"(click)="onTouched(); rate(i + (starred ? (value > i + 1 ? 1 : 0) : 1))"><ng-container *ngIf="starred; else noStar">⭐</ng-container><ng-template #noStar>·</ng-template></span>`,styles: [`span {display: inline-block;width: 25px;line-height: 25px;text-align: center;cursor: pointer;}`],providers: [{provide: NG_VALUE_ACCESSOR,useExisting: forwardRef(() => RatingInputComponent),multi: true}]
})
export class RatingInputComponent implements ControlValueAccessor {stars: boolean[] = Array(5).fill(false);// 允许输入被禁用,并在禁用时使其略微透明。@Input() disabled = false;@HostBinding('style.opacity')get opacity() {return this.disabled ? 0.25 : 1;}// 当评分发生变化时调用的函数。onChange = (rating: number) => {};// 当输入被触摸时(点击星星时)调用的函数。onTouched = () => {};get value(): number {return this.stars.reduce((total, starred) => {return total + (starred ? 1 : 0);}, 0);}rate(rating: number) {if (!this.disabled) {this.writeValue(rating);}}// 允许 Angular 更新模型(评分)。// 在这里更新模型和视图所需的更改。writeValue(rating: number): void {this.stars = this.stars.map((_, i) => rating > i);this.onChange(this.value);}// 允许 Angular 注册一个在模型(评分)更改时调用的函数。// 将函数保存为以后调用的属性。registerOnChange(fn: (rating: number) => void): void {this.onChange = fn;}// 允许 Angular 注册一个在输入被触摸时调用的函数。// 将函数保存为以后调用的属性。registerOnTouched(fn: () => void): void {this.onTouched = fn;}// 允许 Angular 禁用输入。setDisabledState(isDisabled: boolean): void {this.disabled = isDisabled;}
}

此代码将允许输入被禁用,并在禁用时使其略微透明。

运行应用程序:

ng serve

并在 Web 浏览器中进行交互。

您还可以禁用输入控件:


<rating-input [disabled]="true"></rating-input>

现在我们可以说我们的 RatingInputComponent 是一个自定义表单组件!它将在模板驱动或响应式表单中像任何其他原生输入一样工作(Angular 为这些提供了 ControlValueAccessors!)。

结论

在本文中,您将一个基本的星级评分输入组件转换为了 ControlValueAccessor

现在您会注意到:

  • ngModel 现在可以正常工作。
  • 我们可以添加自定义验证。
  • 控件状态和有效性可以通过 ngModel 获得,比如 ng-dirtyng-touched 类。

如果您想了解更多关于 Angular 的知识,请查看我们的 Angular 专题页面,了解练习和编程项目。

相关文章:

如何使用 ControlValueAccessor 在 Angular 中创建自定义表单控件

简介 在 Angular 中创建表单时&#xff0c;有时您希望拥有一个不是标准文本输入、选择或复选框的输入。通过实现 ControlValueAccessor 接口并将组件注册为 NG_VALUE_ACCESSOR&#xff0c;您可以将自定义表单控件无缝地集成到模板驱动或响应式表单中&#xff0c;就像它是一个原…...

视频讲解:优化柱状图

你好&#xff0c;我是郭震 AI数据可视化 第三集&#xff1a;美化柱状图&#xff0c;完整视频如下所示&#xff1a; 美化后效果前后对比&#xff0c;前&#xff1a; 后&#xff1a; 附完整案例源码&#xff1a; util.py文件 import platformdef get_os():os_name platform.syst…...

OpenAI宣布ChatGPT新增记忆功能;谷歌AI助理Gemini应用登陆多地区

&#x1f989; AI新闻 &#x1f680; OpenAI宣布ChatGPT新增记忆功能&#xff0c;可以自由控制内存&#xff0c;提供个性化聊天和长期追踪服务 摘要&#xff1a;ChatGPT新增的记忆功能可以帮助AI模型记住用户的提问内容&#xff0c;并且可以自由控制其内存。这意味着用户不必…...

Solidworks:平面草图练习

继续练习平面草图&#xff0c;感觉基本入门了。...

React18原理: 渲染与更新时的重点关注事项

概述 react 在渲染过程中要做很多事情&#xff0c;所以不可能直接通过初始元素直接渲染还需要一个东西&#xff0c;就是虚拟节点&#xff0c;暂不涉及React Fiber的概念&#xff0c;将vDom树和Fiber 树统称为虚拟节点有了初始元素后&#xff0c;React 就会根据初始元素和其他可…...

嵌入式I2C 信号线为何加上拉电阻(图文并茂)

IIC 是一个两线串行通信总线&#xff0c;包含一个 SCL 信号和 SDA 信号&#xff0c;SCL 是时钟信号&#xff0c;从主设备发出&#xff0c;SDA 是数据信号&#xff0c;是一个双向的&#xff0c;设备发送数据和接收数据都是通过 SDA 信号。 在设计 IIC 信号电路的时候我们会在 SC…...

Vite 5.0 正式发布

11 月 16 日&#xff0c;Vite 5.0 正式发布&#xff0c;这是 Vite 道路上的又一个重要里程碑&#xff01;Vite 现在使用 Rollup 4&#xff0c;这已经代表了构建性能的大幅提升。此外&#xff0c;还有一些新的选项可以改善开发服务器性能。 Vite 4 发布于近一年前&#xff0c;它…...

嵌入式STM32 单片机 GPIO 的工作原理详解

STM32的 GPIO 介绍 GPIO 是通用输入/输出端口的简称&#xff0c;是 STM32 可控制的引脚。GPIO 的引脚与外部硬件设备连接&#xff0c;可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。 以 STM32F103ZET6 芯片为例子&#xff0c;该芯片共有 144 脚芯片&#xff0c…...

系统调用的概念

在嵌入式开发、操作系统开发以及一般的系统编程中&#xff0c;系统调用是一个核心概念。它允许用户空间程序请求内核执行某些操作&#xff0c;如打开文件、读写数据、创建进程等。这些操作通常需要特殊的权限或访问硬件资源&#xff0c;因此不能直接在用户模式下执行。 系统调…...

【无标题】Matlab 之axes函数——创建笛卡尔坐标区

**基本用法&#xff1a;**axes 在当前图窗中创建默认的笛卡尔坐标区&#xff0c;并将其设置为当前坐标区。 应用场景1&#xff1a;在图窗中放置两个 Axes 对象&#xff0c;并为每个对象添加一个绘图。 要求1&#xff1a;指定第一个 Axes 对象的位置&#xff0c;使其左下角位于…...

2.12:C语言测试题

1.段错误&#xff1a;申请堆区内存未返回&#xff0c;str指向NULL 2.段错误&#xff1a;局部变量&#xff0c;本函数结束&#xff0c;p也释放 3.越界访问&#xff0c;可能正常输出hello&#xff0c;可能报错 4.可能段错误&#xff0c;释放后&#xff0c;str未指向NULL&#x…...

【Linux】yum软件包管理器

目录 Linux 软件包管理器 yum 什么是软件包 Linux安装软件 查看软件包 关于rzsz Linux卸载软件 查看yum源 扩展yum源下载 Linux开发工具 vim编辑器 上述vim三种模式之间的切换总结&#xff1a; 命令模式下&#xff0c;一些命令&#xff1a; vim配置 Linux 软件包管理…...

「优选算法刷题」:寻找旋转排序数组中的最小值

一、题目 已知一个长度为 n 的数组&#xff0c;预先按照升序排列&#xff0c;经由 1 到 n 次 旋转 后&#xff0c;得到输入数组。例如&#xff0c;原数组 nums [0,1,2,4,5,6,7] 在变化后可能得到&#xff1a; 若旋转 4 次&#xff0c;则可以得到 [4,5,6,7,0,1,2]若旋转 7 次…...

MySQL 基础入门指南:从安装到基本操作

一、简介 MySQL 是一种流行的开源关系型数据库管理系统&#xff0c;被广泛用于各种规模和类型的应用程序中。如果您对 MySQL 还不熟悉&#xff0c;本文将为您提供一个基础的入门指南&#xff0c;从安装到基本操作。 1.1 安装 MySQL 首先&#xff0c;您需要下载并安装 MySQL。…...

嵌入式Qt Qt Creator安装与工程介绍

一.Qt概述 什么是Qt&#xff1a;Qt是一个跨平台的C图形用户界面应用程序框架。它为应用程序开发者提供建立图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正的组件编程。 二.Qt Creator下载安装 下载地址&#xff1a;Index of /a…...

Windows 系统盘(C盘)爆红如何清理、如何增加C盘空间

1、简介 Windows系统中&#xff0c;系统和保留占用太多的空间&#xff0c;一旦系统盘分配空间较少&#xff0c;使用一段时间后&#xff0c;备份文件、临时文件、系统更新记录等都会在占用系统盘较大空间&#xff0c;导致系统盘空间不够使用&#xff0c;会造成应用运行卡顿。如何…...

【JavaEE Spring】Spring 原理

Spring 原理 1. Bean的作⽤域1.1 概念1.2 Bean的作⽤域 2. Bean的⽣命周期 1. Bean的作⽤域 1.1 概念 在Spring IoC&DI阶段, 我们学习了Spring是如何帮助我们管理对象的. 通过 Controller , Service , Repository , Component , Configuration ,Bean 来声明Bean对象。通…...

【Crypto | CTF】RSA打法

天命&#xff1a;我发现题题不一样&#xff0c;已知跟求知的需求都不一样 题目一&#xff1a;已知 p q E &#xff0c;计算T&#xff0c;最后求D 已知两个质数p q 和 公钥E &#xff0c;通过p和q计算出欧拉函数T&#xff0c;最后求私钥D 【密码学 | CTF】BUUCTF RSA-CSDN…...

红衣大叔讲AI:从OpenAI发布首个视频大模型Sora,谈2024年视觉大模型的十大趋势

OpenAI宣布推出全新的生成式人工智能模型“Sora”。据了解&#xff0c;通过文本指令&#xff0c;Sora可以直接输出长达60秒的视频&#xff0c;并且包含高度细致的背景、复杂的多角度镜头&#xff0c;以及富有情感的多个角色。 OpenAI发布首个视频大模型Sora&#xff0c;一句话生…...

java远程连接Linux执行命令的三种方式

java远程连接Linux执行命令的三种方式 1. 使用JDK自带的RunTime类和Process类实现2. ganymed-ssh2 实现3. jsch实现4. 完整代码&#xff1a;执行shell命令下载和上传文件 1. 使用JDK自带的RunTime类和Process类实现 public static void main(String[] args){Process proc Run…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

c++ 面试题(1)-----深度优先搜索(DFS)实现

操作系统&#xff1a;ubuntu22.04 IDE:Visual Studio Code 编程语言&#xff1a;C11 题目描述 地上有一个 m 行 n 列的方格&#xff0c;从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子&#xff0c;但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...