整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

React:表单元素处理

React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。例如这个纯 HTML 表单只接受一个名称:

<form>
  <label>
    名字:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="提交" />
</form>

此表单具有默认的 HTML 表单行为,即在用户提交表单后浏览到新页面。如果你在 React 中执行相同的代码,它依然有效。但大多数情况下,使用 JavaScript 函数可以很方便的处理表单的提交, 同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用“受控组件”。
受控组件

在 HTML 中,表单元素(如<input>、 <textarea>和 <select>)之类的表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。

我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

例如,如果我们想让前一个示例在提交时打印出名称,我们可以将表单写为受控组件:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('提交的名字: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。由于 handlechange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。

对于受控组件来说,每个 state 突变都有一个相关的处理函数。

这使得修改或验证用户输入变得简单。例如,如果我们要强制要求所有名称都用大写字母书写,我们可以将 handlechange 改写为:

handleChange(event) {
  this.setState({value: event.target.value.toUpperCase()});
}

textarea 标签

在 HTML 中, <textarea> 元素通过其子元素定义其文本:

<textarea>
  你好, 这是在 text area 里的文本
</textarea>

而在 React 中,<textarea> 使用 value 属性代替。这样,可以使得使用 <textarea> 的表单和使用单行 input 的表单非常类似:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('提交的文章: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          文章:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

请注意,this.state.value 初始化于构造函数中,因此文本区域默认有初值。

select 标签

在 HTML 中,<select> 创建下拉列表标签。例如,如下 HTML 创建了水果相关的下拉列表:

<select>
  <option value="grapefruit">葡萄柚</option>
  <option value="lime">酸橙</option>
  <option selected value="coconut">椰子</option>
  <option value="mango">芒果</option>
</select>

请注意,由于 selected 属性的缘故,椰子选项默认被选中。React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。例如:

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('你喜欢的风味是: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          选择你喜欢的风味:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">葡萄柚</option>
            <option value="lime">酸橙</option>
            <option value="coconut">椰子</option>
            <option value="mango">芒果</option>
          </select>
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

总的来说,这使得 <input type="text">, <textarea> 和 <select> 之类的标签都非常相似—它们都接受一个 value 属性,你可以使用它来实现受控组件。

注意
你可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项:
<select multiple={true} value={['B', 'C']}>

文件 input 标签

在 HTML 中,<input type=“file”> 允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API 进行控制。

<input type="file" />

因为它的 value 只读,所以它是 React 中的一个非受控组件。将与其他非受控组件在后续文档中一起讨论。

处理多个输入

当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。

例如:

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          参与:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          来宾人数:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

这里使用了 ES6 计算属性名称的语法更新给定输入名称对应的 state 值:

例如:

this.setState({
  [name]: value
});

等同 ES5:

var partialState = {};
partialState[name] = value;
this.setState(partialState);

另外,由于 setState() 自动将部分 state 合并到当前 state, 只需调用它更改部分 state 即可。
受控输入空值

在受控组件上指定 value 的 prop 可以防止用户更改输入。如果指定了 value,但输入仍可编辑,则可能是意外地将value 设置为 undefined 或 null。

下面的代码演示了这一点。(输入最初被锁定,但在短时间延迟后变为可编辑。)

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

受控组件的替代品

有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件, 这是实现输入表单的另一种方式。
成熟的解决方案

如果你想寻找包含验证、追踪访问字段以及处理表单提交的完整解决方案,使用 Formik 是不错的选择。然而,它也是建立在受控组件和管理 state 的基础之上 —— 所以不要忽视学习它们。

带有两个输入字段和一个提交按钮的 HTML 表单:

<form action="demo_form.php" method="get">

First name: <input type="text" name="fname"><br>

Last name: <input type="text" name="lname"><br>

<input type="submit" value="提交">

</form>

(更多实例见页面底部)


浏览器支持

所有主流浏览器都支持 <form> 标签。


标签定义及使用说明

<form> 标签用于创建供用户输入的 HTML 表单。

<form> 元素包含一个或多个如下的表单元素:

  • <input>

  • <textarea>

  • <button>

  • <select>

  • <option>

  • <optgroup>

  • <fieldset>

  • <label>


HTML 4.01 与 HTML5之间的差异

HTML5 新增了两个新的属性:autocomplete 和 novalidate,同时不再支持 HTML 4.01 中的某些属性。


HTML 与 XHTML 之间的差异

在 XHTML 中,name 属性已被废弃。使用全局 id 属性代替。


属性

New :HTML5 中的新属性。

属性描述
acceptMIME_typeHTML5 不支持。规定服务器接收到的文件的类型。(文件是通过文件上传提交的)
accept-charsetcharacter_set规定服务器可处理的表单数据字符集。
actionURL规定当提交表单时向何处发送表单数据。
autocompleteNewonoff规定是否启用表单的自动完成功能。
enctypeapplication/x-www-form-urlencodedmultipart/form-datatext/plain规定在向服务器发送表单数据之前如何对其进行编码。(适用于 method="post" 的情况)
methodgetpost规定用于发送表单数据的 HTTP 方法。
nametext规定表单的名称。
novalidateNewnovalidate如果使用该属性,则提交表单时不进行验证。
target_blank_self_parent_top规定在何处打开 action URL。

全局属性

<form> 标签支持 HTML 的全局属性。


事件属性

<form> 标签支持 HTML 的事件属性。

实例

带有复选框的表单

此表单包含两个复选框和一个提交按钮。

带有单选按钮的表单

此表单包含两个单选框和一个提交按钮。

如您还有不明白的可以在下面与我留言或是与我探讨QQ群308855039,我们一起飞!

## 什么是表单:

django中的表单不是html中的那个表单.**这个表单是用来验证数据的合法性的一个东西**,也可以生成HTML代码.

### 使用表单:

1. 创建一个`forms.py`的文件,放在指定的app当中,然后在里面写表单.

2. 表单是通过类实现的,继承自`forms.Form`,然后在里面定义要验证的字段.

3. 在表单中,创建字段跟模型是一模一样的,但是没有`null=True`或者`blank=True`等这几种参数了,有的参数是`required=True/False`.

4. 使用`is_valid()`方法可以验证用户提交的数据是否合法,而且HTML表单元素的`name`必须和`django`中的表单的`name`保持一致,否则匹配不到.

5. `is_bound`属性:用来表示`form`是否绑定了数据,如果绑定了,则返回`True`,否则返回`False`.

6. `cleaned_data`:这个是在`is_valid()`返回`True`的时候,保存用户提交上来的数据.

```

username = form.cleaned_data.get('username',None)

password = form.cleaned_data.get('password',None)

password_repeat = form.cleaned_data.get('password_repeat',None)

email = form.cleaned_data.get('email',None)

```

7. 表单生成HTML元素:

```

**views.py**

if request.method == 'GET':

return render(request,'regist_form.html',{'form':RegistForm()})

```

```

**regist.html**

<form action="" method='POST'>

{%csrf_token%}

{{form}}

<input type="submit" vlaue='注册'>

</form>

```

* 使用django的Form类生成的表单,不包含form和submit按钮两个标签,需要手动添加。

* 这个模块用得比较少,这个功能确实很鸡肋,把前端该做的事情放到后台来实现,增加了代码的耦合性也增加了服务器的压力。在真正开发中,是讲究前后端代码分离的。

### 上传文件:

1. 在相应的模型里面定义`FileField`或者是`ImageField`类型的字段,并且设置好`upload_to`参数来指定上传的路径.

2. 需要在`settings.py`文件中指定媒体路径`MEDIA_ROOT`.

3. 数据库保存的是文件的路径,不会保存文件本身.

4. 文件上传需要在HTML代码中的form表单中添加`enctype="multipart/form-data"`以及在views当中,使用`request.FILES`来接收文件.

### 表单错误消息:

1. 表单验证没有通过后,表单会产生一个`errors`属性,这个属性包括所有的验证错误信息。

2. 通过`form.errors`即可访问。

3. 通过`form.errors.as_json()`可以将错误消息转换成json数据。

4. 自定义错误消息:在`Field`中添加一个`error_messages`的`dict`类型的参数,然后根据`code`值设置对应的`message`,例如以下代码:

```

password = forms.CharField(max_length=10,error_messages={'required':u'密码不能少'})

```

其中`code`为`required`.

### 表单自定义错误消息:

1. 在表单中,重写方法`clean_field`,可以自定义针对某一个`field`的验证机制,如果出现错误,抛出一个`ValidationError`异常就可以了。

```

def clean_password(self):

password = self.cleaned_data.get('password',None)

if len(password) < 6:

raise forms.ValidationError(u'password at least 6 length',code='min_length')

```

2. 重写`clean`方法可以在完成`django`默认的验证后,再重新执行`clean`方法的验证,如果某个`field`出现验证错误,通过`add_error`方法给指定的field添加错误消息。如果想抛出一个不属于任何field的错误,直接`raise ValidationError(message)`就可以了。然后通过`__all__`进行访问。也可以通过`form.non_field_errors()`进行访问。

3. `clean_fieldname`在判断没有问题以后,需要返回这个值,比如以下代码,如果不返回`password`,那么后面就不能获取到`password`这个值了:

```

def clean_password(self):

password = self.cleaned_data.get('password',None)

if len(password) < 6:

raise forms.ValidationError(u'password at least 6 length',code='min_length')

return password

```

`clean`方法可以不用返回`cleaned_data`,但是为了代码健全和可读性,应该返回`cleaned_data`.