/ yii2

yii2-form

ActiveField

Yii生成的每个field由4部分组成:

① 最外层div为每个field的容器,
② label为每个field的文本说明,
③ input为输入元素,
④ 还有一个div为错误提示信息。

<div class="form-group field-loginform-username required has-error">
    <label class="control-label" for="loginform-username">Username</label>
    <input type="text" id="loginform-username" class="form-control" name="LoginForm[username]">
    <div class="help-block">Username cannot be blank.</div>
</div>

表单数据的填充

$model->load(Yii::$app->request->post())
yii\base\Model::load([
    'csrf_app' => '随机字符串',
    'SignupForm' => [
        'username' => 'xxx',
        'password' => 'yyy'
    ]
])  
yii\base\Model::setAttributes([
    'username' => 'xxx',
    'password' => 'yyy'
])  
yii\base\Model::safeAttributes()  
yii\base\Model::scenarios()  
yii\base\Model::getValidators()  

创建 Form

推荐使用 Gii 的 Form Generator 来创建 Form:

  • View Name:site/login
  • Model Class:app\models\User
  • Scenario:login

还会提示你可以复制可能用到的控制器代码:

public function actionLogin()
{
    $model = new app\models\User(['scenario' => 'login']);

    if ($model->load(Yii::$app->request->post())) {
        if ($model->validate()) {
            // form inputs are valid, do something here
            return;
        }
    }

    return $this->render('login', [
        'model' => $model,
    ]);
}

ActiveForm

以面向对象的方式重用 UI 代码:http://haobing.wang/yii2-widget/

一般有两种 activeform:

  • yii\widgets\ActiveForm
  • yii\bootstrap\ActiveForm

input 更灵活的布局

yii\widgets\ActiveField

<?= $form->field($model, 'nickname', [
     'template' => '<div class="input-group input-group-lg"><label class="input-group-addon glyphicon glyphicon-user" for="signupform-nickname"></label>{input}</div>{error}'])
     ->input('text', ['placeholder' => '姓名'])->label(false) ?>

输出:

<div class="form-group field-signupform-nickname required">
    <div class="input-group input-group-lg">
        <label class="input-group-addon glyphicon glyphicon-user" for="signupform-nickname"></label>
        <input type="text" id="signupform-nickname" class="form-control" name="SignupForm[nickname]" aria-required="true">
    </div>
    <p class="help-block help-block-error"></p>
</div>

等价于:

<div class="form-group field-signupform-username required">
    <div class="input-group input-group-lg">
        <label class="input-group-addon glyphicon glyphicon-phone" for="signupform-username"></label>
        <?= Html::activeTextInput($model, 'username', ['class' => 'form-control']); ?>
        </div>
    <?= Html::error($model, 'username', ['class' => 'help-block help-block-error', 'tag' => 'p']); ?>
</div>

使用 yii\helpers\Html 相比 yii\widgets\ActiveField 更灵活,可以自行选择是否显示 label 或者 error 的位置,在前台网站展示使用时更方便,但是需要显示将 attribute 名称加到最外层的 div 和 label ,一遍验证样式可以正常显示。

表单错误

<?php $form = ActiveForm::begin(); ?>

<?= $form->errorSummary($model); ?>

<?php ActiveForm::end(); ?>

下拉菜单

<?php echo $form->field($model, 'status')->dropDownList(Order::$status, ['prompt' => '请选择状态']) ?>

请选择状态 会显示在第一项:

日期选择

参考:

composer require --prefer-dist yiisoft/yii2-jui 
use yii\jui\DatePicker;

<?= $form->field($model,'created_at')->widget(DatePicker::className(),['clientOptions' => ['dateFormat' => 'yyyy-MM-dd']])->textInput(['placeholder' => '请选择日期']) ?>

GridView 中:

[
    'attribute' => 'list_date',
     // 自定义列宽度,当筛选框显示不全时使用
    'headerOptions' => ['width' => '100'],
    'filter' => \yii\jui\DatePicker::widget([
         'model'=>$searchModel,
         'attribute' => 'list_date',
         'dateFormat' => 'yyyy-MM-dd']
     ),
    'format' => 'raw',
],

Bootstrap 日期选择

composer require 2amigos/yii2-date-picker-widget:1.0.7

Package operations: 2 installs, 0 updates, 0 removals

  • Installing bower-asset/bootstrap-datepicker (v1.7.0): Downloading (100%)
  • Installing 2amigos/yii2-date-picker-widget (1.0.7): Downloading (100%)

注意版本匹配。

clientOptions 可以设置的值,参考 http://bootstrap-datepicker.readthedocs.io/en/v1.7.0/

https://github.com/2amigos/yii2-date-picker-widget

选月份

GridView:

[
     'attribute' => 'month',
     'headerOptions' => ['width' => '150'],
     'filter' => \dosamigos\datepicker\DatePicker::widget([
           'model' => $searchModel,
           'attribute' => 'month',
           'language' => Yii::$app->language,
           'clientOptions' => [
               'autoclose' => true,
               'startView'=> 'months',
               'minViewMode'=> 'months',
               'format' =>  'yyyy-mm',
            ]
      ]),
],
<?= $form->field($model, 'created_at')->widget(
        \dosamigos\datepicker\DatePicker::className(), [
        'language' => Yii::$app->language,
        'template' => '{addon}{input}',
        'clientOptions' => [
            'autoclose' => true,
            'format' => 'yyyy-mm-dd',
            'startView'=> 'days',
            'minViewMode'=> 'days',
        ],
    ]);?>

或者使用时间区间:

    <?= $form->field($model, 'createdFrom')->widget(\dosamigos\datepicker\DateRangePicker::className(), [
        'attributeTo' => 'createdTo',
        'form' => $form, // best for correct client validation
        'language' => Yii::$app->language,
        'clientOptions' => [
            'autoclose' => true,
            'format' => 'yyyy-mm-dd',
        ]
    ]);?>

搜索表单

Url::to(['bill/index', 'BillSearch' => ['order_id' => $model->orderid]])

这里对应的需要注意的是 SearchModel BillSearch

文件上传

实例:https://yii2-cookbook.readthedocs.io/forms-uploading-files/

寻找可用组件:https://packagist.org/?q=yii2 upload&p=0

php 默认上传文件大小为 2M,需要手动修改配置文件 /etc/php.ini

upload_max_filesize = 5M

然后重启 php-fpm:

sudo systemctl restart php-fpm.service

表单验证

一个表单,就是一个 FormModel。

输入过滤 则对应 filter 验证器:

[
    // trim 掉 "username" 和 "email" 输入
    [['username', 'email'], 'filter', 'filter' => 'trim', 'skipOnArray' => true],

    // 标准化 "phone" 输入
    ['phone', 'filter', 'filter' => function ($value) {
        // 在此处标准化输入的电话号码
        return $value;
    }],
]

核心验证器: https://www.yiiframework.com/doc/guide/2.0/zh-cn/tutorial-core-validators
处理空值: https://www.yiiframework.com/doc/guide/2.0/zh-cn/input-validation#handling-empty-inputs

Ajax 验证

默认情况下,ActiveForm 不启用 ajax 验证,enableAjaxValidation 为 false。

View 中开启 ajax 验证:

use yii\widgets\ActiveForm;

$form = ActiveForm::begin([
    'id' => 'registration-form',
]);

echo $form->field($model, 'username', ['enableAjaxValidation' => true]);

// ...

ActiveForm::end();

如果需要对整个表单启用 ajax 验证,则需要将 form 的 enableAjaxValidation 属性设置为 true

$form = ActiveForm::begin([
    'id' => 'contact-form',
    'enableAjaxValidation' => true,
]);

Controller 中对 ajax 请求做验证处理:

if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
    Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    return \yii\bootstrap\ActiveForm::validate($model);
}

afterValidate

$('[data-form="user"]').on('afterValidate',function (event, messages, errorAttributes){  
        console.log(messages);
        console.log(errorAttributes);
        if(errorAttributes.length>0){  
            console.log(errorAttributes); 
        }else{  
            console.log('clear'); 
        }  
    }); 

beforeSubmit

Use beforeSubmit event instead of submit, the beforeSubmit will only be triggered once the form passes the validation.

$('form').on('beforeSubmit', function(e) {
    var form = $(this);
    var formData = form.serialize();
    $.ajax({
        url: form.attr("action"),
        type: form.attr("method"),
        data: formData,
        success: function (data) {
             //...
        },
        error: function () {
            alert("Something went wrong");
        }
    });
}).on('submit', function(e){
    e.preventDefault();
});

常用方法

$('#w0').yiiActiveForm('submitForm') // work, show errors
$('#w0').yiiActiveForm('resetForm') // work, hide errors
$('#w0').yiiActiveForm("validateAttribute", 'signupform-username');
$('#w0').yiiActiveForm('validate')

Working with ActiveForm via JavaScript

ActiveForm

https://yii2-cookbook.readthedocs.io/forms-activeform-js/

  • yii\web\YiiAssetyii.js
  • yii\validators\ValidationAssetyii.validation.js
  • yii\widgets\ActiveFormAssetyii.activeForm.js
jQuery(function($) {
    jQuery('#operate-modal').modal({
        "show": false
    });
    jQuery('#w0').yiiActiveForm([{
        "id": "signupform-nickname",
        "name": "nickname",
        "container": ".field-signupform-nickname",
        "input": "#signupform-nickname",
        "error": ".help-block.help-block-error",
        "enableAjaxValidation": true,
        "validateOnChange": false,
        "validateOnBlur": false,
        "validate": function(attribute, value, messages, deferred, $form) {
            value = yii.validation.trim($form, attribute, []);
            yii.validation.required(value, messages, {
                "message": "请输入您的姓名"
            });
            yii.validation.string(value, messages, {
                "message": "姓名长度异常",
                "min": 2,
                "tooShort": "姓名应该包含至少2个字符。",
                "max": 32,
                "tooLong": "姓名只能包含至多32个字符。",
                "skipOnEmpty": 1
            });
        }
    },
    {
        "id": "signupform-username",
        "name": "username",
        "container": ".field-signupform-username",
        "input": "#signupform-username",
        "error": ".help-block.help-block-error",
        "enableAjaxValidation": true,
        "validateOnChange": false,
        "validateOnBlur": false,
        "validate": function(attribute, value, messages, deferred, $form) {
            value = yii.validation.trim($form, attribute, []);
            yii.validation.required(value, messages, {
                "message": "手机号不能为空。"
            });
            yii.validation.regularExpression(value, messages, {
                "pattern": /^1[345789]{1}\d{9}$/,
                "not": false,
                "message": "手机号格式不正确",
                "skipOnEmpty": 1
            });
        }
    },
    {
        "id": "signupform-password",
        "name": "password",
        "container": ".field-signupform-password",
        "input": "#signupform-password",
        "error": ".help-block.help-block-error",
        "enableAjaxValidation": true,
        "validateOnChange": false,
        "validateOnBlur": false,
        "validate": function(attribute, value, messages, deferred, $form) {
            yii.validation.required(value, messages, {
                "message": "密码不能为空。"
            });
            yii.validation.string(value, messages, {
                "message": "密码必须是一条字符串。",
                "min": 6,
                "tooShort": "密码应该包含至少6个字符。",
                "skipOnEmpty": 1
            });
        }
    },
    {
        "id": "signupform-captcha",
        "name": "captcha",
        "container": ".field-signupform-captcha",
        "input": "#signupform-captcha",
        "error": ".help-block.help-block-error",
        "enableAjaxValidation": true,
        "validateOnChange": false,
        "validateOnBlur": false
    },
    {
        "id": "signupform-code",
        "name": "code",
        "container": ".field-signupform-code",
        "input": "#signupform-code",
        "error": ".help-block.help-block-error",
        "enableAjaxValidation": true,
        "validateOnChange": false,
        "validateOnBlur": false,
        "validate": function(attribute, value, messages, deferred, $form) {
            value = yii.validation.trim($form, attribute, []);
            yii.validation.required(value, messages, {
                "message": "请输入短信验证码"
            });
            yii.validation.regularExpression(value, messages, {
                "pattern": /^\d{6}$/,
                "not": false,
                "message": "短信验证码为 6 位数数字",
                "skipOnEmpty": 1
            });
        }
    }], {
        "validationUrl": "site\/signup-validate"
    });
});

5 个 js 文件:

  • yii\assets\yii.js
  • yii\assets\yii.activeForm.js
  • yii\assets\yii.captcha.js
  • yii\assets\yii.gridView.js
  • yii\assets\yii.validation.js

yii\assets\yii.js

yii is the root module for all Yii JavaScript modules.
It implements a mechanism of organizing JavaScript code in modules through the function "yii.initModule()".

Each module should be named as "x.y.z", where "x" stands for the root module (for the Yii core code, this is "yii").

A module may be structured as follows:

window.yii.sample = (function($) {
     var pub = {
         // whether this module is currently active. If false, init() will not be called for this module
         // it will also not be called for all its child modules. If this property is undefined, it means true.
         isActive: true,
         init: function() {
             // ... module initialization code goes here ...
         },

         // ... other public functions and properties go here ...
     };

     // ... private functions and properties go here ...

     return pub;
 })(window.jQuery);

Using this structure, you can define public and private functions/properties for a module.
Private functions/properties are only visible within the module, while public functions/properties
may be accessed outside of the module. For example, you can access "yii.sample.isActive".

You must call "yii.initModule()" once for the root module of all your modules.

在 console 中直接输入 yii 即可。

使用方法:

yii.confirm('hhhel',function(){console.log('ok')},function(){console.log('cancel')});
yii.getCsrfParam()

form 模块

$('#contact-form').on('beforeSubmit', function (e) {
    if (!confirm("Everything is correct. Submit?")) {
        return false;
    }
    return true;
});

可用的事件:

  • beforeValidate
  • afterValidate
  • beforeValidateAttribute
  • afterValidateAttribute
  • beforeSubmit
  • ajaxBeforeSend
  • ajaxComplete

Validation 模块

yii.validation.addMessage(messages, message, value)
yii.validation.boolean(value, messages, options)
yii.validation.captcha(value, messages, options)
yii.validation.compare(value, messages, options, $form)
yii.validation.email(value, messages, options)
yii.validation.file(attribute, messages, options)
yii.validation.image(attribute, messages, options, deferredList)
yii.validation.ip(value, messages, options)
yii.validation.isEmpty(value)
yii.validation.number(value, messages, options)
yii.validation.range(value, messages, options)
yii.validation.regularExpression(value, messages, options)
yii.validation.required(value, messages, options)
yii.validation.string(value, messages, options)
yii.validation.trim($form, attribute, options)
yii.validation.url(value, messages, options)
yii.validation.validateImage(file, messages, options, deferred, fileReader, image)

使用:

yii.validation.isEmpty('xx')
false
yii.validation.isEmpty('')
true