自定义表单控件 [(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)] 的相关文章

  • Ajax文件上传

    我想使用 Ajax 和 php 上传文件 我有一个表格
  • 如何按多个项目搜索/过滤列表?

    我正在寻找一个示例 或者可能是一个关于通过在文本框中输入的多个项目来过滤 搜索项目列表的方法的一点提示 假设我有一个列表 ul li Coffee li li Tea li li Milk li li Water li li Juice l
  • 按键对 JavaScript 对象进行排序

    我需要按键对 JavaScript 对象进行排序 因此 以下内容 b asdsad c masdas a dsfdsfsdf 会成为 a dsfdsfsdf b asdsad c masdas 这个问题的其他答案已经过时 与实施现实不符 并
  • 在其他不可滚动的 div 上滚动时如何滚动可滚动的 div?

    我知道这个问题听起来很令人困惑 但这就是我想要做的 在下面的代码片段中 如果用户在绿色 div 上滚动 我希望黄色 div 相应地滚动 就像滚动黄色 div 一样 请注意 黄色 div 有overflow auto 但绿色的则不然 docu
  • 如何强制折断不可折断的字符串?

    我有一个根据数据库中包含的数据生成的 HTML 页面 数据库有时包含浏览器无法分解的长字符串 因为这些字符串不包含可分解的字符 空格 点 逗号等 有没有办法使用 html css 甚至 javascript 来解决这个问题 看到这个link
  • Javascript 在另一个函数中检测“Shift”键按下

    我正在从 Flash 影片 使用外部接口 调用我的 html 页面中的 Javascript 函数 并且我想知道调用该函数时用户是否按下了 Shift 键 例如 如果我通过鼠标单击调用该函数 这似乎很简单 因为我可以传递事件并检查 if e
  • 无法读取未定义的“触及”属性

    为什么我会收到此错误无法读取未定义的属性 为什么无法读取formName controls email touched但它能够阅读formName get custDetails touched
  • 避免在 ES6 的函数内定位 this 的对象作用域

    例如 我正在使用 D3 js 运行一个项目 导入特定模块并调用它们的函数 Setup TypeScript ES6 导入特定的 D3 组件 角6 我有一个对象 在本例中是一个角度指令 并在 SVG 画布上绘制一些圆圈 并希望它们在拖动事件上
  • 使 Material UI Grid 项目的子项拉伸以适合父容器的剩余高度

    1 现状 我有一个包含 4 个网格项的 Material UI 网格容器 每个 Grid 项中都有一个 Typography 组件 其中包含标题和包含一些内容的 Card 如下所示 2 期望的外观 我希望卡片填充网格项目的剩余高度并且不超过
  • 窗口大小调整触发的 DOM 事件

    我有一个布局相当复杂的页面 最初打开页面时 某些元素的对齐存在问题 但是 可以通过更改浏览器窗口的大小来 永久 解决此问题 显然 我不希望用户必须调整浏览器窗口的大小才能使页面正确显示 所以我想知道是否有一种方法可以在页面首次加载时以编程方
  • ReactCSSTransitionGroup 组件WillLeave 未调用

    我尝试使用 ReactCssTransition 但不知何故该事件没有被调用 componentWillLeave 这是我的组件 import React Component from react import TransitionGrou
  • Firebase 身份验证在 iOS 登录时卡住,没有错误

    我已经使用电子邮件和密码设置了 firebase 身份验证登录的最基本实现 firebase auth signInWithEmailAndPassword email password then gt console log Logged
  • 如何知道浏览器空闲时间?

    如何跟踪浏览器空闲时间 我用的是IE8 我没有使用任何会话管理 也不想在服务器端处理它 这是纯 JavaScript 方法来跟踪空闲时间 并在达到一定限制时执行一些操作 var IDLE TIMEOUT 60 seconds var idl
  • put方法中的Angularjs文件上传不起作用

    我有一个简单的待办事项应用程序 我试图在其中上传照片和单个待办事项 现在我已经创建了这个工厂函数来负责待办事项的创建 todosFactory insertTodo function todo return http post baseUr
  • 如何使用 JavaScript 获取元素的填充值?

    我有一个textarea在我的 HTML 中 我需要获取整数或浮点形式的填充数值 以像素为单位 我如何使用 JavaScript 获取它 我没有使用 jQuery 所以我正在寻找纯 JavaScript 解决方案 这将返回padding l
  • JavaScript 中的实时摩尔斯电码转换器

    在看到谷歌关于莫尔斯电码 gmail 的愚人节笑话后 我想我应该尝试用 javascript 创建一个实时莫尔斯电码转换器 我正在使用正则表达式和替换将莫尔斯电码更改为字符 例如 replace g a replace g r 我遇到的问题
  • 弹出窗口的动态高度取决于内容,可能吗?

    是否有可能获得一个宽度始终为 400px 的弹出窗口 但根据弹出窗口中的内容动态高度 我已经看到了这个 但不知道如何将其应用到弹出窗口 调整 iframe 的宽度高度以适应其中的内容 https stackoverflow com ques
  • 如何为 Imagus 悬停缩放扩展开发自定义过滤器?

    当我读到关于悬停缩放是邪恶的 http www reddit com r YouShouldKnow comments 1wjrc8 ysk that the hover zoom extension is spyware 哎呀 有两篇文章
  • 检测浏览器选项卡是否具有焦点

    是否有可靠的跨浏览器方法来检测选项卡是否具有焦点 场景是 我们有一个定期轮询股票价格的应用程序 如果页面没有焦点 我们可以停止轮询并为每个人节省流量噪音 特别是当人们喜欢打开具有不同投资组合的多个选项卡时 Is window onblur
  • AngularJS 在指令运行之前通过 AJAX 检索数据

    我正在使用 AngularUIuiMap http angular ui github com directives map实例化谷歌地图的指令 uiMap 指令非常适合处理硬编码数据 mapOptions and myMarkers 但是

随机推荐

  • 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