模板驱动表单学习

简介: 模板驱动表单学习

Angular知识学习(一)中有讲述到表单的知识,不过那是最基础的演示,在之后的学习中又了解到模板驱动表单,所以考虑对之前的表单案例进行重构,完善表单功能,让案例更接近应用。


根据官网模板驱动表单的知识内容,我们重新构建人员登记表单,主要分为以下步骤:

  1. 创建 Uuser 模型类
  2. 创建控制此表单的组件
  3. 创建具有初始表单布局的模板。
  4. 使用 ngModel 双向数据绑定语法把数据属性绑定到每个表单输入控件。
  5. 往每个表单输入控件上添加 name 属性 (attribute)。
  6. 添加自定义 CSS 来提供视觉反馈。
  7. 显示和隐藏有效性验证的错误信息。
  8. 使用 ngSubmit 处理表单提交。
  9. 禁用此表单的提交按钮,直到表单变为有效。


创建User模型类


使用 Angular CLI 命令 ng g class 生成一个名叫 Uuer 的新类:


ng g class model/uuer
复制代码


内容如下:


export class Uuser {
  constructor(
    public name: string,
    public sex: string,
    public city: string,
    public hobbies: any[],
    public remark: string
  ) {
  }
}
复制代码


该类主要包含五个属性,分别是姓名、性别、城市、爱好和备注。其中爱好有多个,所以用数组来表示。


创建表单组件


使用 Angular CLI 命令 ng g component 生成一个名叫 UserForm 的新组件:


ng g component components/userForm
复制代码


因为模板驱动的表单位于它们自己的模块,所以在使用表单之前,需要将 FormsModule 添加到应用模块的 imports 数组中。对  app.module.ts  进行修改:


import { FormsModule }   from '@angular/forms';
  imports: [
    BrowserModule,
    FormsModule
  ]
复制代码


有两处更改


  1. 导入 FormsModule
  2. FormsModule 添加到 ngModule 装饰器的 imports 列表中,这样应用就能访问模板驱动表单的所有特性,包括 ngModel


关于表单内容的分析,官方文档写的非常详细,这里我只针对本案例中的难点进行分析,其他细节部分可以阅读官方文档。


user-form.component.html内容如下:


<h2>人员登记系统</h2>
<div class="container">
  <div [hidden]="submitted">
    <form (ngSubmit)="onSubmit()" #form="ngForm">
      <div class="form-group">
        <label for="name">姓 名</label>
        <input class="form-control" id="name" type="text" required name="name" [(ngModel)]="user.name" #name="ngModel">
        <span [hidden]="name.valid || name.pristine" class="alert alert-danger">Name is required</span>
      </div>
      <div class="form-group">
        <label>性 别 </label> &nbsp;
        <div class="radio-inline">
          <input type="radio" value="男" name="sex" id="man" [(ngModel)]="user.sex" > <label for="man">男</label>
        </div>
        <div class="radio-inline">
          <input type="radio" value="女" name="sex" id="woman" [(ngModel)]="user.sex" > <label for="woman">女</label>
        </div>
      </div>
      <div class="form-group">
        <label for="city">城 市</label>
        <select class="form-control" id="city" required name="city" [(ngModel)]="user.city" #city="ngModel">
          <option *ngFor="let ct of cities" [value]="ct">{{ct}}</option>
        </select>
        <span [hidden]="city.valid || city.pristine" class="alert alert-danger">City is required</span>
      </div>
      <div class="form-group">
        <label for="hobby">爱 好</label>&nbsp;
        <span *ngFor="let item of user.hobbies;let key=index" class="checkbox-inline">
          <input type="checkbox" [id]="'check'+key" [(ngModel)]="item.status" [name]="'check'+key"><label [for]="'check'+key">{{item.title}}</label>
          &nbsp;&nbsp;
        </span>
      </div>
      <div class="form-group">
        <label for="remark">备 注</label>
        <textarea class="form-control" id="remark" type="text" required name="remark" [(ngModel)]="user.remark" #remark="ngModel"></textarea>
        <span [hidden]="remark.valid || remark.pristine" class="alert alert-danger">remark is required</span>
      </div>
      <button type="submit" class="btn btn-success" [disabled]="!form.valid">Submit</button>
      <button type="button" class="btn btn-default" (click)="newUser();form.reset()">New User</button>
    </form>
  </div>
  <div [hidden]="!submitted">
    <h2>You submitted the following:</h2>
    <div class="row">
      <div class="col-xs-3">姓 名</div>
      <div class="col-xs-9">{{ user.name }}</div>
    </div>
    <div class="row">
      <div class="col-xs-3">性 别</div>
      <div class="col-xs-9">{{  user.sex }}</div>
    </div>
    <div class="row">
      <div class="col-xs-3">城 市</div>
      <div class="col-xs-9">{{  user.city }}</div>
    </div>
    <div class="row">
      <div class="col-xs-3">爱 好</div>
      <div class="col-xs-9">
        <span *ngFor="let item of user.hobbies">
          <span *ngIf="item.status == 1">{{item.title}}</span>&nbsp;&nbsp;
        </span>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-3">备 注</div>
      <div class="col-xs-9">{{  user.remark }}</div>
    </div>
    <br>
    <button class="btn btn-primary" (click)="submitted=false">Edit</button>
  </div>
</div>
复制代码


同官方文档中案例相比,本文的案例增加了单选框和多选框的应用,尤其是多选框,考虑到数据的双向绑定,所以必须对 user.hobbies 属性进行初始化,通过勾选前台按钮,来改变 user.hobbies的状态值,从而达到信息记录的目的。

user-form.component.ts内容如下:


import { Component, OnInit } from '@angular/core';
import { Uuser } from '../../model/uuser';
@Component({
  selector: 'app-user-form',
  templateUrl: './user-form.component.html',
  styleUrls: ['./user-form.component.css']
})
export class UserFormComponent implements OnInit {
  submitted = false;
  cities = ['北京', '上海', '广州 ', '深圳', '杭州', '武汉', '成都'];
  hobbies = ['唱歌', '跳舞', '跑步', '健身', '游泳'];
  user = new Uuser('', '男', this.cities[1], [], '');
  constructor() { }
  ngOnInit(): void {
    this.setHobbies();
  }
  //每个User对象都初始化hobby属性,只是status值默认为0,通过前台勾选来修改status
  setHobbies() {
    // tslint:disable-next-line: prefer-for-of
    for (let i = 0; i < this.hobbies.length; i++) {
      this.user.hobbies.push(
        {
          title: this.hobbies[i],
          status: 0
        }
      );
    }
  }
  onSubmit() {
    this.submitted = true;
  }
  newUser() {
    this.user = new Uuser('', '', '', [], '');
    this.setHobbies();
  }
}
复制代码


为了增加前台表单的观赏性,对表单的 CSS 样式做了一些修改。


首先是 styles.css,引入 Bootstrap 样式,对表单整体框架显示进行优化。


@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');
复制代码


其次是 user-form.component.css


h2{
  text-align: center;
}
.ng-valid[required], .ng-valid.required  {
  border-left: 5px solid #42A948; /* green */
}
.ng-invalid:not(form)  {
  border-left: 5px solid #a94442; /* red */
}
复制代码


可以在输入框的左侧添加带颜色的竖条,当在必填字段输入有效内容时,输入框左侧会变为绿色,否则视为无效,变为红色。


页面测试


对上述的代码进行验证,首先完整走一遍逻辑过程。


1.jpg


我们来梳理一下流程,首先是维护一个人的基本信息,点击 Submit 按钮提交表单,即跳转到人员信息查看页面,再点击 Exit 按钮退回到人员维护页面,点击 New User 按钮,清空页面内容,重新录入人员信息,再次提交表单可正常跳转到人员信息查看页面。


测试过程中可以发现,当必填项不填写内容,Submit 按钮始终是灰色的,即无法点击提交,此处是对整个表单进行有效验证,在实际应用中也是很有必要的。


关于性别单选框有一点需要注意:平时我们设置单选框的默认值,加上 checked 即可,但是由于在当前案例中使用了双向数据绑定,所以该属性不起作用,必须给 user.sex 设置默认值,从而实现单选框的默认选定。


关于爱好多选框,从数据双向绑定的角度来看,是无法向 user.hobbies 中增加数据,多选框的勾选只是状态值的改变,所以在 user-form.component.ts 文件中会增加一个 setHobbies 方法。


总结


本文对于 Angular 表单的使用进行了优化,利用框架特性来支持数据修改、验证和更多操作:


  • Angular HTML 表单模板。
  • 带有 @Component 装饰器的表单组件类。
  • 通过绑定到 NgForm.ngSubmit 事件属性来处理表单提交。
  • 模板引用变量,例如 #form#name
  • [(ngModel)] 语法用来实现双向数据绑定。
  • name 属性的用途是有效性验证和对表单元素的变更进行追踪。
  • 指向 input 控件的引用变量上的 valid 属性,可用于检查控件是否有效、是否显示/隐藏错误信息。
  • 通过绑定到 NgForm 的有效性状态,控制 Submit 按钮的禁用状态。
  • 定制 CSS 类来给用户提供无效控件的视觉反馈。



目录
相关文章
|
5天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
|
3天前
|
云安全 人工智能 自然语言处理
阿里云x硅基流动:AI安全护栏助力构建可信模型生态
阿里云AI安全护栏:大模型的“智能过滤系统”。
|
4天前
|
人工智能 自然语言处理 自动驾驶
关于举办首届全国大学生“启真问智”人工智能模型&智能体大赛决赛的通知
关于举办首届全国大学生“启真问智”人工智能模型&智能体大赛决赛的通知
|
5天前
|
Linux 虚拟化 iOS开发
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
983 4
VMware Workstation Pro 25H2 for Windows & Linux - 领先的免费桌面虚拟化软件
|
7天前
|
存储 机器学习/深度学习 人工智能
大模型微调技术:LoRA原理与实践
本文深入解析大语言模型微调中的关键技术——低秩自适应(LoRA)。通过分析全参数微调的计算瓶颈,详细阐述LoRA的数学原理、实现机制和优势特点。文章包含完整的PyTorch实现代码、性能对比实验以及实际应用场景,为开发者提供高效微调大模型的实践指南。
617 2
|
5天前
|
JavaScript API 开发工具
如何在原生App中调用Uniapp的原生功能?
如何在原生App中调用Uniapp的原生功能?
307 139
|
4天前
|
编解码 自然语言处理 文字识别
Qwen3-VL再添丁!4B/8B Dense模型开源,更轻量,仍强大
凌晨,Qwen3-VL系列再添新成员——Dense架构的Qwen3-VL-8B、Qwen3-VL-4B 模型,本地部署友好,并完整保留了Qwen3-VL的全部表现,评测指标表现优秀。
388 7
Qwen3-VL再添丁!4B/8B Dense模型开源,更轻量,仍强大