【问题标题】:Yii 1.1 - creating a multi step form with validationYii 1.1 - 创建一个带有验证的多步骤表单
【发布时间】:2014-01-06 22:01:21
【问题描述】:

我基本上是在尝试使用 Yii 中的 CActiveForm 类创建一个多步骤表单。我的想法是我想使用内置功能以最简单的方式实现这一目标。我的要求如下:

  • 多步骤一页表单(使用通过 jQuery 显示/隐藏的 DIV)
  • 每个步骤的 AJAX 验证(仅验证特定于步骤的属性)
  • 必须使用 validateOnChange() 和 validateOnSubmit() 方法进行验证

这是我迄今为止开发的一个半有效的解决方案:

查看:

<div class="form">

        <?php $form = $this->beginWidget('CActiveForm', array(
            'id'=>'listing-form',
            'enableClientValidation'=>false,
            'enableAjaxValidation'=>true,
            'clientOptions'=>array(
                'validateOnChange'=>true,
                'validateOnSubmit'=>true,
                'afterValidate'=>'js:validateListing',
            ),
        )); ?>

        <?php echo $form->errorSummary($model); ?>

        <div class="step" id="step-1">
            // model input fields
            <?php echo CHtml::submitButton('Next Step', array('name'=>'step1')); ?>
        </div>

        <div class="step" id="step-2" style="display: none;">
            // model input fields
            <?php echo CHtml::submitButton('Next Step', array('name'=>'step2')); ?>
        </div>

        <div class="step" id="step-3" style="display: none;">
            // model input fields
            <?php echo CHtml::submitButton('Submit', array('name'=>'step3')); ?>
        </div>

        <?php $this->endWidget(); ?>

</div>

JavaScript:

function validateListing(form, data, hasError)
{
        if(hasError)
        {
            // display JS flash message
        }
        else
        {
            if($('#step-1').css('display') != 'none')
            {
                $('#step-1').hide();
                $('#step-2').show();
            }
            else if($('#step-2').css('display') != 'none')
            {
                $('#step-2').hide();
                $('#step-3').show();
            }
            else if($('#step-3').css('display') != 'none')
            {
                return true; // trigger default form submit
            }
        }
}

控制器:

public function actionCreate()
{
        $model = new Listing;

        // step 1 ajax validation
        if(isset($_POST['step1']))
        {
            $attributes = array('name', 'address1', 'etc');

            $this->performAjaxValidation($model, $attributes);
        }

        // step 2 ajax validation
        if(isset($_POST['step2']))
        {
            $attributes = array('category', 'type', 'etc');

            $this->performAjaxValidation($model, $attributes);
        }

        // step 3 ajax validation
        if(isset($_POST['step3']))
        {
            $attributes = array('details', 'source', 'etc');

            $this->performAjaxValidation($model, $attributes);
        }

        // process regular POST
        if(isset($_POST['Listing']))
        {
            $model->attributes = $_POST['Listing'];

            if($model->validate()) // validate all attributes again to be sure
            {
               // perform save actions, redirect, etc
            }
        }

        $this->render('create', array(
            'model'=>$model,
        ));
}

protected function performAjaxValidation($model, $attributes=null)
{
    if(isset($_POST['ajax']) && $_POST['ajax']==='listing-form')
    {
        echo CActiveForm::validate($model, $attributes);
        Yii::app()->end();
    }
}

总结一下。基本上我所拥有的是一个带有 3 个提交按钮的表单(每个步骤一个)。在我的控制器中,我检查按下了哪个提交按钮,并对特定于该步骤的属性运行 AJAX 验证。

我使用自定义afterValidate() 函数在提交时显示/隐藏步骤。在第 3 步,触发了默认的表单提交,它将所有表单属性发布到控制器。

这很好用,但它不适用于validateOnChange()(因为没有发布提交按钮)。另外我想知道这是否实际上是最好的方法,或者是否有人知道更好的方法?

谢谢。

【问题讨论】:

  • 如何在模型中使用场景?请更新您的代码

标签: yii


【解决方案1】:

我建议使用场景来打开和关闭适当的规则。根据发送到控制器的内容调整模型场景。

注意:这也可能是使用 CFormModel 而不是 CActiveRecord 的好地方,具体取决于您的表单中的内容。

编辑:您可以在每个 div 部分添加一个隐藏字段,其中包含有关您所在步骤的信息吗?看起来应该可以代替您的提交按钮。

【讨论】:

  • 表单中的数据是要保存到数据库中的,这就是我使用ActiveRecord的原因。我确实尝试过使用场景,但这仍然没有做任何不同的事情(除了稍微简化一些控制器代码)。我仍然无法让它与 validateOnChange 一起使用。
  • @GSTAR 刚刚发布了另一个建议。
  • 问题是因为它是一个大表单,在每个步骤中添加一个隐藏字段不会有任何影响,因为每次都会发布所有 3 个隐藏字段。
【解决方案2】:

选项 1 当您没有收到按钮时,为什么不验证整个表单,为什么只需要验证特定属性? Yii 将验证整个模型,发回所有错误,但只有特定错误会被活动表单显示,因为它已经是这样工作的。

选项 2 您可以有 3 个表格(不是现在的 1 个),每个步骤 1 个。还要为每个步骤创建 3 个场景 1。

每个表单都有一个隐藏字段,它与表单一起发布,它实际上可以是场景名称,只需在它进入时验证它。使用此隐藏字段验证模型以设置您所在的场景。

表单提交成功后,您可以在模型上缓存部分,最终您拥有完整的模型。

【讨论】:

    【解决方案3】:

    您始终可以进行自定义验证,它不会破坏您的正常表单验证

    在你的模型中

    private $step1 = false;
    private $step2 = false;
    private $all_ok = false;
    
    protected function beforeValidate()
    {
        if(!empty($this->attr1) && $this->attr2) // if the fields you are looking for are filled, let it go to next
        {
            $this->step1 = true;
        }
    
        if($this->step1)
        {
            ... some more validation
            $this->step2 = true;
        }
    
        if($this->step2)
        {
            ... if all your logic meets
            $this->all_ok = true;
        }
    
         // if all fields that your looking for are filled, let parent validate them all
         // if they don't go with their original rules, parent will notify
        if($this->all_ok)
            return parent::beforeValidate();
    
        $this->addError($this->tableSchema->primaryKey, 'please fillout the form correctly');
        return false;
    }
    

    【讨论】:

      【解决方案4】:

      我认为最好为验证的每个步骤创建特定的类,并使用带有规则的场景。下面是一个小例子。

      //protected/extensions/validators
      class StepOneMyModelValidator extends CValidator
      {
      
          /**
           * @inheritdoc
           */
          protected function validateAttribute($object, $attribute)
          {
              /* @var $object YourModel */
              // validation step 1 here.
              if (exist_problems) {
                  $object->addError($attribute, 'step1 is failed');
              }
      ...
      

      创建其他类(步骤)进行验证...

      // in your model
          public function rules()
          {
              return array(
                 array('attr', 'ext.validators.StepOneMyModelValidator', 'on' => 'step1'),
              ...
      

      如何在控制器中使用:

      $model = new Listing();
      $steps = array('step1', 'step2', /* etc... */);
      foreach($_POST as $key => $val) {
      if (in_array($key, $steps)) {
             $model->setScenario($key);
             break;
         }
      }
      $model->validate();
      echo '<pre>';
      print_r($model->getErrors());
      echo '</pre>';
      die();
      

      或者我们可以在一个验证器中验证所有步骤。

      【讨论】:

        猜你喜欢
        • 2013-05-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多