[Yii2笔记]022创建表单(Creating Forms) [ 技术分享 ]
说明
学习Yii Framework 2(易2框架)的过程是漫长的,也是充满乐趣的,以下是我学习Yii2框架时对官网英文资料(请参见原文网址)的翻译和代码实现,提供了较完整的代码,供你参考。不妥之处,请多多指正!
原文网址:
http://www.yiiframework.com/doc-2.0/guide-input-forms.html
本文主题:创建表单(Creating Forms)
1、基于AR的表单:ActiveForm 2、创建列表 3、集成Pjax 4、深入阅读
1、基于AR(ActiveRecord)的表单:ActiveForm
在Yii中使用表单的方式是通过yii\widggets\ActiveForm来完成,当一个表单是基于模型时,此方法是首选方法。另外,在yii\helpers\Html中有一些有用的方法,使用它们可以为表单添加按钮和帮助文本。 一个表单,是显示在客户端的,在多数情况下会有一个响应模型,此模型用于在服务器端验证输入信息(更多信息请查看Validating Input章节)。 Validating Input章节: http://www.yiiframework.com/doc-2.0/guide-input-validation.html 当创建基于模型的表单时,第一步是先定义模型(model),此模型可以是基于AR(Active Record)类的(代表数据库中的数据),也可以是一个继承自yii\base\Model的基本模型类(Model class),后者可以获取任意类型的输入数据,例如:一个登录表单。在下例中,我们将展示如何使用一个基本模型来创建一个登录表单: <?php class LoginForm extends \yii\base\Model{
public $username;
public $password;
public function rules(){
return [
//在此定义一些验证规则
];
}
}
在控制器中,我们将传递一个模型的实例到视图中去,在视图中将使用一个ActiveForm小部件来显示表单: <?php use yii\helpers\Html; use yii\widgets\ActiveForm; $form=ActiveForm::begin([
'id'=>'login-form',
'options'=>['class'=>'form-horizontal'],
]);?>
<?=$form->field($model,'username')?>
<?=$form->field($model,'password')->passwordInput()?>
<div class="form-group">
<div class="col-lg-offset-1 col-lg-11" >
<?=Html::submitButton('Login',['class'=>'btn btn-primary'])?>
</div>
</div>
<?php ActiveForm::end()?>
<?
使用begin()和end()括起来
在上例代码中ActiveForm::begin()不会产生一个表单实例,仅仅标识此处是表单的开始。放置在ActiveForm::begin()和ActiveForm::end()中的内容将被放置到HTML的
标签中。和其他小部件一样,你可以定义传递一个数组到begin()方法用于配置此小部件,此时,附加的CSS类和表单id就可以在打开的标签中使用了,关于所有可用选项,请参考yii\widgets\ActiveForm的API文件: http://www.yiiframework.com/doc-2.0/yii-widgets-activeform.html活动字段(ActivedField)
为了在表单中创建一个带说明标签(label)和JavaScript验证的表单元素,需要调用ActiveForm::field()方法,它将返回一个yii\widgets\ActiveField实例。当此方法的输出被直接显示时,你会看到它就是一个文本的input元素。要自定义这些输出的内容,你可以在此调用上添加更多的ActiveField链式方法:
<!-- 一个密码输入框 -->
<?= $form->field($model, 'password')->passwordInput() ?>
<!--添加一个提示(在输入框下方)和自定义的说明标签 -->
<?= $form->field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?>
<!-- 创建一个HTML5邮箱输入框 -->
<?= $form->field($model, 'email')->input('email') ?>
<? 这将生成包含、和其他标签元素,使用的是表单字段定义的模板,输入项的名称将由模型的表单名字和属性名称最终决定,例如:上例中username属性的输入项名称是LoginForm[username],此命名规则将为 所有的属性都将使用此命名规则为登录表单创建相应的输入项,这些输入项在服务器端使用$_POST['LoginForm']获取。
小贴士:如果在表单中只有一个模型,要简化输入项的名称,你可以在重写模型的formName()方法返回一个空字符串,从而覆盖表单数组中的定义,此用法在GridView中创建简洁URL时非常有用。 D:\phpwork\advanced\frontend\models\Validator.php
public function formName() {
return "";
}
定义模型属性可通过许多巧妙的方法来实现,例如,当上传多个文件或选择多值时一个选项会是一个数组值,此时,你需要定义属性名称时在其后添加[]:
//允许多个文件被上传:
echo $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']);
//允许items可以多选:
echo $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']);
要特别注意的是命名提交按钮元素的名称,jQuery文档中有一些保留字会造成冲突: 表单和它们的子元素不能使用与表单属性相冲突的名称或id,如submit、length、method,名称冲突会造成令人困惑的错误。关于一个避免此类问题的完整规则列表,请参见DOMLint文档: http://kangax.github.io/domlint/
其他的HTML标签可以使用原生的HTML或Html-helper类,如上例中的Html::submitButton()。
小贴士:如果你在应用中使用Twitter的Bootstrap CSS,你可以使用yii\bootstrap\ActiveForm替换yii\widgets\ActiveForm。此扩展表单扩展自后者,并在创建输入项时使用Bootstrap定义的样式表。
小贴士:如果要定义带星号的必填字段,你可以使用如下的CSS:
div.required label.control-label:after{
content:"*";
color:red;
}
2、创建列表(Creating Lists)
有3种列表: dropdown列表 radio列表 checkbox列表
要创建一个列表,需要先准备列表项,可以手工建一个:
$item=[
1=>'item 1',
2=>'item 2',
];
或从数据库中获取:
$item=Category::find()
->select(['label'])
->indexBy(['_id'=>SORT_ASC])
->column();
这些列表项可以被不同的列表小部件处理,表单项的值(包括当前激活项)将根据$model 属性的当前值被自动设置。
创建一个下拉列表(drop-down list)
我们可以使用活动字段的yii\widgets\ActiveField::dropDownList()方法创建一个下拉列表:
echo $form->field($model,'category')->dropdownlist([
1=>'item 1',
2=>'itme 2',
],
['propt'=>'Select Category']
);
创建一个单选列表(radio list)
我们可以使用一个活动字段的yii\widgets\ActiveField::radioList()方法来创建一个单选列表:
echo $form->field($model,'category')->radioList([
1=>'radio 1',
2=>'radio 2',
]);
创建一个复选框列表(checkbox list)
我们可以使用活动字段的yii\widgets\ActiveField::checkboxList()方法来创建一个复选框列表:
echo $form->field($model,'category')->checkboxList([
1=>'checkbox 1',
2=>'checkbox 2'
]);
3、集成Pjax
Pjax小部件允许你更新页面中一部分,而不是整个页面,你可以在提交后使用它只替换表单部分的内容。 你可以配置$formSelector 定义来定义表单的哪个部分来触发Pjax,如果没有设置此项,则被Pjax包起来的内容中有data-pjax属性的表单都将触发Pjax请求。 $formSelector 的文档: http://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html#$formSelector-detail
use yii\widgets\Pjax;
use yii\widgets\ActiveForm;
Pjax:begin([
// Pjax选项
]);
$form=ActiveForm::begin([
'options' =>['data'=>['pjax'=>true]]
//更多ActiveForm选项
]);
// ActiveForm内容
ActiveForm::end();
Pjax::end();
小贴士:在Pjax小部件内部谨慎使用连接,因为返回的内容将在小部件内容进行渲染,如果要防止此种情况,可以在HTML中使用data-ajax="0"属性。
提交按钮和文件上传的值
众所周知,在上传文件和提交按钮值时,使用jQuery.serializeArray()会存在问题,现在都在使用HTML5的FormData类来完成。 也就是说,在Ajax或使用Pjax小部件中上传文件和获取提交按钮值都将使用浏览器支持的FormData类。
使用pjax
http://www.yiiframework.com/doc-2.0/yii-widgets-pjax.html Pjax是一个小部件,它集成了jQuery的pjax插件。 pjax只处理其begin()和end()之间包含的内容,将调用小部件中定义的body内容。默认情况下,任何链接被点击,或在body内容中的表单提交(带data-ajax属性的表单)都会触发AJAX请求。作为对AJAX的请求的响应,Pjax会发送更新的body内容到客户端,这些内容会替换原有内容。浏览器的URL也会使用pushState进行更新,整个处理请求的过程将不重载模板和相关的资源(js,css)。 你可以配置$linkSelector 定义哪个链接将触发pjax,也可以配置$formSelector 来定义哪个表示的提交会触发pjax。 你可以给容器中的链接添加data-pjax="0"属性以使其不要触发pjax。 下例将展示如何与yii\grid\GridView小部件一起使用Pjax,这样分页、排序和筛选都可以通过pjax来完成。
ues yii\widgets\Pjax;
Pjax::begin();
echo GridView::widget([...]);
Pjax::end();
Pjax实例,使用Pjax方式发布新记录,并在同一个页面中直接显示出来
http://localhost:8081/country/index-pjax //post//new 原文网址:
http://www.yiichina.com/tutorial/484
http://www.yiiframework.com/wiki/772/pjax-on-activeform-and-gridview-yii2/
/ 本例存在的问题: 表单中无法进行Ajax验证,如['name', 'unique'], /
D:\phpwork\basic\controllers\CountryController.php
源代码:
public function actionIndexPjax() {
$model = new Country();
if ($model->load(Yii::$app->request->post()) && $model->save())
{
$model = new Country();
}
$searchModel = new CountrySearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('indexPjax', ['searchModel' => $searchModel, 'dataProvider' => $dataProvider,'model' => $model,]);
}
D:\phpwork\basic\views\country\indexPjax.php
源代码:
<?php
use yii\helpers\Html;
use yii\grid\GridView;
use yii\widgets\Pjax;
/* @var $this yii\web\View */
/* @var $searchModel app\models\CountrySearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = Yii::t('app', 'Countries Pjax');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="country-index">
<h1><?= Html::encode($this->title)." Time22:".time() ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<p>
<?= Html::a(Yii::t('app', 'Create Country'), ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?php Pjax::begin([
'id' => 'countries',
'enablePushState' => true,
'timeout' => 1000,
]); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'code',
'name',
'continent',
'population',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
<?php Pjax::end(); ?>
<?= $this->render('formPjax', [
'model' => $model,
]) ?>
</div>
<? D:\phpwork\basic\views\country\formPjax.php
源代码:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* @var $this yii\web\View */
/* @var $model app\models\Countries */
/* @var $form yii\widgets\ActiveForm */
?>
<?php
$this->registerJs(
'$("document").ready(function(){
$("#new_country").on("pjax:end", function() {
$.pjax.reload({container:"#countries"}); //Reload GridView
});
});'
);
?>
<div class="countries-form">
<?php yii\widgets\Pjax::begin(['id' => 'new_country']) ?>
<?php $form = ActiveForm::begin(['options' => ['data-pjax' => true ]]); ?>
<?= $form->field($model, 'name')->textInput(['maxlength' => 200]) ?>
<?= $form->field($model, 'code')->textInput(['maxlength' => 2]) ?>
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
<?php yii\widgets\Pjax::end() ?>
</div>
测试结果:
<?
http://localhost:8081/country/index-pjax
/*
Countries Pjax Time22:1491008923
Create Country
第1-2条,共14条数据.
# Code Name Continent Population
1 cc cccccc (未设置) (未设置)
2 nn bbbbbb
*/
Pjax实例,使用Pjax方式实现列表页同一页面中显示记录详情
//detail,本例的详情在列表的下方,可改成左列表右详情的格式,更实用一些。 原文网址:
http://www.yiiframework.com/wiki/845/a-single-page-with-a-list-and-a-detail
D:\phpwork\basic\controllers\ArticlesController.php
源代码:
public function actionIndex()
{
$searchModel = new ArticlesSearhch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$article=new Articles();
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'article'=>$article,
]);
}
public function actionAjaxView($id)
{
return $this->renderPartial('_view', [
'model' => $this->findModel($id),
]);
}
D:\phpwork\basic\views\articles\index.php
源代码:
<?php
use yii\helpers\Html;
use yii\grid\GridView;
use yii\widgets\Pjax;
/* @var $this yii\web\View */
/* @var $searchModel app\models\ArticlesSearhch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = Yii::t('app', 'Articles');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="articles-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<p>
<?= Html::a(Yii::t('app', 'Create Articles'), ['create'], ['class' => 'btn btn-success']) ?>
<?= Html::a(Yii::t('app', 'Authors'), ['authors/index'], ['class' => 'btn btn-primary']) ?>
<?= Html::a(Yii::t('app', 'Tags'), ['tag/index'], ['class' => 'btn btn-primary']) ?>
<?= Html::a(Yii::t('app', 'Two GridView'), ['authorsql/multiple'], ['class' => 'btn btn-primary']) ?>
<?= Html::a(Yii::t('app', 'ArticlesSql'), ['articles/index-sql'], ['class' => 'btn btn-primary']) ?>
<?= Html::a(Yii::t('app', 'ArticlesArray'), ['articles/index-array'], ['class' => 'btn btn-primary']) ?>
<?= Html::a(Yii::t('app', 'ArticlesGridview'), ['articles/index-gridview'], ['class' => 'btn btn-primary']) ?>
</p>
<?php Pjax::begin(); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
//'filterPosition' =>GridView::FILTER_POS_HEADER,//将搜索框置于表头上方
//'filterPosition' =>GridView::FILTER_POS_BODY,//默认值,将搜索框置于表头下方
// 'filterPosition' =>GridView::FILTER_POS_FOOTER,//将搜索框置于表尾(需要先显示表尾)
// 'showFooter' => true,//显示表尾
'layout' => '{pager}{summary}{items}{summary}{pager}',//定义各部分的显示位置
'emptyText' => '没有数据!',//设置没有记录时的显示信息
'headerRowOptions' => [
'class' => 'text-center',
],
'rowOptions' => [
'class' => 'text-center',//记录行内容居中
'style'=>'cursor:pointer',//鼠标样式为手形
],
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'authors.name',
'lastEdited:date',
'title',
// 'description:ntext',
// 'content:ntext',
// 'format',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
<?php Pjax::end(); ?>
<div id="post-detail">
<?php echo $this->render('_view', ['model' => $article]); ?>
</div>
</div>
<?php
$ajax_url = yii\helpers\Url::to(['ajax-view']); //获取ajax响应地址
$csrf_param = Yii::$app->request->csrfParam; //获取csrf参数名
$csrf_token = Yii::$app->request->csrfToken; //获取csrf参数值
$this->registerJs("
$('div.articles-index').on('click', 'tr', function() { //仅对div.articles-index下的tr触发点击事件
var id = $(this).data('key'); //获取id
if(id){//id存在时才触发ajax请求
$.ajax({
'type' : 'GET',
'url' : '$ajax_url',
'dataType' : 'html',
'data' : {
//替换为变量值:
'$csrf_param' : '$csrf_token',//'_csrf' : 'R2xwQ2F5ZC13Dx4EBAMPfwAdJRoUNRBDMxQSMycwIlgwVBIJVB0dHQ==',
'id' : id
},
'success' : function(data){ //返回的数据显示
$('#post-detail').html(data);
}
});
}
});
");
?>
D:\phpwork\basic\views\articles_view.php
源代码:
<?php
use yii\helpers\Html;
use yii\widgets\DetailView;
?>
<h1><?= Html::encode($model->title) ?></h1>
<?= DetailView::widget([
'model' => $model,
'attributes' => [
'id',
'authorId',
'lastEdited',
'title',
'description:ntext',
'content:ntext',
'format',
],
]) ?>
测试结果:
http://localhost:8081/articles/index
/*
北京清明假期高速路出行量将创历史新高
ID 19
authorId 5
Last Edited Time 1490940801
Title 北京清明假期高速路出行量将创历史新高
*/
4、深入阅读
下一章节Validating Input处理提交表单数据的验证问题,包括客户端的验证和ajax方式的服务器端验证。 关于表单更复杂的用法,你可以阅读以下内容:
收集选项卡输入:使用Tab为多个模型收集数据 http://www.yiiframework.com/doc-2.0/guide-input-tabular-input.html
从多模型获取数据:在同一个表单中处理多个不同的模型 http://www.yiiframework.com/doc-2.0/guide-input-multiple-models.html
上传文件:如何使用表单上传文件 http://www.yiiframework.com/doc-2.0/guide-input-file-upload.html
//ActiveForm防止重复提交的官网写法: D:\phpwork\news\controllers\SiteController.php
源代码:
public function actionContact()
{
$model = new ContactForm();
if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
Yii::$app->session->setFlash('contactFormSubmitted');
return $this->refresh();
}
return $this->render('contact', [
'model' => $model,
]);
}
D:\phpwork\news\views\site\contact.php
源代码:
<?php
/* @var $this yii\web\View */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\ContactForm */
use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\captcha\Captcha;
$this->title = 'Contact';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-contact">
<h1><?= Html::encode($this->title) ?></h1>
<?php if (Yii::$app->session->hasFlash('contactFormSubmitted')): ?>
<div class="alert alert-success">
Thank you for contacting us. We will respond to you as soon as possible.
</div>
<p>
Note that if you turn on the Yii debugger, you should be able
to view the mail message on the mail panel of the debugger.
<?php if (Yii::$app->mailer->useFileTransport): ?>
Because the application is in development mode, the email is not sent but saved as
a file under <code><?= Yii::getAlias(Yii::$app->mailer->fileTransportPath) ?></code>.
Please configure the <code>useFileTransport</code> property of the <code>mail</code>
application component to be false to enable email sending.
<?php endif; ?>
</p>
<?php else: ?>
<p>
If you have business inquiries or other questions, please fill out the following form to contact us.
Thank you.
</p>
<div class="row">
<div class="col-lg-5">
<?php $form = ActiveForm::begin(['id' => 'contact-form']); ?>
<?= $form->field($model, 'name')->textInput(['autofocus' => true]) ?>
<?= $form->field($model, 'email') ?>
<?= $form->field($model, 'subject') ?>
<?= $form->field($model, 'body')->textarea(['rows' => 6]) ?>
<?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [
'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
]) ?>
<div class="form-group">
<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
</div>
<?php endif; ?>
</div>
(全文完)
共 0 条回复
阿江
最后登录:2024-03-03
在线时长:186小时21分
- 粉丝94
- 金钱16816
- 威望160
- 积分20276