自定义表单控件 [(ngModel)]

2023-10-27

[(ngModel)]拆分

[(ngModel)][]输入()输出组合起来,进行双向数据绑定。拆分开来

  1. 输入属性[ngModel]
  2. (ngModelChange)输出监听元素值的变化,并同步view value与model value。
<input type="text" id="modelInner" [ngModel]="model" (ngModelChange)="getModelChange($event)">
    model: string;
    constructor() {
        this.model = 'model init';
    }

    getModelChange(event: string) {
        this.model = event;  // view value 与 model value 同步
    }

自定义组件上使用 [(ngModel)]

我们不能把[(ngModel)]用到非表单类的原生元素或第三方自定义组件上,除非写一个合适的值访问器,这种技巧超出了本章的范围。

Angular文档中描述到这里,就中止了。刚好我要定制一个模拟radio的组件,只能如文档所说,依葫芦画瓢实现 ControlValueAccessor

ControlValueAccessor接口

ControlValueAccessor acts as a bridge between the Angular forms API and a native element in the DOM.
Implement this interface if you want to create a custom form control directive that integrates with Angular forms.

简而言之,实现了这个接口的组件,就可以使用 Angular forms API,比如[(ngModel)]

interface ControlValueAccessor { 
  writeValue(obj: any): void
  registerOnChange(fn: any): void
  registerOnTouched(fn: any): void
  setDisabledState(isDisabled: boolean)?: void
}

实现ControlValueAccessor步骤

模仿primeng中的自定义radio组件,写了一个简单的自定义radio组件。

  1. 创建一个RADIO_VALUE_ACCESSOR常量用来在组件中注册NG_VALUE_ACCESSOR
  2. 实现ControlValueAccessor中的3+1个方法

完整demo代码如下:

import { NgModule, Component, Input, Output, ElementRef, OnInit, EventEmitter, forwardRef, ViewChild, ChangeDetectorRef } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

const RADIO_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => PRadioComponent),
  multi: true
};

@Component({
  selector: 'app-p-radio',
  template: `
        <div class="p-radio">
            <label class="radio-label" (click)="select()" *ngIf="label">
                <div class="name" [class.checked-name]="rb.checked">{{label}}</div>
            </label>
            <div class="helper-hidden-accessible">
                <input #rb type="radio" [attr.name]="name" [attr.value]="value" [checked]="checked">
            </div>
            <div class="radio-md" (click)="handleClick()">
                <div class="radio-icon " [class.radio-checked]="rb.checked">
                     <div class="radio-inner"></div>
                </div>
            </div>
        </div>
    `,
  styleUrls: ['./p-radio.component.scss'],
  providers: [RADIO_VALUE_ACCESSOR]
})
export class PRadioComponent implements ControlValueAccessor {

  @Input() name: string;
  @Input() label: string;
  @Input() value: string;
  checked: boolean;

  @ViewChild('rb') inputViewChild: ElementRef;
  @Output() pRadioChange: EventEmitter<any> = new EventEmitter();
  onModelChange: Function = () => { };

  constructor(
    private cd: ChangeDetectorRef
  ) { }

  // model view -> view value
  writeValue(value: any): void {
    if (value) {
      this.checked = (value === this.value);
      if (this.inputViewChild.nativeElement) {
        this.inputViewChild.nativeElement.checked = this.checked;
      }
      this.cd.markForCheck();
    }
  }

  // view value ->model value
  registerOnChange(fn: Function): void {
    this.onModelChange = fn;
  }

  registerOnTouched(fn: Function): void { }

  handleClick() {
    this.select();
  }

  select() {
    this.inputViewChild.nativeElement.checked = !this.inputViewChild.nativeElement.checked;
    this.checked = !this.checked;
    if (this.checked) {
      this.onModelChange(this.value); // 同步view value 和 model value
    } else {
      this.onModelChange(null);
    }
    this.pRadioChange.emit(null);
  }

}

@NgModule({
  imports: [CommonModule],
  exports: [PRadioComponent],
  declarations: [PRadioComponent]
})

export class RadioButtonModule { }

方法何时被调用?

writeValue(obj: any): void

API中提到 (model -> view) 时,writeValue() 会被调用。
model value 和 view value分别指什么?
举个调用PRadioComponent的例子:

  <app-p-radio [value]="'1'" [label]="'text1'" [(ngModel)]="checkedValue"></app-p-radio>

这里checkedValue属性就是model value,view value 为PRadioComponent内部的某个属性(PRadioComponent中定义为this.value)。
当model view(checkedValue)发生改变时,PRadioComponent中的writeValue(obj: any)就会被调用,参数为当前model value(checkedValue)的值,在函数中将参数值赋给内部的view value,从而实现(model -> view)。接受到model value的值后,改变PRadioComponent的UI显示。

registerOnChange(fn: any): void

这个方法的作用是同步 view value 和 model value (view -> model),

 registerOnChange(fn: Function): void {
    this.onModelChange = fn;
  }

调用this.onModelChange()时候,将view value当作参数传入此方法中,即完成了同步,此例子中this.onModelChange(this.value);

上面两种方法是相对的:

  • writeValue(obj: any): model value发生改变 ,完成后UI发生改变(model value-> view value)
  • registerOnChange(fn: any): 触发事件(比如click),view value和UI发生改变,完成调用后model value与view value同步(view value-> model value)

registerOnTouched(fn: any): void

setDisabledState(isDisabled: boolean)?: void

目的只为在控件中简单的使用[(ngModel)],所以这两个方法没有用到。registerOnTouched(fn: any)必须实现,所以定义了一个空函数。

实际效果

初始值为'a',点击改变view value,在Angury调试工具中看到值改为'b'。然后在调试工具中将checkedValue改为'a',视图发生了改变。可见,完成了数据的双向绑定。
实际效果

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义表单控件 [(ngModel)] 的相关文章

  • Angular - 如何从 DOM 中删除我使用过 $compile 的元素?

    我需要的是两个 ng views 的功能 因为我不能 我想更改某些内容的innerHTML 并编译它 我遇到的问题是 当我再次更改内容时 我可以编译 但是 Angular 是否会自行删除绑定 或者我必须手动执行此操作 如果是这样 怎么办 编
  • 我如何能够以两行显示标题,并且每行的字体大小不同?

    我正在使用 Google Chart API 创建时间线图 并希望将图的标题修改为两行 问题 我如何能够显示具有不同字体大小的两线图表标题 电流输出 理想输出 相关研究 我唯一能找到的是有人试图用饼图来做到这一点 但我尝试了但无法使其发挥作
  • 使用 jquery 远程图像属性

    目前我正在尝试获取远程图像宽度 高度 我正在开发一个链接共享模块 就像当你在 Facebook 上粘贴链接时 你可以看到标题 描述和图像 所以我尝试使用 php getimagesize 来获取图像宽度 高度 但速度非常慢 所以我正在考虑使
  • 如何使用 JavaScript 创建链接?

    我有一个标题字符串和一个链接字符串 我不知道如何将两者放在一起以使用 JavaScript 在页面上创建链接 任何帮助表示赞赏 我试图解决这个问题的原因是因为我有一个 RSS 源并且有一个标题和 URL 列表 我想将标题链接到 URL 以使
  • Javascript 函数查找数字的倍数

    创建一个名为的函数multiplesOf 它将接受两个参数 第一个参数是数字数组 第二个参数是数字 该函数应返回一个新数组 该数组由参数数组中的每个数字组成 该数字是参数数字的倍数 So multiplesOf 5 6 7 8 9 10 3
  • 在 contenteditable div 中选择范围

    我有一个contenteditablediv 和其中的一些段落 这是我的代码 div style border solid 1px black width 300px height 300px div Hello world div div
  • IE从哪个版本开始支持Object.create(null)?

    您可以通过多种方式在 JavaScript 中创建对象 creates an object which makes the Object prototype of data var data1 new Object Object liter
  • 隐藏 Div 的父级

    我只是想隐藏父divcomments section div class content content green div div div 我试过这个 document getElementById comments section pa
  • React-Redux:state.setIn() 和 state.set() 有什么区别?

    我见过使用setIn and set 在一些react redux代码中 state setIn state set 我在这里找到了一些文档https facebook github io immutable js https facebo
  • 有没有办法使用 Rspec/Capybara/Selenium 将 javascript console.errors 打印到终端?

    当我运行 rspec 时 是否可以让 capybara selenium 向 rspec 报告任何 javascript console errors 和其他异常 我有一大堆测试失败 但当我手动测试它时 我的应用程序正在运行 如果不知道仅在
  • 设置 cookie 时中断 JavaScript 执行

    当设置 cookie 时 是否可以始终中断浏览器开发人员工具中的 javascript 执行 无需显式设置 JS 断点 document cookie 在 html head 块的开头添加此代码片段效果很好
  • Chrome 扩展程序在代码中使用 client_secret

    我正在开发具有自己的 oAuth 授权的 Google Chrome 扩展 当然 我必须使用 client id 和 client secret 作为请求令牌 有什么办法可以向用户隐藏这些数据吗 由于此请求只是 javascript 源代码
  • 检查 jQuery 1.7 中是否存在基于文本的选择选项

    所以我有以下 HTML 片段
  • 刷新页面时保存用户的选择

    我目前有一个页面显示不同团队的数据 我有一些数据 用户可以单击使其处于 打开 或 关闭 状态 并为每个数据显示不同的图标 它基本上就像一个清单 只是没有物理复选框 我想记住哪些 复选框 已被选中 即使在用户刷新页面或关闭浏览器并稍后返回之后
  • 为什么我们在打字稿中使用 HTMLInputElement ?

    我们为什么使用 document getElementById ipv as HTMLInputElement value 代替 document getElementById ipv value 功能getElementById返回具有类
  • DataTables row.add 到特定索引

    我正在替换这样的行项目 var targetRow entity row dataTable targetRow closest table dataTable DataTable dataTable row targetRow remov
  • 从数据库检查数据的异步解决方案各种循环子句

    我想要做的是异步检查数据库并从中获取结果 在我的应用程序中我试图实现Asynchronously将此步骤解决为 从数据库中检查手机号码JsonArray循环子句的种类 Create JsonArray从结果 打印创建的数组 我学到了足够多的
  • 在 JavaScript 循环之外声明变量可以提高速度和内存?

    C 也有类似的问题 但我们没有看到 JavaScript 的任何问题 在循环内声明变量是否可以接受 假设循环有 200 次迭代 使用样本 2 相对于样本 1 是否有性能要求 内存和速度 我们使用 jQuery 来循环 它提高了我们将 var
  • 如何用另一个响应替换窗口的 URL 哈希?

    我正在尝试使用替换方法更改哈希 URL document location hash 但它不起作用 function var anchor document location hash this returns me a string va
  • 如何确定所有角度2分量都已渲染?

    当所有 Angular2 组件完成渲染时 是否会触发一个角度事件 For jQuery 我们可以用 function 然而 对于 Angular2 当domready事件被触发 html 只包含角度组件标签 每个组件完成渲染后 domrea

随机推荐

  • Flutter AudioPlayer单例模式

    一 前言 一般情况下 audioPlayer最好设置成单例模式 这样在跨页面操作时会更好的使用 确保所有对象都访问一个实例 二 引用 audioplayers 功能 控制mp3文件播放 地址 https pub dev packages a
  • LAStools

    LASlib with LASzip DLL are LAStools http www cs unc edu isenburg lastools
  • ctfshow-WEB-web7

    ctf show WEB模块第7关是一个SQL注入漏洞 注入点是数值型注入 源码中过滤了空格 我们可以使用括号 或者注释 来代替空格 页面中有一个文章列表 随便点一个 从url地址栏中可以看到 页面通过文章的id值来查询文章内容 我们可以考
  • SAS EM之SAS Credit Scoring不能使用

    SAS EM之SAS Credit Scoring不能使用 关于SAS EM找不到记分卡问题 我们要去看SAS Credit Scoring license有没有许可 未许可的话是不能使用的 这是我之前不能使用的截图 是没有信用评分这个节点
  • vue项目中使用神策进行数据埋点

    前言 在vue种使用神策进行数据埋点 一 数据埋点的意义 所谓埋点就是在应用中特定的流程收集一些信息 用来跟踪应用使用的状况 后续用来进一步优化产品或是提供运营的数据支撑 包括访问数 Visits 访客数 Visitor 停留时长 Time
  • 什么是轴向注意力(Axial Attention)机制

    Axial Attention 轴向注意力 有行注意力 row attention 和列注意力 column attention 之分 一般是组合使用 原文阅读 https arxiv org pdf 1912 12180v1 pdf se
  • Android自动化测试,5个必备的测试框架

    Appium Appium是一个开源的移动测试工具 支持iOS和Android 它可以用来测试任何类型的移动应用 原生 网络和混合 作为一个跨平台的工具 你可以在不同的平台上运行相同的测试 为了实现跨平台的功能 Appium使用了供应商提供
  • 【AD21】keepout层和机械1层怎么相互转换

    1 从keepout转换为Mechanical 1层 见下图 图1 图2 图3 图4 2 从keepout转换为Mechanical 1层 见下图 如果想要将keepout层转换成机械1层 可以先全选中想要转换的keepoutz层 然后在c
  • Docker之旅:在Docker容器中创建第一个程序

    Docker的概念 Docker是开发人员和系统管理员 使用容器开发 部署和运行应用程序的平台 使用Linux容器来部署应用程序称为集装箱化 容器不是新的事物 但它们用于轻松部署应用程序 一 测试一下Docker的版本 1 查看Docker
  • 实用工具推荐,浏览器必备宝藏插件:Wetab新标签页

    打开浏览器 你的起始页是否充满了广告和各种乱七八糟的信息呢 或者过于单调 而失去了某些你想要的功能 这里给大家推荐一个在Chrome浏览器和edge浏览器上都能愉快使用的浏览器插件 Wetab新标签页 没有广告 页面干净美观且具备各种实用功
  • android live 电视 源码,GitHub - mxiaoguang/LivePlayback: Android TV直播电视节目 ,包含各央视频道及卫视频道...

    Android TV直播电视节目 更多技术博客 项目 欢迎关注公众号 Android TV开发交流群 135622564 传统电视直播节目 在Android TV上起着越来越重要的作用 央视 各地卫视 满足观众日益增长的多元化需求 看下效果
  • IV转换电路原理图

    毫安级IV转换电路如下 如果要uA级转换 可把运放改为 D795 SG 8210 L C6482等输入偏置电流在pA级的运
  • tomcat配置443端口

  • true_type与false_type

    std true type和std false type 实际上是类型别名 是两个类型 类模板 注意区分true type与false type与true和false区别 true type false type代表类型 true fals
  • 【项目实战】Python基于局部离群因子LOF算法(LocalOutlierFactor)实现信用卡数据异常值检测项目实战

    说明 这是一个机器学习实战项目 附带数据 代码 文档 代码讲解 如需数据 代码 文档 代码讲解可以直接到文章最后获取 1 项目背景 异常检测是数据挖掘领域研究的基本问题之一 已被广泛应用于网络入侵检测 信用卡欺诈侦查等领域 局部离群因子 简
  • 以太坊客户端Geth命令用法-参数详解

    Geth是在以太坊智能合约开发中最常用的工具 必备开发工具 一个多用途的命令行工具 熟悉Geth可以让我们有更好的效率 大家可收藏起来作为Geth命令用法手册 本文主要是对geth help的翻译 基于最新的geth 1 7 3 stabl
  • sqlite3查看数据库中有哪些表(代码)

    说实话 用代码实现sqlite3查看数据库中有哪些表我还真的没找到现成资源 网上提供的语句还真用不了 而且大多都是命令行语句 由于的做的MFC项目要用到这个功能 特意学习了下 下面分享我的成果 希望可以帮到你 环境 VS2005 inclu
  • FPGA中值滤波实现并Modelsim仿真,与MATLAB中值滤波进行对比

    文章目录 一 中值滤波算法 二 FPGA实现中值滤波 2 1 3 3窗口的生成 2 2 排序模块 2 3中值滤波模块 2 4 整体RTL图 三 modeslim仿真 四 matlab中值滤波 五 效果对比 一 中值滤波算法 1 中值滤波算法
  • 解决wangEditor表格边框显示不出来、没有的问题

    仔细阅读文档 不过我一直找表格或者边框搜索 发现没有特定的栏目 所以忽略了 当然也有我不够仔细的原因 有点着急莽荒了 链接 在官网的这个页面 从官网贴下来的 把这段复制到想要的地方就可以了
  • 自定义表单控件 [(ngModel)]

    ngModel 拆分 ngModel 将 输入 输出组合起来 进行双向数据绑定 拆分开来 输入属性 ngModel ngModelChange 输出监听元素值的变化 并同步view value与model value