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,因此显示的值将随着用户输入而更新。
这使得修改或验证用户输入变得简单。例如,如果我们要强制要求所有名称都用大写字母书写,我们可以将 handlechange 改写为:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
在 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 初始化于构造函数中,因此文本区域默认有初值。
在 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']}>
在 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 中的新属性。
属性 | 值 | 描述 |
---|---|---|
accept | MIME_type | HTML5 不支持。规定服务器接收到的文件的类型。(文件是通过文件上传提交的) |
accept-charset | character_set | 规定服务器可处理的表单数据字符集。 |
action | URL | 规定当提交表单时向何处发送表单数据。 |
autocompleteNew | onoff | 规定是否启用表单的自动完成功能。 |
enctype | application/x-www-form-urlencodedmultipart/form-datatext/plain | 规定在向服务器发送表单数据之前如何对其进行编码。(适用于 method="post" 的情况) |
method | getpost | 规定用于发送表单数据的 HTTP 方法。 |
name | text | 规定表单的名称。 |
novalidateNew | novalidate | 如果使用该属性,则提交表单时不进行验证。 |
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`.
*请认真填写需求信息,我们会在24小时内与您取得联系。