表单

Posted wpengch1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了表单相关的知识,希望对你有一定的参考价值。

注:学习使用,禁止转载

表单是最重要也是最复杂的

表单可能是WEB世界里最重要的,通过表单获取用户的输入.另一方面,表单看起来又是简单的,你放置一个input标签,一个submit按钮,然后点击按钮,提交,有什么难的呢?
实践证明,表单确实是很复杂的,理由如下:

  • 表单输入意味着修改服务器与客户端的数据.
  • 改变通常会需要去反映到其他的地方
  • 用户会随意输入不同的值,所以需要校验
  • 如果需要,需要清楚地标明错误
  • 依赖字段具有复杂的逻辑
  • 不依靠DOM选择器,我们不能测试表单

幸运的是,angular2提供了解决这些问题的工具:

  • Controls封装了输入,并且提供一个与它们工作的对象
  • Validators给了我们一个校验工具
  • Observers让我们可以检测我们的表单,并且根据需要作出反应

在这一章,我们会深入学习表单.

Controls 和 Control Groups

ng2中,表单的两个基础设施就是Controls 和 Control Groups

Controls

Controls代表一个简单的输入域,它是ng2中表单的最小单元.

Controls封装了字段的值和有效,修改,错误的状态.

比如,我们会在Typescript中像下面一样使用Control

// create a new Control with the value "Nate"
let nameControl = new Control("Nate");

let name = nameControl.value; // -> Nate

// now we can query this control for certain values:
nameControl.errors // -> StringMap<string, any> of errors
nameControl.dirty // -> false
nameControl.valid // -> true
// etc.

为了构建表单,我们创建Control或者Control group,然后添加元数据和逻辑给它们.

像angular中的其他事情一样,我们有一个类(在本例子中是Control),我们会作为属性添加给DOM(本例子中是ngControl),如下:

<!-- part of some bigger form -->
<input type="text" ngControl="name" />

在我们的表单上下文中新建一个Control对象.

ControlGroup

许多表单都不止一个值,所以我们需要管理很多Controls,如果我们希望去校验我们的表单,去迭代每一个Control并校验是笨重的.为了解决这个问题,ControlGroup提供了一个封装.

下面我们创建一个ControlGroup.

let personInfo = new ControlGroup(
 firstName: new Control("Nate"),
 lastName: new Control("Murray"),
 zip: new Control("90210")
 )

ControlGroup和Control有一个公共的父类(AbstractControl).也就是说我们可以像一个简单Control一样检查personInfo的值和状态.

personInfo.value; // -> 
 // firstName: "Nate",
 // lastName: "Murray",
 // zip: "90210"
 //

 // now we can query this control group for certain values, which have sensible
 // values depending on the children Control's values:
 personInfo.errors // -> StringMap<string, any> of errors
 personInfo.dirty // -> false
 personInfo.valid // -> true
 // etc.

注意,当我们获取ControlGroup的值时,我们会获取到一个key-value的对象.

第一个表单

这里有许多创建表单的重要方面我们没有提及,下面我们来逐一学习.

下面这个是我们将要创建的一个简单的表单:

在我们的理解中,我们创建一个电子商务型的网站,我们会列出我们出售的产品列表.在这个例子中,我们需要存储产品的SKU码,因此,我们创建一个获取SKU码的简单表单,它仅仅只有一个输入域.

我们的表单是非常简单的,它只有一个input和一个sumbit按钮.

让我们将这个表单组装成一个组件,如果你没有忘记,创建组件的三个部分为:

  • 配置
  • 创建模板
  • 实现组件类

Simple SKU,配置选项

code/forms/app/forms/demo_form_sku.ts

import  Component  from '@angular/core';
import  FORM_DIRECTIVES  from '@angular/common';
@Component(
  selector: 'demo-form-sku',
  directives: [FORM_DIRECTIVES],

注意,这里我们导入了FORM_DIRECTIVES,FORM_DIRECTIVES是一个指令组,它包含:

  • ngControl
  • ngControlGroup
  • ngForm
  • ngModel

我们没有怎么使用这些指令,也没有说明它们能做什么.但是,现在,只需要知道我们需要使用这些指令就可以了.

Simple SKU模板

code/forms/app/ts/forms/demo_form_sku.ts

<div class="ui raised segment">
    <h2 class="ui header">Demo Form: Sku</h2>
    <form #f="ngForm" (ngSubmit)="onSubmit(f.value)" class="ui form">
      <div class="field">
        Forms in Angular 2 127
        <label for="skuInput">SKU</label>
        <input type="text" id="skuInput" placeholder="SKU" ngControl="sku">
        <button type="submit" class="ui button">Submit</button>
      </div>
    </form>
  </div>

form 和 NgForm

注意,我们导入了FORM_DIRECTIVES,所以我们可以使用ngForm添加到form上面.

NgForm做的事情是有好多好处的,而且也不明显.

导入FORM_DIRECTIVES后,ngForm会自动隐士地添加到任何的form标签上面.

ngForm给了我们两个东西:

  • 一个命名为ngForm的ControlGroup
  • 一个ngSubmit的输出

你可以在我们的代码中看到,我们使用了这两个东西:

<form #f="ngForm" (ngSubmit)="onSubmit(f.value)" class="ui form">
</form>

首先我们使用#f=”ngForm”创建了名为ngForm的表单,#v=thing说明,我们希望使用本地变量v代表这个view.

这里,我们创建了ngForm的标明,并绑定到变量f上.

我们的ngForm指令从哪里来? 它来自于NgForm指令.
ngForm的类型是什么?它是ControlGroup.这意味着我们可以将我们的视图作为ControlGroup使用.(ngSubmit)代表的是我们提交的时候需要做的事情.

所有添加起来的理解就是,当我们提交表单的时候,传递ControlGroup的value作为参数,调用我们组建的onSubmit方法.

input和ngControl

在我们讨论ngControl前,input标签有一些事情是我们感兴趣的.

code/forms/app/ts/forms/demo_form_sku.ts

<form #f="ngForm" (ngSubmit)="onSubmit(f.value)" class="ui form">
      <div class="field">
        Forms in Angular 2 127
        <label for="skuInput">SKU</label>
        <input type="text" id="skuInput" placeholder="SKU" ngControl="sku">
        <button type="submit" class="ui button">Submit</button>
      </div>
    </form>
  • class=”ui form”是可选的
  • for属性与input的id是对应的
  • placeholder是当用户没有输入的时候用来提示用的.

NgControl指令是用来标识ngControl这个选择器.这就意味着我们的input标签添加了这个属性:ngControl=”whatever”,这个例子中是sku.

NgControl会自动创建一个Control添加给父组件,这个例子中是ControlGroup.然后绑定这个DOM元素给Control.也就是说,我们通过名字sku将input与Control进行关联.

Simple SKU Form:组件定义

code/forms/app/ts/forms/demo_form_sku.ts

export class DemoFormSku  onSubmit(form: any): void 
    console.log('you submitted value:', form);
  

试试

将所有代码合起来,像下面这样:
code/forms/app/ts/forms/demo_form_sku.ts

import  Component  from '@angular/core';
import  FORM_DIRECTIVES  from '@angular/common';

@Component(
  selector: 'demo-form-sku',
  directives: [FORM_DIRECTIVES],
  template: `
  <div class="ui raised segment">
    <h2 class="ui header">Demo Form: Sku</h2>
    <form #f="ngForm"
          (ngSubmit)="onSubmit(f.value)"
          class="ui form">

      <div class="field">
        <label for="skuInput">SKU</label>
        <input type="text"
               id="skuInput"
               placeholder="SKU"
               ngControl="sku">
      </div>

      <button type="submit" class="ui button">Submit</button>
    </form>
  </div>
  `
)
export class DemoFormSku 
  onSubmit(form: any): void 
    console.log('you submitted value:', form);
  

如果你运行这个程序,浏览器中会显示下面这样:

使用FormBuilder

隐式使用ngForm和ngControl是方便的,但是它没有给予我们太多的可自定义选项,通常,我们会使用一个更加复杂的方式创建form,那就是FormBuilder.

FormBuilder是一个帮助类,帮助我们创建表单.你可以将其理解为工厂方法.

让我们将FormBuilder添加到我们前面的例子中,看下面:

  • 在我们组件定义类中怎么使用FormBuilder
  • 是view中怎么使用自定义的ControlGroup.

怎么使用FormBuilder

在我们的组件类构造器参数中注入一个参数.
code/forms/app/ts/forms/demo_form_sku_with_builder.ts

export class DemoFormSkuBuilder  myForm: ControlGroup;
  constructor(fb: FormBuilder) 
    this.myForm = fb.group(
    'sku': ['ABC123'] );

onSubmit(value: string): void  
    console.log('you submitted value: ', value);

注入之后,一个FormBuilder的实例将会被创建,并且我们将其分配给局部变量fb.

我们将会使用FormBuilder的两个主要方法:

  • control:创建一个新的Control
  • group: 创建一个新的ControlGroup

注意,我们在本例子中使用myForm局部变量代表我们的表单.

myForm的类型是ControlGroup.通过fb.group创建的.group的参数是一个key-value,它代表这个ControlGroup里面的Control.在这个例子中,我们设置了一个名字为sku的Control,它的值是ABC123.

现在,我们有一个myForm的ControlGroup,我们需要使用它(通过绑定它到我们的form元素).

在view中使用myForm

我们希望改变去使用我们的myForm.如果没有忘记,在上面,我们说,导入FORM_DIRECTIVES时,ngForm会自动应用到我们的form元素上.我们也注意到了,ngForm创建了它自己的ControlGroup.好了,在这个例子中,我们不希望使用外部的ControlGroup,我们希望使用我们自己定义的myForm.我们应该怎么做?

当我们需要使用我们自己的ControlGroup时,angular提供了另外一种方式:它叫着ngFormModel,并且我们可以像下面这样使用它:
code/forms/app/ts/forms/demo_form_sku_with_builder.ts

 <form [ngFormModel]="myForm"
          (ngSubmit)="onSubmit(myForm.value)"

这里,我们使用ngFormModel告诉angular,我们将会使用myForm到这个上.

我们需要使用myForm的onSubmit代替f的onSubmit,最后一件事情就是绑定我们的Control到input上,使用NgFormControl.

code/forms/app/ts/forms/demo_form_sku_with_builder.ts

<input
type="text"
id="skuInput"
placeholder="SKU"
[ngFormControl]="myForm.controls['sku']">

试试

所有代码:
code/forms/app/ts/forms/demo_form_sku_with_builder.ts

import  Component  from '@angular/core';
import 
  FORM_DIRECTIVES,
  FormBuilder,
  ControlGroup
 from '@angular/common';

@Component(
  selector: 'demo-form-sku-builder',
  directives: [FORM_DIRECTIVES],
  template: `
  <div class="ui raised segment">
    <h2 class="ui header">Demo Form: Sku with Builder</h2>
    <form [ngFormModel]="myForm" 
          (ngSubmit)="onSubmit(myForm.value)"
          class="ui form">

      <div class="field">
        <label for="skuInput">SKU</label>
        <input type="text" 
               id="skuInput" 
               placeholder="SKU"
               [ngFormControl]="myForm.controls['sku']">
      </div>

    <button type="submit" class="ui button">Submit</button>
    </form>
  </div>
  `
)
export class DemoFormSkuBuilder 
  myForm: ControlGroup;

  constructor(fb: FormBuilder) 
    this.myForm = fb.group(
      'sku': ['ABC123']
    );
  

  onSubmit(value: string): void 
    console.log('you submitted value: ', value);
  

记住:
隐式创建ControlGroup和Control使用:
- ngForm
- ngControl
绑定存在的ControlGroup和Control使用:
- ngFormModel
- ngFormControl

添加校验器

用户经常不会按正确的模式输入正确的数据,如果用户没有按正确的格式输入数据,我们可以给用户一个反馈,并且让其不能提交,这种情况下,我们使用校验器(Validators).
Validators被Validators模块提供,而且,最简单的Validators是Validators.required.
Validators.require标明该Control是必填字段.

为了使用Validators,我们必须做两件事情.

  • 分配一个Validator给Control
  • 检查Validator状态,并给出反馈
let control = new Control('sku', Validators.required);

在我们的例子中,由于使用了FormBuilder,所以可以像下面这样:
code/forms/app/ts/forms/demo_form_with_validations_explicit.ts

constructor(fb: FormBuilder) 
    this.myForm = fb.group(
      'sku':  ['', Validators.required]

现在,我们需要使用Validators到我们的view上,在View上,有两种方式访问Validators.

  • 我们可以明确分配一个Control sku到我们类的实例,这个是比较啰嗦的,但是更加灵活.
  • 在view中,我们从myForm获取Control sku,这中方式,在Component类中的代码量少,但是在view中的代码多.

为了分析他们的区别,我们看下面的例子.

显示的将Control变量设置为实例变量

如下:

更加灵活的方式是将Controls分开成单独的Control放在组件类中,下面是我们的类代码:
code/forms/app/ts/forms/demo_form_with_validations_explicit.ts

export class DemoFormWithValidationsExplicit 
  myForm: ControlGroup;
  sku: AbstractControl;

  constructor(fb: FormBuilder) 
    this.myForm = fb.group(
      'sku':  ['', Validators.required]
    );

    this.sku = this.myForm.controls['sku'];
  

  onSubmit(value: string): void 
    console.log('you submitted value: ', value);
  

注意:

  • 我们在类中定义sku: AbstractControl
  • 当使用FormBuilder创建之后,我们分配给sku.

这是一个很好的注意,它表明我们可以在我们的view的任何地方使用sku.现在,我们的sku是可以被validated.我们希望找到不同的方式去使用它.

  • 校验所有的form,提供一条信息
  • 分开校验每一个Control,并提供一条信息
  • 分开校验每一个Control,当不合法的时候使用红色标注
  • 分开校验每一个Control,是否已经输入,如果没有输入,显示一条信息

表单信息

我们可以通过检查myForm.valid来校验myForm.

code/forms/app/ts/forms/demo_form_with_validations_explicit.ts

<div *ngIf="!myForm.valid"

字段信息

我们也可以在字段不符合规定的时候显示一条信息
code/forms/app/ts/forms/demo_form_with_validations_explicit.ts

[ngFormControl]="sku">
         <div *ngIf="!sku.valid"

字段颜色

我们也可以标注字段的颜色,当不合法的时候.

code/forms/app/ts/forms/demo_form_with_validations_explicit.ts

<div class="field"
          [class.error]="!sku.valid && sku.touched">

指定特定的校验器

当需要依靠特定的校验器时,也可以制定.通过hasError()来实现.
code/forms/app/ts/forms/demo_form_with_validations_explicit.ts

class="ui error message">SKU is invalid</div> 
<div *ngIf="sku.hasError('required')"

所有代码

code/forms/app/ts/forms/demo_form_with_validations_explicit.ts

/* tslint:disable:no-string-literal */
import  Component  from '@angular/core';
import 
  CORE_DIRECTIVES,
  FORM_DIRECTIVES,
  FormBuilder,
  ControlGroup,
  Validators,
  AbstractControl
 from '@angular/common';

@Component(
  selector: 'demo-form-with-validations-explicit',
  directives: [CORE_DIRECTIVES, FORM_DIRECTIVES],
  template: `
  <div class="ui raised segment">
    <h2 class="ui header">Demo Form: with validations (explicit)</h2>
    <form [ngFormModel]="myForm"
          (ngSubmit)="onSubmit(myForm.value)"
          class="ui form">

      <div class="field"
          [class.error]="!sku.valid && sku.touched">
        <label for="skuInput">SKU</label>
        <input type="text"
               id="skuInput"
               placeholder="SKU"
               [ngFormControl]="sku">
         <div *ngIf="!sku.valid"
           class="ui error message">SKU is invalid</div>
         <div *ngIf="sku.hasError('required')"
           class="ui error message">SKU is required</div>
      </div>

      <div *ngIf="!myForm.valid"
        class="ui error message">Form is invalid</div>

      <button type="submit" class="ui button">Submit</button>
    </form>
  </div>
  `
)
export class DemoFormWithValidationsExplicit 
  myForm: ControlGroup;
  sku: AbstractControl;

  constructor(fb: FormBuilder) 
    this.myForm = fb.group(
      'sku':  ['', Validators.required]
    );

    this.sku = this.myForm.controls['sku'];
  

  onSubmit(value: string): void 
    console.log('you submitted value: ', value);
  

显示设置sku Controls作为组件类的实例变量

像我们上面一样,我们在组件类中创建了一个实例变量存储每一个input标签.

但是我们能不能不创建实例变量而直接浏览Control呢?答案是肯定的.我们会学习浏览Form的其他方式.

让我们看看另外的例子.

code/forms/app/ts/forms/demo_form_with_validations_shorthand.ts

export class DemoFormWithValidationsShorthand  
    myForm: ControlGroup;
      constructor(fb: FormBuilder) 
            this.myForm = fb.group(
                'sku': ['', Validators.required] );
      
      onSubmit(value: any): void 
        console.log('you submitted value:', value.sku);
      

这两个例子的代码有点像,但是仔细看,你会发现,sku: AbstractControl已经不在这里面了.

让我们看看这三个字段级别的校验器,跟上面的作为对比.

声明一个本地sku引用

因为我们没有直接定义一个本地变量保存sku引用,所以我们需要一种获得它引用的方式,这里有两种方式:

  • 通过myForm.find
  • 通过ngFormControl

myForm.find

myForm具有一个find函数,它可以通过路径找到它的子元素,然后校验他们.

code/forms/app/ts/forms/demo_form_with_validations_shorthand.ts

<div class="field"
      [class.error]="!myForm.find('sku').valid && myForm.find('sku').touched">

这比前面的代码还多,不可取.

通过NgFormControl导出

这里有另外一种方式获得引用,**通过ngForm导出ngFormControl.这是前面章节没有讲过的内容.

Component可以导出他们自己的引用,让你可以在视图中引用它们

我们在接下来的章节中讲解怎么使用exportAs导出,但是现在只需要知道,很多内建组件都可以像这样做.

在这个例子中,NgFormControl导出它自己作为ngForm.你可以使用#reference来引用他们.

如下:

code/forms/app/ts/forms/demo_form_with_validations_shorthand.ts

<input
type="text"
id="skuInput"
placeholder="SKU"
#sku="ngForm"
[ngFormControl]="myForm.controls['sku']">

注意,上面的就是使用ngFormControl导出它自己引入变量sku,但是要注意,sku是一个指令,不是Control.为了访问sku Control,需要使用sku.control.

现在,sku对于我们来说是可以利用的.我们可以像下面这样使用它.

code/forms/app/ts/forms/demo_form_with_validations_shorthand.ts

<div *ngIf="!sku.control.valid"
class="ui error message">SKU is invalid</div>
<div *ngIf="sku.control.hasError('required')" class="ui error message">SKU is required</div>

sku变量的范围

当我们创建一个引用的时候,它是可以在其兄弟节点或者子节点使用,但是不能在其父节点使用.

比如,不能像下面这样:

// this won't work
   <div class="field"
[class.error]="!sku.control.valid && sku.control.touched">

因为div是form的父元素.

全部代码

code/forms/app/ts/forms/demo_form_with_validations_shorthand.ts

import  Component  from '@angular/core';
import 
  CORE_DIRECTIVES,
  FORM_DIRECTIVES,
  FormBuilder,
  ControlGroup,
  Validators
 from '@angular/common';

@Component(
  selector: 'demo-form-with-validations-shorthand',
  directives: [CORE_DIRECTIVES, FORM_DIRECTIVES],
  template: `
<div class="ui raised segment">
  <h2 class="ui header">Demo Form: with validations (shorthand)</h2>
  <form [ngFormModel]="myForm"
        (ngSubmit)="onSubmit(myForm.value)"
        class="ui form">

    <div class="field"
      [class.error]="!myForm.find('sku').valid && myForm.find('sku').touched">
      <label for="skuInput">SKU</label>
      <input type="text"
             id="skuInput"
             placeholder="SKU"
             #sku="ngForm"
             [ngFormControl]="myForm.controls['sku']">
       <div *ngIf="!sku.control.valid"
         class="ui error message">SKU is invalid</div>
       <div *ngIf="sku.control.hasError('required')"
         class="ui error message">SKU is required</div>
    </div>

    <div *ngIf="!myForm.valid"
      class="ui error message">Form is invalid</div>

    <button type="submit" class="ui button">Submit</button>
  </form>
</div>
  `
)
export class DemoFormWithValidationsShorthand 
  myForm: ControlGroup;

  constructor(fb: FormBuilder) 
    this.myForm = fb.group(
      'sku':  ['', Validators.required]
    );
  

  onSubmit(value: any): void 
    console.log('you submitted value:', value.sku);
  

自定义校验器

我们通常需要编写自己的校验器,怎么做?

为了弄清楚怎么实现自定义校验器,让我们看看内建的Validators.required.

export class Validators 
  static required(c: Control): StringMap<string, boolean> 
    return isBlank(c.value) || c.value == "" ? "required": true : null;

输入为一个Control,输出为一个

编写一个校验器

比如我们希望标志我们的sku,输入的必须是以123开头的.

我可以像下面这样写:
code/forms/app/ts/forms/demo_form_with_custom_validations.ts

function skuValidator(control: Control):  [s: string]: boolean   if (!control.value.match(/^123/)) 
    return invalidSku: true;
  

当输入的不是以123开头时,这个校验器会返回invalidSku的错误代码.

分配校验器给Control

现在我们需要将skuValidator添加到Control上面去.但是这里有一个问题,我们的sku上面已经有一个校验器了,我们怎么添加多个校验器到同一个Control呢?

答案是我们使用Validators.compose.如下:
code/forms/app/ts/forms/demo_form_with_custom_validations.ts

this.myForm = fb.group(
      'sku':  ['', Validators.compose([
        Validators.required, skuValidator])]
    );

Validators.compose包装多个校验器给Control,当所有的校验器都是有效的时候,Control才是有效的.否则校验不通过.

现在我们可以在视图中使用这个新的校验器.

code/forms/app/ts/forms/demo_form_with_custom_validations.ts

<div *ngIf="sku.hasError('invalidSku')"
class="ui error message">SKU must begin with <tt>123</tt></div>

监听变化

到现在为止,当表单提交的时候,我们只是从我们的表单中读取数据.但是通常,我们希望监听每一个Control的变化.

ControlGroup与Control都有一个EventEmitter,它可以用来监听数据的变化.

为了监听变化,我们需要做下面的两个操作:

  • 通过调用control.valueChanges获取
  • 使用EventEmitter.observer方法添加一个观察者

下面是一个例子:
code/forms/app/ts/forms/demo_form_with_events.ts

export class DemoFormWithEvents 
  myForm: ControlGroup;
  sku: AbstractControl;

  constructor(fb: FormBuilder) 
    this.myForm = fb.group(
      'sku':  ['', Validators.required]
    );

    this.sku = this.myForm.controls['sku'];

    this.sku.valueChanges.subscribe(
      (value: string) => 
        console.log('sku changed to:', value);
      
    );

    this.myForm.valueChanges.subscribe(
      (form: any) => 
        console.log('form changed to:', form);
      
    );

  

  onSubmit(form: any): void 
    console.log('you submitted value:', form.sku);
  

这里我们监听两个事件:sku变化事件和整个form的变化事件.

我们传递进去的是一个简单的key.接下来是一个函数,当值变哈的时候会被调用.

ngModel

ngModel是一个特殊的指令,它绑定一个Model给form.ngModel是一个实现了双向数据绑定的特殊指令.双向数据绑定是比较复杂的.angular2默认使用的是单项数据流,但是在Form中,由于需要跟踪用户改变,所以使用双向数据绑定.

看下面的例子:
code/forms/app/ts/forms/demo_form_ng_model.ts

export class DemoFormNgModel  
    myForm: ControlGroup; productName: string;
    constructor(fb: FormBuilder) 
        this.myForm = fb.group(
        'productName': ['', Validators.required] );
    
    onSubmit(value: string): void  
        console.log('you submitted value: ', value);
    

注意,我们在勒种定义了productName: string 实例变量.然后让我们看看在input标签中怎么使用ngModel:
code/forms/app/ts/forms/demo_form_ng_model.ts

<input
    type="text"
    id="productNameInput"
    placeholder="Product Name"
    [ngFormControl]="myForm.find('productName')"
    [(ngModel)]="productName">   

ngModel的语法是很奇特的,它使用[]和()合起来,由前面的可知,[]是输入,()是输出,也就是说双向就是输入和输出都绑定了.
然后,我们将productName显示出来.

code/forms/app/ts/forms/demo_form_ng_model.ts

<div class="ui info message">
The product name is: productName
</div>

看起来像下面这样:

可以看到,是实时同步的.

总结

Form有很多的复杂功能,但是angular2提供了一个清晰明了的方式,一旦你学会使用了ControlGroups, Controls, 和 Validations,使用表单就会变得很简单了.

以上是关于表单的主要内容,如果未能解决你的问题,请参考以下文章

label的for属性

全局变量的隐士声明

label的for属性(转载)

label 的for属性总结

九岭隐士传奇印书版

Scala笔记整理:类型参数(泛型)与隐士转换