[Angular 基础] - 表单:模板驱动表单

news2024/11/6 3:04:39

[Angular 基础] - 表单:模板驱动表单

之前的笔记:

  • [Angular 基础] - routing 路由(上)

  • [Angular 基础] - routing 路由(下)

  • [Angular 基础] - Observable


Angular 内置两种表单的支持,这篇写的就是第一种,即模板驱动表单 (Template-Driven Form)

Template-Driven Form 的实现比较简单,Angular 自身会生成和提供对应的表单控制和管理状态,对于开发者来说,实现相对而言更加简单,因此更加适合简单的表单实现

做一个平行对比,React 有 uncontrolled form & controlled form,Angular 有一个 Template-Driven Form 和 Reactive form,虽然这么看起来两个实现似乎挺像的,不过本质上还是有些区别的

对于 React 的 uncontrolled form,它本质上是让 HTML 去进行处理,然后可以通过 refs 去获得 HTML 的值,React 不会对其进行太多的干预;对比 Angular 的 Template-Driven Form,Angular 本身通过 绑定 对状态进行了接管

开始

下面是一个表单:

<div class="container">
  <div class="row">
    <div class="col-xs-12 col-sm-10 col-md-8 col-sm-offset-1 col-md-offset-2">
      <form>
        <div id="user-data">
          <div class="form-group">
            <label for="username">Username</label>
            <input type="text" id="username" class="form-control" />
          </div>
          <button class="btn btn-default" type="button">
            Suggest an Username
          </button>
          <div class="form-group">
            <label for="email">Mail</label>
            <input type="email" id="email" class="form-control" />
          </div>
        </div>
        <div class="form-group">
          <label for="secret">Secret Questions</label>
          <select id="secret" class="form-control">
            <option value="pet">Your first Pet?</option>
            <option value="teacher">Your first teacher?</option>
          </select>
        </div>
        <button class="btn btn-primary" type="submit">Submit</button>
      </form>
    </div>
  </div>
</div>

表单渲染如下:

在这里插入图片描述

相别于直接使用 JS+HTML 的方式实现表单,使用 Angular 这里没有添加 action 这一 action,这一步会让 Angular 去安排。

这一点在之前也提过,需要实现这一点,也需要在 app.module.ts 中导入 FormsMorule

绑定控制

这里主要使用 ngModelname="" 绑定所有输入:

<label for="username">Username</label>
<input type="text" id="username" class="form-control" ngModel name="username" />

<label for="email">Mail</label>
<input type="email" id="email" class="form-control" ngModel name="email" />

<label for="secret">Secret Questions</label>
<select id="secret" class="form-control" ngModel name="secret">
  <option value="pet">Your first Pet?</option>
  <option value="teacher">Your first teacher?</option>
</select>

我这里省略了其他部分的代码,只留下了对应的 labelinput

⚠️:name 是一个 HTML attribute,这个实现本质上跟 angular 没什么关系。但是在 Template-Driven Form 中它的作用非常的重要。它会用来创建一个 FormControl 的实例对象,对 ngModel 进行自动绑定,这也是为什么这里的 ngModel 使用语法和 2 way binding 里的不一样。可以说,没有 name,那么 Angular 就无法正确识别对应的 ``FormControl`,也无法接管对应的 onChange 和 validation

提交表单 AKA 获取提交的值

这里提交表单的做法不是之前实现的那样,在 submit button 上绑定 onclick 事件,而是使用 angular 提供的 directive 去实现——这也是为什么这种实现叫 Template-Driven Form,代码修改如下:

  • V 层

    <form (ngSubmit)="onSubmit(f)" #f="ngForm">
      <!-- 其余不变 -->
    </form>
    
  • VM 层

    onSubmit(form: NgForm) {
        console.log('submitted');
        console.log(form);
    }
    

输出结果如下:

在这里插入图片描述

注意这里的 local reference 用法,它绑定了一个特殊的 ngForm directive,因此传到 onSubmit 中的值就是 NgForm。默认情况下它的值是当前 HTML 元素类型,表单对应的元素类型是 HTMLFormElement

通过 NgForm 就可以轻松的获取当前表单的值、控制(即 FormControl)、验证等。这些值可以杯统称为当前表单的 状态

验证

这里主要是通过 angular 提供的验证搭配 HTML5 的验证进行实现,代码修改如下:

<div class="form-group">
  <label for="username">Username</label>
  <input
    type="text"
    id="username"
    class="form-control"
    ngModel
    name="username"
    required
  />
</div>
<input
  type="email"
  id="email"
  class="form-control"
  ngModel
  name="email"
  required
  email
/>

这里主要用了 angular 提供的 requiredemail 这两个 validators。这个时候表单还是可以继续提交的,输出结果如下:

在这里插入图片描述

可以看到,当前 NgFormsubmittedtrueinvalid 也是 true

这个时候就可以利用一下这个 invalid 属性:

<button class="btn btn-primary" type="submit" [disabled]="f.invalid">
  Submit
</button>

这样可以阻止用户在表单验证未通过的情况下,点击 submit 事件:

修改报错样式

这里主要通过 CSS 实现,修改代码如下:

input.ng-invalid.ng-touched,
select.ng-invalid.ng-touched {
  border: 1px solid red;
}

实现效果如下:

在这里插入图片描述

这里也是通过 angular 提供的 class 进行的实现。angular 会在对应的元素上添加对应的状态,touched 指的是用户是否触碰(focus)过当前元素。

报错信息

这里使用 ngIf 搭配对应的 directive 进行报错信息的渲染,代码修改如下:

<div class="form-group">
  <label for="email">Mail</label>
  <input
    type="email"
    id="email"
    class="form-control"
    ngModel
    name="email"
    autocomplete="off"
    required
    email
    #email="ngModel"
  />
  <span class="help-block" *ngIf="email.invalid && email.touched"
    >Please enter a valid email!</span
  >
</div>

效果如下:

在这里插入图片描述

⚠️:这里依旧使用 local reference+绑定 directive 的方法实现,不过这里绑定的是 ngModel 而不是 ``NgForm`

设置默认值

这里主要就是设置一下默认值,使用的是 property binding,代码如下:

<div class="form-group">
  <label for="secret">Secret Questions</label>
  <select id="secret" class="form-control" [ngModel]="'pet'" name="secret">
    <option value="pet">Your first Pet?</option>
    <option value="teacher">Your first teacher?</option>
  </select>
</div>

实现效果如下:

在这里插入图片描述

⚠️:这里使用的是 property binding,即 [ngModel] 提供默认值,而不是提供 2-way binding——在有需求的情况下还是可以使用 2-way binding 的。使用 property binding 并不会修改 VM 层中的数据。

👀:也可以在 VM 层实现一个变量,而不是直接将值写到 V 层中

组合 form control

这可以将一些数据组合在一起,比如说地址的组合通常为省+市+具体地址+邮编才能组合成一个完整的地址,实现方法如下:

<div id="user-data" ngModelGroup="userData">
  <div class="form-group">
    <label for="username">Username</label>
    <input
      type="text"
      id="username"
      class="form-control"
      ngModel
      name="username"
      required
    />
  </div>
  <button class="btn btn-default" type="button">Suggest an Username</button>
  <div class="form-group">
    <label for="email">Mail</label>
    <input
      type="email"
      id="email"
      class="form-control"
      ngModel
      name="email"
      autocomplete="off"
      required
      email
      #email="ngModel"
    />
    <span class="help-block" *ngIf="email.invalid && email.touched"
      >Please enter a valid email!</span
    >
  </div>
</div>

效果如下:

在这里插入图片描述

⚠️:这里的语法为 ngModelGroup="string-value"

👀:这里同样可以添加 local reference,如: #userData="ngModelGroup" 这样就可以对整个 group 进行提示,如:

<p *ngIf="userData.invalid && userData.touched">User Data is invalid!</p>

这个报错信息只有在用户提供了正确的用户名和邮箱之后才会消失:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

radio button

这里没有什么特别的地方,不过可以搭配 ngFor 让整个实现变得简单一些。

假设 VM 层有一个数组包含 [male, female] 这两个数据,V 层渲染如下:

<div class="form-group" *ngFor="let gender of genders">
  <label for=""></label>
  <input type="radio" name="gender" ngModel [value]="gender" />
  {{ gender }}
</div>

结果如下:

在这里插入图片描述

使用 @ViewChild 获取表单

这里补充另一种可以获取表单的方式,就是通过 @ViewChild 进行绑定

@ViewChild 的使用在之前的笔记提过: [Angular 基础] - 视图封装 & 局部引用 & 父子组件中内容传递

使用方法如下:

export class AppComponent {
  @ViewChild('f') signupForm: NgForm;

  suggestUserName() {
    const suggestedName = 'Superuser';
  }

  // onSubmit(form: NgForm) {
  //   console.log('submitted');
  //   console.log(form);
  // }

  onSubmit(f: NgForm) {
    console.log(this.signupForm);
  }
}

输出结果如下:

在这里插入图片描述

使用 @ViewChild

使用 @ViewChild 可以提供更加灵活的使用,比如说 UI 上的一个 Suggest an Username 按钮,我想要点一下这里就生成一个用户名时,就可以使用 @ViewChild 去实现。

这里会提供两种写法,根据业务需求去使用,具体使用方式在注释里:

  • VM 层

    suggestUserName() {
      const suggestedName = 'Superuser';
      // 这里必须提供所有的值,否则会报错,因此就有可能会重写所有值的问题
      this.signupForm.setValue({
        userData: {
          username: suggestedName,
          email: '',
        },
        secret: 'pet',
        gender: 'male',
      });
    
      // 这里就是打个补丁,不会重写所有的值
      this.signupForm.form.patchValue({
        userData: {
          username: suggestedName,
        },
      });
    }
    

    这里直接使用 setValue 去设置一个与提交格式对应的表单

  • V 层

    <button class="btn btn-default" type="button" (click)="suggestUserName()">
      Suggest an Username
    </button>
    

    这里绑定 click 事件

这里的效果是重写所有值,动图如下:

在这里插入图片描述

⚠️:还是可以使用 event handler+2-way data binding 去实现的,只是对于 Template-Driven Form 来说,这种实现方法最方便

使用表单数据

这里还是需要创建一个新的对象用来 match 提交的表单数据,从而完成渲染,实现如下:

  • V 层

    <div class="row" *ngIf="f.submitted">
      <div class="col-xs-12">
        <h3>Your Data</h3>
        <p>Username: {{ user.username }}</p>
        <p>Email: {{ user.email }}</p>
        <p>Secret Question: {{ user.secret }}</p>
        <p>Gender: {{ user.gender }}</p>
      </div>
    </div>
    
  • VM 层

    onSubmit(f: NgForm) {
        this.user.username = f.value.userData.username;
        this.user.email = f.value.userData.email;
        this.user.secret = f.value.secret;
        this.user.gender = f.value.gender;
    }
    

渲染如下:

获取数据进行渲染

这里的逻辑是只有在提交的时候会对数据进行赋值,所以直接检查 f.submitted 也不会造成修改表单数据就让下面的数据区域重新渲染的结果。

重置表单

这里就一行代码:

this.signupForm.reset();

效果如下:

在这里插入图片描述

⚠️:reset 中也可以传值,用法和 setValue 类似

reference

  • Validators

    angular 的所有 validators

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1505653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

wps由于找不到krpt.dll,无法继续执行代码的解决方法

遇到由于找不到krpt.dll,无法继续执行代码的问题时&#xff0c;理解如何修复这个问题变得至关重要。本文会教大家krpt.dll的恢复流程&#xff0c;并介绍该DLL文件的相关属性。我们将一步步指导你如何处理缺失文件的情况&#xff0c;让你能够解决阻碍代码正常运行的障碍&#xf…

C语言初学10:typedef

一、作用 为用户定义的数据类型取一个新名字 二、对结构体使用typedef定义新的数据类型名字 #include <stdio.h> #include <string.h>typedef struct Books //使用 typedef 来定义一个新的数据类型名字 {char title[50];} book;int main( ) {//book是typedef定…

背包问题算法

背包问题算法 0-1背包问题二维数组一维数组 完全背包问题二维数组一维数组 多重背包问题一维数组 0-1背包问题 问题&#xff1a;背包的容量为9&#xff0c;有重量分别为[2, 4, 6, 9]的四个物品&#xff0c;价值分别为[3, 4, 5, 6]&#xff0c;求背包能装的物品的最大价值是多少…

LiveNVR监控流媒体Onvif/RTSP功能-支持云端录像监控视频集中存储录像回看录像计划配置NVR硬件设备录像回看

LiveNVR支持云端录像监控视频集中存储录像回看录像计划配置NVR硬件设备录像回看 1、流媒体服务软件2、录像回看3、查看录像3.1、时间轴视图3.2、列表视图 4、如何分享时间轴录像回看&#xff1f;5、iframe集成示例7、录像计划7、相关问题7.1、录像存储位置如何配置&#xff1f;…

【电路】工作于直流4.5V电压的声控小灯

这个声控小灯用于控制4.5V直流供电的小灯泡&#xff0c;可用作学生实验也可用作声控夜光小灯。电路主要由5G555时基集成电路和一些分立元件组成&#xff0c;如下图所示&#xff1a; 工作原理 压电陶瓷片B与晶体三极管VT1&#xff0c;电阻R1&#xff0c;和电阻R2等组成了声控脉…

数据结构之单链表及其实现!

目录 ​编辑 1. 顺序表的问题及思考 2.链表的概念结构和分类 2.1 概念及结构 2.2 分类 3. 单链表的实现 3.1 新节点的创建 3.2 打印单链表 3.3 头插 3.4 头删 3.5 尾插 3.6 尾删 3.7 查找元素X 3.8 在pos位置修改 3.9 在任意位置之前插入 3.10 在任意位置删除…

【Primsjs】vue+代码高亮

效果 括号变色括号鼠标移入高亮效果 代码效果 目录树 在这里插入图片描述 安装 cnpm i primsjs简介 文档&#xff08;点我进去&#xff09; 备用地址-https://prismjs.com/docs/index.html Primsjs使用 导入配置插件 注意&#xff1a;需要什么插件就导入什么插件 码 &l…

Linux centos6安装rz、sz命令

centos6传文件提示command not found # yum install lrzsz 提示错误 wget http://www.ohse.de/uwe/releases/lrzsz-0.12.20.tar.gz 下载离线包 https://www.ohse.de/uwe/software/lrzsz.html 下载最新版本 [rootnode1 ~]# tar -zxvf lrzsz-0.12.20.tar.gz …

PV与PVC知多少?解锁CKA认证考点攻略!

往期精彩文章 : 提升CKA考试胜算&#xff1a;一文带你全面了解RBAC权限控制&#xff01;揭秘高效运维&#xff1a;如何用kubectl top命令实时监控K8s资源使用情况&#xff1f;CKA认证必备&#xff1a;掌握k8s网络策略的关键要点提高CKA认证成功率&#xff0c;CKA真题中的节点维…

蓝桥省赛倒计时 35 天-双指针

双指针介绍 双指针算法是一种常用的算法技巧&#xff0c;它通常用于在数组或字符串中进行快速查找、匹配、排序或移动操作。 pointer 双指针并非真的用指针实现&#xff0c;一般用两个变量来表示下标&#xff08;在后面都用指针来表示&#xff09;。 双指针算法使用两个指针在数…

【Java JVM】Class 文件

Java 的口号 “一次编写, 到处运行 (Write Once, Run Anywhere)” 的基础: JVM 和 所有平台都统一支持的程序存储格式 – 字节码 (Byte Code)。 只要在对应的平台安装对应的 JVM, 将我们编写的源码编译为 Class 文件, 就能达到了一次编写, 导出运行的目标, 中间的所有细节由不同…

0103n阶行列式-行列式-线性代数

文章目录 一 n阶行列式二 三阶行列式三 特殊行列式结语 一 n阶行列式 ∣ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋯ ⋯ ⋯ ⋯ a n 1 a n 2 ⋯ a n n ∣ \begin{vmatrix}a_{11}&a_{12}&\cdots&a_{1n}\\a_{21}&a_{22}&\cdots&a_{2n}\\\cdots&\cdots…

大型房企知识竞赛活动方案

&#xff08;一&#xff09; 线上挑战赛 1、加入团队 个人可以依据自身情况选择加入初始团队&#xff0c;也可创建团队。 2、题库来源 参考权威题库&#xff0c;适当加入公司帮扶贫困县的相关历史数据题目 3、小程序活动专题页 模块包括&#xff1a;党史知识线上挑战赛、活动宣…

Unity 采用自定义通道ShaderGraph实现FullScreen的窗户雨滴效果

效果如下 ShaderGraph实现 N21 随机化 DragLayer分层 将DragLayer分成四层&#xff0c;分别调整每层的缩放和大小 Shader实现的链接&#xff08;Unity 雨水滴到屏幕效果&#xff09; 我也是参考这个实现Shader Graph

Android7.1 ANR error 弹窗处理

Android7.1 ANR error 弹窗处理 问题描述解决方法 郑重声明:本人原创博文&#xff0c;都是实战&#xff0c;均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip OS:Android 7.1.2 Kernel: 3.10 问题描述 有时会用到第三方apk&#xff0c;内置到系统中&…

通信-CAN-00 标准概述

总结了下CAN的基本知识&#xff0c;实际CAN的标准&#xff0c;内容&#xff0c;工具使用&#xff0c;上位机开发&#xff0c;下位机开发等&#xff0c;后续会找时间慢慢更新。本文主要介绍CAN标准&#xff0c;并对11898进行了进一步的介绍。 1 CAN概念 CAN-Controller Area N…

C++ 多状态dp

目录 按摩师 打家劫舍 打家劫舍2 删除并获得点数 粉刷房子 按摩师 面试题 17.16. 按摩师 最大值问题 f : 预约此次的最长时间 g &#xff1a;不预约此次的最长时间 出现的错误&#xff1a;return max(f[n - 1]), g[n - 1]); 注意&#xff1a;①题目没给nums的范围&…

软件杯 图像识别-人脸识别与疲劳检测 - python opencv

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是…

【C++】深度解剖多态

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是多态&#xff0c;熟练掌握多态的定义&a…

预约自习室

预约自习室 1、技术介绍 自习室预约系统的后端开发语言采用Node&#xff0c;后端开发框架采用Express&#xff0c;数据库采用的Node的最佳搭档MySQL。采用Vue作为前端开发框架&#xff0c;Element-UI作为开发的组件库&#xff0c;微信小程序。期间采用axios实现网页数据获取&a…