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小時內與您取得聯系。