# Forms
Nette \ Forms極大地方便了創建和處理Web表單。 它可以做什么?
1、驗證客戶端(JavaScript)和服務器端發送的數據
2、提供高水平的安全性
3、多個渲染模式
Nette Framework非常努力地做到安全性,既然表單是最常見的用戶輸入,Nette表單就像不可穿透一樣好。 所有都是動態和透明地維護,不需要手動設置。 眾所周知的漏洞(例如跨站腳本(XSS)和跨站點請求偽造(CSRF))被過濾,以及特殊的控制字符。 檢查所有輸入的UTF-8有效性。 在驗證時,將檢查每個多項選擇,選擇框和類似項的偽造值。 聽起來不錯? 讓我們試試看。
使用Nette / Forms,您可以減少常規任務,例如在服務器端和客戶端雙重驗證。 您還可以避免常見的錯誤和安全問題。
# 開始表單
讓我們為我們的應用創建一個簡單的注冊表單。 表單使用組件添加到控制器:
~~~
use Nette\Application\UI;
class HomepagePresenter extends UI\Presenter
{
// ...
protected function createComponentRegistrationForm()
{
$form = new UI\Form;
$form->addText('name', 'Name:');
$form->addPassword('password', 'Password:');
$form->addSubmit('login', 'Sign up');
$form->onSuccess[] = [$this, 'registrationFormSucceeded'];
return $form;
}
// called after form is successfully submitted
public function registrationFormSucceeded(UI\Form $form, $values)
{
// ...
$this->flashMessage('You have successfully signed up.');
$this->redirect('Homepage:');
}
}
~~~

注意一定要引用use Nette\Application\UI;
這樣一個表單組件就完成了。下面應是在模板中渲染使用控制標注了。
~~~
{control registrationForm}
~~~
我們先把app\presenters\templates\Homepage\default.latte
里清空,再把上面代碼放進去,刷新主頁就可以看到以下圖(記得,現在操作只是實例,如果開發的話自已去做自已控制器和視圖)

我們創建了一個簡單的表單,在提交并成功驗證后調用registrationFormSucceeded()。 表單本身作為第一個參數傳遞。 提交的值作為Nette \ Utils \ ArrayHash對象中包含的第二個參數傳遞。 如果你喜歡一個簡單的數組,你可以鍵入提示第二個參數數組$ values。 您還可以使用$ values = $ form-> getValues()檢索提交的值。
現在我們發現提交時沒有任何的反映,原來是我們沒有引用他特有的JS,所以我們再把代碼改成這樣。
~~~
{block content}
<div id="content">
{control registrationForm}
</div>
{/block}
{block scripts}
{include parent}
<script src="https://files.nette.org/sandbox/jush.js"></script>
{/block}
~~~
現在我們試一下提交,就會發現出現
~~~
You have successfully signed up.
~~~
說明框架中的JS一定要引用他的JS。
這個表單呈現的形式遵循基本的Web輔助功能。 所有標簽都生成為<label>元素并與其輸入相關聯。
$values中存儲的數據不包含表單按鈕的值,因此它們可以進行更多操作(例如插入數據庫)。 值得注意的是,在輸入的左側和右側的空格被刪除。
雖然我們提到驗證,我們的表單沒有。 讓我們修復它。 為了要求用戶輸入名字,在表單項上調用setRequired()方法。 您可以將錯誤消息作為可選參數傳遞,如果用戶未填寫他的姓名,則會顯示錯誤消息:
~~~
$form->addText('name', 'Name:')
->setRequired('Please fill your name.');
~~~

我們試一下沒有輸入任何數據在name.它就會自動驗證并提示信息(這個功能我超喜歡,不會讓你再去學JS驗證)。

~~~
如果你不使用nette / sandbox,你需要鏈接netteForms.js以啟用JavaScript驗證。 您可以在src / assets文件夾中找到該文件。
<script src="netteForms.js"></script>
~~~
Nette Framework將所需的類添加到所有的強制性元素。 添加以下樣式到模板中將名稱輸入的標簽變為紅色。
~~~
<style>
.required label { color: maroon }
</style>
~~~

即使默認驗證規則可能很方便,但是我們將添加另一個驗證規則,這次使用addRule()方法創建自已規則。
例如表單將獲得另一個條件輸入年齡,它是可選的,它必須是一個數字(Form :: INTEGER)和在某些邊界(Form :: RANGE)。 這次我們將使用addRule()創建。
我們在HomepagePresenter 增加以下代碼。
~~~
$form->addText('age', 'Age:')
->setRequired(FALSE)
->addRule(Form::INTEGER, 'Your age must be an integer.')
->addRule(Form::RANGE, 'You must be older 18 years and be under 120.', [18, 120]);
~~~

注意自已創建一定要加上提示這句代碼

如果不加就會出錯,為了這個錯誤本人足足找了好幾天才明白。因為官網沒有提示。
現在再刷新一次。

好,可以試一試輸入錯誤數值,它就會象上面彈出提示。超喜歡。
Nette Framework支持HTML5,包括新的表單元素。 感謝,我們可以設置年齡輸入為數字:
~~~
$form->addText('age', 'Age:')
->setType('number')
~~~
這樣除了數字外其他都輸不進去了。安全性又提高。
在最先進的瀏覽器(即Google Chrome,Safari和Opera)中,輸入旁邊會顯示微小箭頭。 iPhone的Safari顯示帶數字的優化鍵盤。
讓我們回到密碼字段,這應該是必需的。 此外,我們可以限制最小密碼長度(Form :: MIN_LENGTH),同樣以下代碼修改:
~~~
$form->addPassword('password', 'Password:')
->setRequired('Pick a password')
->addRule(Form::MIN_LENGTH, 'Your password has to be at least %d long', 3);
~~~
我們將再添加一個輸入passwordVerify,其中用戶將被提示再次輸入他的密碼,以檢查拼寫錯誤。 使用驗證規則,我們將檢查兩個字段是否包含相同的值(Form :: EQUAL)。 注意動態第三個參數,其實是密碼控制本身:
~~~
$form->addPassword('passwordVerify', 'Password again:')
->setRequired('Fill your password again to check for typo')
->addRule(Form::EQUAL, 'Password missmatch', $form['password']);
~~~

如果表單不用于注冊,而是用于編輯記錄,那么可以設置默認值。
這是一個完整的,完全工作的注冊表,具有客戶端和服務器端驗證。 自動處理魔術引號,檢查無效的UTF-8字符串等。一切都準備好了,沒有在我們這邊的努力 - Nette已經照顧它。
# 表單輸入
**產生標準表單文本類輸入方法**
**addText($name, $label = NULL)**
添加這些文本字段(類文本輸入)。 Nette自動會把輸入左側和右側的空格去除。 除了預設的驗證規則,以下也是可用的:
| Form::MIN_LENGTH | 最小字符串長度 |
| --- | --- |
| Form::MAX_LENGTH | 最大字符串長度 |
| Form::LENGTH | 精確長度 |
| Form::EMAIL | 查看是否有效的電子郵件地址 |
| Form::URL | 查看是否有效URL地址 |
| Form::PATTERN | 測試正則表達式的整個值,有點像是在^和a $里面 |
| Form::INTEGER | 查看是否整數 |
| Form::NUMERIC | Form :: INTEGER的別名 |
| Form::FLOAT | 查看值是否浮點數 |
| Form::MIN | 最小的整數值 |
| Form::MAX | 最大的整數值 |
| Form::RANGE |數值是否在范圍內 |
使用方法
~~~
$form->addText('zip', 'Postcode:')
->setRequired()
->addRule(Form::PATTERN, 'Postcode must have exactly 5 numerals', '([0-9]\s*){5}');
~~~
我們試一下

我們輸入錯誤數值就會彈出以下圖錯誤信息。

**addPassword($name, $label = NULL)**
添加密碼字段(TextInput類)。 也會自動修剪左側和右側的空格。 支持與addText相同的驗證規則集。
~~~
$form->addPassword('password', 'Password:')
->setRequired()
->addRule(Form::MIN_LENGTH, 'Password has to be at least %d characters long', 3)
->addRule(Form::PATTERN, 'Password must contain a number', '.*[0-9].*');
~~~
**addTextArea($name, $label = NULL)**
添加多行文本字段(TextArea類)。支持與addText相同的驗證規則集。 與在線輸入不同,它不修剪任一邊緣上的輸入空白。
~~~
$form->addTextArea('note', 'Note:')
->setRequired(FALSE) // optional
->addRule(Form::MAX_LENGTH, 'Your note is way too long', 10000);
~~~
**addUpload($name, $label = NULL)**
添加文件上傳字段(類UploadControl)。 除了預設的驗證規則,以下是可用的:
| Form::MAX_FILE_SIZE | 驗證最大文件大小 |
| --- | --- |
| Form::MIME_TYPE | 檢查MIME類型是否有效 |
| Form::IMAGE | 檢查上傳的文件是JPEG,PNG還是GIF |
~~~
$form->addUpload('thumbnail', 'Thumbnail:')
->setRequired(FALSE) // optional
->addRule(Form::IMAGE, 'Thubnail must be JPEG, PNG or GIF')
->addRule(Form::MAX_FILE_SIZE, 'Maximum file size is 64 kB.', 64 * 1024 /* v bytech */);
~~~
**addMultiUpload($name, $label = NULL)**
添加多個文件上傳字段。 驗證規則與addUpload()相同,并添加以下內容:
| | |
| --- | --- |
| Form::MIN_LENGTH | 最少文件數值 |
| Form::MAX_LENGTH | 最大文件數值 |
| Form::LENGTH | 上傳文件的精確數值 |
~~~
$form->addMultiUpload('files', 'Files');
~~~
**addHidden($name, $default = NULL)**
添加隱藏字段(類HiddenField)。
~~~
$form->addHidden('user_id');
~~~
**addCheckbox($name, $caption = NULL)**
添加一個復選框(類復選框)。 返回值為布爾TRUE或FALSE,選中或未選中。
~~~
$form->addCheckbox('agree', 'I agree with terms')
->setRequired('You must agree with our terms');
~~~
**addRadioList($name, $label = NULL, array $items = NULL)**
添加單選按鈕(RadioList類)。 提供值的數組作為第三個參數傳遞。
~~~
$sex = [
'm' => 'male',
'f' => 'female',
];
$form->addRadioList('gender', 'Gender:', $sex);
~~~
**addCheckboxList($name, $label = NULL, array $items = NULL)**
添加用于選擇多個元素的復選框列表。 值的數組作為第三個參數傳遞。 組件檢查提交的值是否來自給定范圍。
~~~
$form = new Form;
$form->addCheckboxList('colors', 'Colors:', [
'r' => 'red',
'g' => 'green',
'b' => 'blue',
]);
~~~
**addSelect($name, $label = NULL, array $items = NULL)**
添加選擇框(SelectBox類)。 提供值的數組作為第三個參數傳遞。 也許是二維的。 第一個項目通常用作號召性用語消息,但實際選擇時沒有價值 - 這就是setPromt()方法。

~~~
$countries = [
'Europe' => [
'CZ' => 'Czech republic',
'SK' => 'Slovakia',
'GB' => 'United Kingdom',
],
'CA' => 'Canada',
'US' => 'USA',
'?' => 'other',
];
$form->addSelect('country', 'Country:', $countries)
->setPrompt('Pick a country');
~~~
**addMultiSelect($name, $label = NULL, array $items = NULL)**
添加多個選擇框
~~~
$form->addMultiSelect('options', 'Pick many:', $options);
~~~
**addSubmit($name, $caption = NULL)**
添加提交按鈕
~~~
$form->addSubmit('submit', 'Register');
~~~
如果您不想在按下提交按鈕時驗證表單(例如“取消”或“預覽”按鈕),可以使用setValidationScope([])將其關閉。
可以添加多個提交按鈕。 要找出他們中的哪一個被點擊,請使用
~~~
if ($form['submit']->isSubmittedBy()) {
// ...
}
~~~
或者
~~~
if ($form->isSubmitted() === $form['submit']) {
// ...
}
~~~
**addButton($name, $caption)**
添加按鈕(類Button),無提交功能。 它對于將其他功能綁定到id很有用,例如JavaScript動作。
~~~
$form->addButton('raise', 'Raise salary')
->setAttribute('onclick', 'raiseSalary()');
~~~
**addImage($name, $alt = NULL)**
以圖片的形式添加提交按鈕
~~~
$form->addImage('submit', '/path/to/image');
~~~
**addContainer($name)**
添加一個子表單(類Container)或一個容器,可以像表單一樣處理。 這意味著你可以使用setDefaults()或getValues()方法。
~~~
$sub1 = $form->addContainer('first');
$sub1->addText('name', 'Your name:');
$sub1->addEmail('email', 'Email:');
$sub2 = $form->addContainer('second');
$sub2->addText('name', 'Your name:');
$sub2->addEmail('email', 'Email:');
~~~
# Low-level forms
要向表單添加項目,您不必調用$ form-> addXyz()。 從版本2.1開始,表單項可以僅在模板中引入。 例如,如果您需要生成動態項目。
~~~
{foreach $items as $item}
<p><input type=checkbox name="sel[]" value={$item->id}> {$item->name}</p>
{/foreach}
~~~
提交后,您可以檢索值:
~~~
$values = $form->getHttpData($form::DATA_TEXT, 'sel[]');
$values = $form->getHttpData($form::DATA_TEXT | $form::DATA_KEYS, 'sel[]');
~~~
在第一個參數中,指定元素類型(類型=文件為DATA_FILE,文本,密碼或電子郵件等單行輸入為DATA_LINE,其余為DATA_TEXT)。 第二個參數與HTML屬性名稱匹配。 如果需要保留密鑰,可以將第一個參數與DATA_KEYS組合。 這對select,radioList或checkboxList很有用。
getHttpData()返回已清理的輸入。 在這種情況下,它將始終是有效的UTF-8字符串數組,無論什么是由表單發送。 如果你想接收安全的數據,這是一個替代使用$ _POST或$ _GET直接。
# 驗證規則
大多數已提及的輸入形式都支持以下規則:
| | |
| --- | --- |
| Form::FILLED | 是元素填充? |
| Form::REQUIRED | Form :: FILLED的別名 |
|Form::EQUAL |值是等于? |
| Form::NOT_EQUAL | 該值不能等于給定值 |
| Form::IS_IN | 檢查值是否在數組中 |
| Form::IS_NOT_IN | 檢查值是否不在數組中 |
| Form::VALID | 輸入驗證? |
| Form::BLANK | 該項目不能填充 |
我們可以為所有驗證規則設置自定義錯誤消息,或者使用默認錯誤消息。 多語言表單的郵件會自動翻譯。
在錯誤消息文本中可以使用以下特殊替換字符串:
| | |
| --- | --- |
| %label | 替換為標簽文本 |
| %name | 替換為輸入標識 |
| %value | 替換為提交的輸入值 |
除了驗證規則,可以設置條件。 它們的設置非常像規則,但我們使用addRule()而不是addCondition(),當然我們保留它沒有錯誤消息(條件只是詢問):
~~~
$form->addPassword('password', 'Password:')
// if password is not longer than 5 characters ...
->addCondition(Form::MAX_LENGTH, 5)
// ... then it must contain a number
->addRule(Form::PATTERN, 'Must contain number', '.*[0-9].*');
~~~
條件可以鏈接到另一個元素,然后它的使用。 用addConditionOn()替換addCondition()并將其他輸入設置為第一個參數。 在以下情況下,只有在復選框被選中(即它的布爾值為TRUE)時才需要電子郵件:
~~~
$form->addCheckbox('newsletters', 'send me newsletters');
$form->addEmail('email', 'Email:')
// if checkbox is checked ...
->addConditionOn($form['newsletters'], Form::EQUAL, TRUE)
// ... require email
->setRequired('Fill your email address');
~~~
所有條件都可以用?(波浪號)取反,即。 addCondition(?Form :: NUMBER,...)如果字段未填充,則通過驗證。 條件可以使用elseCondition()和endCondition()方法分組為復雜結構。
正如你所看到的,驗證規則和條件的語言是強大的。 即使所有的結構在服務器端和客戶端,在JavaScript中工作。
我們可以添加自己的驗證器。 方法addRule()和addCondition()也接受回調函數或lambda函數:
~~~
// user validation: checks if $item is divisible by $arg
function divisibilityValidator($item, $arg)
{
return $item->value % $arg === 0;
}
$form->addInteger('number', 'Number:')
->setRequired(FALSE)
->addRule('divisibilityValidator', 'Number must be divisible by %d.', 8);
~~~
# 自定義錯誤
我們經常發現用戶的輸入是錯誤的,即使每個表單的都有驗證方法。 例如,插入新數據庫行時,輸入錯誤或無效的登錄憑據。 在這種情況下,我們可以使用addError()方法手動添加錯誤。 它可以在指定輸入或表單本身上調用:
~~~
try {
$values = $form->getValues();
$this->user->login($values->username, $values->password);
$this->redirect('Homepage:');
} catch (Nette\Security\AuthenticationException $e) {
$form->addError($e->getMessage());
}
~~~
建議將錯誤直接鏈接到表單元素,因為當使用默認渲染器時,錯誤將顯示在它旁邊。
~~~
$form['date']->addError("We apologize but this date has been already taken.");
~~~
# 全局默認消息
要全局更改驗證錯誤消息,請修改靜態數組Nette \ Forms \ Rules :: $ defaultMessages。
~~~
Nette\Forms\Rules::$defaultMessages[Form::FILLED] = "All fields are obligatory.";
~~~
# 自定義驗證功能
如果需要自定義驗證功能,可以為整個表單定義自己的驗證函數,并將它們綁定到onValidate事件。 通常,這用于驗證值的組合:
~~~
protected function createComponentSignInForm()
{
$form = new Form();
...
$form->onValidate[] = [$this, 'validateSignInForm'];
return $form;
}
public function validateSignInForm($form)
{
$values = $form->getValues();
if (...) { // validation logic
$form->addError('Password does not match.');
}
}
~~~
您可以將多個函數綁定到事件。 如果不使用$ form-> addError()添加任何錯誤,該函數被認為是成功的。
# JavaScript
驗證規則通過HTML5屬性data-nette-rules傳遞到JavaScript部分,其中包含所有規則和條件的JSON。 驗證本身由另一個腳本處理,它關聯所有提交事件,迭代所有輸入并運行相應的驗證。 默認實現是netteForms.js文件,可以在src / assets找到。 你需要做的是鏈接它。
~~~
<script src="netteForms.js"></script>`
~~~
可以通過擴展Nette.validators對象來添加自定義驗證規則:
~~~
<script>
Nette.validators.divisibilityValidator = function(elem, args, val) {
return val % args === 0;
};
</script>
~~~
如果我們的PHP驗證回調是一個類中的靜態方法,我們通過刪除所有反斜杠\并用單個下劃線_替換雙冒號來創建JavaScript的名稱。 App \ MyValidator :: divisibilityValidator被轉換為AppMyValidator_divisibilityValidator。
# 禁用驗證
在某些情況下,您需要禁用驗證。 如果提交按鈕在提交后不應運行驗證(例如取消或預覽按鈕),您可以通過調用$ submit-> setValidationScope([])來禁用驗證。 您還可以通過指定要驗證的項目來部分驗證表單。
~~~
$form->addText('name')->setRequired();
$details = $form->addContainer('details');
$details->addInteger('age')->setRequired('age');
$details->addInteger('age2')->setRequired('age2');
$form->addSubmit('send1'); // Validates the whole form
$form->addSubmit('send2')->setValidationScope(FALSE); // Validates nothing
$form->addSubmit('send3')->setValidationScope([$form['name']]); // Validates only 'name' input
$form->addSubmit('send4')->setValidationScope([$form['details']['age']]); // Validates only 'age' input
$form->addSubmit('send5')->setValidationScope([$form['details']]); // Validates 'details' container
~~~
表單上的onValidate事件總是被調用,不受setValidationScope的影響。 只有在指定此容器進行部分驗證時,才會調用容器上的onValidate事件。
# 處理表單
通過方法getValues()訪問提交的值,如果我們傳遞TRUE作為第一個參數,它返回一個ArrayHash類的實例或一個簡單的數組。
~~~
$values = $form->getValues(); // Nette\Utils\ArrayHash
$values = $form->getValues(TRUE); // array
~~~
# 禁用輸入
為了禁用輸入,可以調用$ control-> setDisabled(TRUE)。
~~~
$form->addEmail('email', 'E-mail:')->setDisabled(TRUE);
~~~
輸入不能被寫入,它的值不會被getValues()返回。如果我們需要只讀輸入(輸入將被呈現為禁用,但其值將顯示),我們先禁用輸入,然后設置值。 原因是setDisabled()方法重置輸入的值。
~~~
$form->addText('readonly', 'Readonly:')->setDisabled()->setValue('readonly value');
~~~
如果只需要刪除輸入的值而不是禁用它,則可以使用setOmitted(TRUE)。 此選項對于例如拒絕反垃圾郵件輸入很有用。
~~~
$form->addText('antispam', 'Antispam:')->setOmitted(TRUE);
~~~
# 修改輸入值
使用addFilter方法,我們可以在處理表單之前修改檢索的值。 我們可以結合addFilter和addCondition和addConditionOn方法。
~~~
$form->addText('zip', 'Postcode:')
->addCondition($form::FILLED)
->addFilter(function ($value) {
return str_replace(' ', '', $value);
});
~~~
當我們稍后在表單處理中訪問值時,郵編不會包含任何空格。
# 其他表單擴展
你還可以擴展帶有標簽,類和其他事情的窗體。注意擴展的條件,規則和用法的順序。
設置類或JavaScript屬性:
~~~
$form->addInteger('number', 'Number:')
->setAttribute('class', 'bigNumbers');
$form->addSelect('rank', 'Order by:', ['price', 'name'])
->setAttribute('onchange', 'submit()'); // calls JS function submit() on change
// applying on whole $form
$form->getElementPrototype()->id = 'myForm';
$form->getElementPrototype()->target = '_top';
~~~
設置輸入類型(HTML5):
~~~
$form->addText('email', 'Your e-mail:')
->setType('email')
->setAttribute('placeholder', 'Please, fill in your e-mail address');
~~~
設置描述(默認情況下在輸入后呈現):
~~~
$form->addText('phone', 'Number:')
->setOption('description', 'This number will remain hidden');
~~~
為了添加HTML內容,可以使用Html類。
~~~
use Nette\Utils\Html;
$form->addText('phone', 'Phone:')
->setOption('description', Html::el('p')
->setHtml('This number remains hidden. <a href="...">Terms of service.</a>')
);
~~~
Html元素也可以用來代替label:$ form-> addCheckbox('conditions',$ label)。
# 控制器中的表單
當我們在控制器中用到表單這個組件,那我們就一定要加上
~~~
use Nette\Application\UI\Form;
~~~
它是Nette\Forms\Form擴展組件。
# 在多個控制器中使用同一個表單組件
在我們需要在多個控制器中使用一個共同表單的情況下,我們有兩個選項:
1、把表單放入控制器的層次結構中成為一個共同的父類并在那里定義一個子方法
2、或在單獨的子類中定義它,并在控制器的子類中創建它的實例。
這種類別的適當位置是例如。 app / forms / SignInFormFactory.php。 我們的子類將如下所示:
~~~
use Nette\Application\UI\Form;
class SignInFormFactory
{
/**
* @return Form
*/
public function create()
{
$form = new Form;
$form->addText('name', 'Name:');
// ...
$form->addSubmit('login', 'Log in');
return $form;
}
}
~~~
現在在每個控制器的子類中,使用我們的表單,我們使用create()方法使用我們的表單子類創建一個表單實例:
~~~
protected function createComponentSignInForm()
{
$form = (new SignInFormFactory)->create();
$form['login']->caption = 'Continue'; // we can also modify our form
$form->onSuccess[] = [$this, 'signInFormSubmitted']; // and add event handlers
return $form;
}
~~~
我們也可以在一個地方處理我們提交的表格。 這很簡單,只需將我們的事件處理程序移動到我們的子類,將其從signInFormSubmitted重命名為 submitted。 或者我們可以使用匿名函數作為處理程序:
~~~
use Nette\Application\UI\Form;
class SignInFormFactory
{
/**
* @return Form
*/
public function create()
{
$form = new Form;
$form->addText('name', 'Name:');
...
$form->addSubmit('login', 'Log in');
$form->onSuccess[] = function (Form $form, \stdClass $values) {
// we process our submitted form here
};
return $form;
}
}
~~~
# 提交表單
如果表單包含多個提交按鈕,我們需要區分,將處理程序綁定到onSubmit事件是有用的,該事件在onSuccess之前立即調用。
~~~
$form->addSubmit('login', 'Log in')
->onClick[] = [$this, 'signInFormSubmitted'];
~~~
如果通過按Enter鍵提交表單,則調用第一個提交按鈕。
僅當提交的值通過驗證時,才調用onSuccess和onClick事件的處理程序。 您不需要在處理函數中驗證輸入。 該表單還具有onSubmit事件,該事件在驗證時被調用。
# 默認值
有兩種方法可以設置默認值。 方法setDefaults():
~~~
$form->addText('name', 'Name:');
$form->addInteger('age', 'Age:');
$form->setDefaults([
'name' => 'John',
'age' => '33'
]);
~~~
或方法setDefaultValue()對單個輸入:
~~~
$form->addEmail('email', 'Email:')
->setDefaultValue('user@example.com');
~~~
選擇框和單選列表的默認值使用傳遞的值數組的鍵設置:
~~~
$form->addSelect('country', 'Country', [
'cz' => 'Czech republic',
'sk' => 'Slovakia',
]);
$form['country']->setDefaultValue('sk'); // country defaults to Slovakia
~~~
另一個有用的選項是“emptyValue”。 如果表單提交后的表單字段的值與其“emptyValue”相同,則該字段的行為為未填充。
~~~
$form->addText('phone', 'Phone:')
->setEmptyValue('+42');
~~~
# 表單外觀
表單的外觀可能大不相同。 事實上有兩個極端。 一方面是需要再一次呈現一組非常相似的表單,幾乎沒有改變。 通常是UI和后端。
另一邊是微小的甜蜜形式,每一個都是一件藝術品。 他們的布局最好用HTML編寫。 當然,除了這些極端之外,還有許多形式。
# DefaultFormRenderer
渲染器自動渲染表單。 它在表單上設置了setRenderer()方法,當調用$ form-> render()或echo $ form時,它獲得控制權。 如果我們沒有設置自定義渲染器,則使用Nette \ Forms \ Rendering \ DefaultFormRenderer。 所以你要寫的是:
~~~
echo $form;
~~~
所有輸入字段都呈現在HTML表中。 輸出可能如下所示:

很好格式化,不是嗎? :-)
這取決于你,不管是否使用表,許多網頁設計師喜歡不同的標記,例如列表。 我們可以配置DefaultFormRenderer,以便它不會渲染到表中。 我們只需要設置合適的$ wrapper。 第一個索引總是表示一個區域,第二個索引是它的元素。 所有相應的區域如圖所示:

~~~
默認情況下,一組控件被包裹在<table>中,每對都是一個包含一對標簽和控件(表格<th>和<td>)的表行<tr>。 讓我們更改所有的包裝元素。 我們將包裝控件到<dl>中,保留對本身,將標簽放入<dt>并將控件包裝到<dd>:
~~~
~~~
$renderer = $form->getRenderer();
$renderer->wrappers['controls']['container'] = 'dl';
$renderer->wrappers['pair']['container'] = NULL;
$renderer->wrappers['label']['container'] = 'dt';
$renderer->wrappers['control']['container'] = 'dd';
echo $form;
~~~
將結果轉換為以下代碼段:
~~~
<dl>
<dt><label class="required" for="frm-name">Name:</label></dt>
<dd><input type="text" class="text" name="name" id="frm-name" value="" /></dd>
<dt><label class="required" for="frm-age">Age:</label></dt>
<dd><input type="text" class="text" name="age" id="frm-age" value="" /></dd>
<dt><label>Gender:</label></dt>
...
</dl>
~~~
Wrappers 可以影響很多屬性。
1、向每個表單輸入添加特殊的CSS類
2、區分奇數行和偶數行
3、使必需和可選繪制不同
4、設置,錯誤消息是否顯示在表單上方或靠近每個元素
[Bootstrap支持](https://github.com/nette/forms/blob/a0bc775b96b30780270bdec06396ca985168f11a/examples/bootstrap3-rendering.php#L58)
# 手動渲染
您可以手動呈現表單,以更好地控制生成的代碼。 將表單放在{form myForm}和{/ form}對宏中。 輸入可以使用渲染輸入的{input myInput}宏和渲染其標簽的{label myInput /}來渲染。
~~~
{form signForm}
<!-- Simple errors rendering -->
<ul class="errors" n:if="$form->hasErrors()">
<li n:foreach="$form->errors as $error">{$error}</li>
</ul>
<table>
<tr class="required">
<th>{label name /}</th>
<td>{input name}</td>
</tr>
<!--If you need manualy render radiolist -->
<p>{input radioList:itemKey} | {input radioList:itemKeyTwo}</p>
...
</table>
{/form}
~~~
您還可以通過使用n:name屬性輕松地將表單與模板相連接。
~~~
function createComponentSignInForm()
{
$form = new Form;
$form->addText('user')->setRequired();
$form->addPassword('password')->setRequired();
$form->addSubmit('send');
return $form;
}
~~~
~~~
<form n:name=signInForm class=form>
<p><label n:name=user>Username: <input n:name=user size=20></label>
<p><label n:name=password>Password: <input n:name=password></label>
<p><input n:name=send class="btn btn-default">
</form>
~~~
~~~
您還可以使用n:name與<select>,<button>或<textarea>元素和內容。
~~~
您可以單獨通過HTML元素呈現RadioList,復選框或CheckboxList。 這被稱為局部渲染:
~~~
{foreach $form[gender]->items as $key => $label}
<label n:name="gender:$key"><input n:name="gender:$key"> {$label}</label>
{/foreach}
~~~
或者,您可以使用基本宏{input gender:$ key}和{label gender:$ key}。 訣竅是使用冒號。 對于一個簡單的復選框,使用{input myCheckbox:}。
宏formContainer有助于在表單容器中呈現輸入。
~~~
{form signForm}
<table>
<th>Which news you wish to receive:</th>
<td>
{formContainer emailNews}
<ul>
<li>{input sport} {label sport /}</li>
<li>{input science} {label science /}</li>
</ul>
{/formContainer}
</td>
...
</table>
{/form}
~~~
如何為HTML元素設置更多屬性? 方法getControl()和getLabel()將元素作為Nette \ Utils \ Html對象返回,這可以很容易地調整。
~~~
{form signForm class => 'big'}
<table>
<tr class="required">
<th>{label name /}</th>
<td>{input name cols => 40, autofocus => TRUE}</td>
</tr>
~~~
它也可以“breathe life”到一個原始的HTML輸入與n:name屬性宏,它使用它的形式輸入使用它的標識:
~~~
<table>
<tr class="required">
<th><label for="frm-name"></th>
<td><input cols=40 n:name="name"></td>
</tr>
~~~
# 分組輸入
通過創建組,輸入字段可以分組為可視字段集:
~~~
$form->addGroup('Personal data');
~~~
創建新組會激活它 - 所有進一步添加的元素都將添加到此組。 你可以建立這樣的表單:
~~~
$form = new Form;
$form->addGroup('Personal data');
$form->addText('name', 'Your name:');
$form->addInteger('age', 'Your age:');
$form->addEmail('email', 'Email:');
$form->addGroup('Shipping address');
$form->addCheckbox('send', 'Ship to address');
$form->addText('street', 'Street:');
$form->addText('city', 'City:');
$form->addSelect('country', 'Country:', $countries);
~~~
# 跨站點請求偽造(CSRF)保護
Nette Framework保護您的應用程序免受跨站點請求偽造(CSRF)攻擊。 攻擊者在網頁上誘騙受害者,該網站靜靜地向受害者登錄的服務器執行請求。 服務器不會識別用戶是否自愿發送請求。
保護很簡單:
~~~
$form->addProtection('Security token has expired, please submit the form again');
~~~
生成和驗證身份驗證令牌可以防止此攻擊。 它具有有限的到期時間:會話有效期。 感謝它不阻止在多個窗口中使用應用程序(只要它是相同的會話)。 第一個參數是顯示的錯誤消息,如果令牌已過期。
此保護應添加到更改敏感數據的所有形式。
# 多語言表單
如果要創建多語言應用程序,則需要在許多語言突變中呈現非常相同的表單。 Nette框架表單具有內置的翻譯支持。 你只需要設置一個轉換器的形式,它是一個實現Nette \ Localization \ ITranslator接口的對象,只有一個方法translate()。
~~~
class MyTranslator implements Nette\Localization\ITranslator
{
/**
* Translates the given string.
* @param string message
* @param int plural count
* @return string
*/
public function translate($message, $count = NULL)
{
return ...;
}
}
$form->setTranslator($translator);
~~~
所有標簽,錯誤消息和選擇值都被透明地翻譯成其他語言。
# Nette / Forms是獨立的
Nette / Forms可以在沒有整個框架的情況下作為獨立包使用。 您可以通過Composer安裝:
~~~
composer require nette/forms
~~~
該表單很容易創建如下:
~~~
use Nette\Forms\Form;
$form = new Form;
$form->addText('name', 'Name:');
$form->addPassword('password', 'Password:');
$form->addSubmit('send', 'Sign up');
echo $form; // renders the form
~~~
這種表單通過POST方法提交到同一頁面。 你可以輕易改變:
~~~
$form = new Form;
$form->setAction('/submit.php');
$form->setMethod('GET');
...
~~~
我們可以通過調用$ form-> isSuccess()來確定表單是否已提交并通過驗證。 如果是這樣,讓我們打印出數據。
~~~
if ($form->isSuccess()) {
echo 'Form was filled and submitted successfully';
$values = $form->getValues();
dump($values);
}
~~~
您可以訪問表單項,如訪問數組。 在我們的例子中,你可以在索引$ form ['name']上找到第一個文本輸入。
建議在處理數據后重定向到其他頁面。 您可以通過這種方式避免重復提交表單。
# 渲染表單
每個元素都有getLabel()和getControl()方法,它們返回標簽的HTML代碼和元素本身。 Nette提供getter和setter屬性訪問,就好像您正在訪問屬性本身一樣。
~~~
<?php $form->render('begin') ?>
<?php $form->render('errors') ?>
<table>
<tr class="required">
<th><?php echo $form['name']->label // Calls getLabel() ?></th>
<td><?php echo $form['name']->control // Calls getControl() ?></td>
</tr>
<tr class="required">
<th><?php echo $form['age']->label ?></th>
<td><?php echo $form['age']->control ?></td>
</tr>
...
</table>
<?php $form->render('end') ?>
~~~
- Nette簡介
- 快速開始
- 入門
- 主頁
- 顯示文章詳細頁
- 文章評論
- 創建和編輯帖子
- 權限驗證
- 程序員指南
- MVC應用程序和控制器
- URL路由
- Tracy - PHP調試器
- 調試器擴展
- 增強PHP語言
- HTTP請求和響應
- 數據庫
- 數據庫:ActiveRow
- 數據庫和表
- Sessions
- 用戶授權和權限
- 配置
- 依賴注入
- 獲取依賴關系
- DI容器擴展
- 組件
- 字符串處理
- 數組處理
- HTML元素
- 使用URL
- 表單
- 驗證器
- 模板
- AJAX & Snippets
- 發送電子郵件
- 圖像操作
- 緩存
- 本土化
- Nette Tester - 單元測試
- 與Travis CI的持續集成
- 分頁
- 自動加載
- 文件搜索:Finder
- 原子操作