Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yii 1.1 - creating a multi step form with validation

Tags:

yii

I'm basically trying to create a multi-step form using the CActiveForm class in Yii. The idea is I want to use the built-in functionality to achieve this in the simplest way possible. The requirement I have is as follows:

  • A multi step ONE PAGE form (using DIVs that show/hide with jQuery)
  • AJAX validation on EACH step (validate step-specific attributes only)
  • The validation MUST work using the validateOnChange() and validateOnSubmit() methods

This is a half-working solution I have developed so far:

View:

<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
            }
        }
}

Controller:

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();
    }
}

To summarise. Basically what I have is a form with 3 submit buttons (one for each step). In my controller I check which submit button was pressed and I run AJAX validation for the attributes specific to that step.

I use a custom afterValidate() function to show/hide the steps upon submit. On step 3, the default form submit is triggered, which posts all the form attributes to the controller.

This works well, except it won't work with validateOnChange() (since the submit button doesn't get posted). Also I was wondering whether this is actually the best way to do this, or if anyone knows of a better way?

Thanks.

like image 202
MAX POWER Avatar asked Jan 06 '14 22:01

MAX POWER


1 Answers

I'd suggesting using scenarios to turn on and off the appropriate rules. Adjust the model scenario based on what is sent to your controller.

Note: this may also be a really good place to use a CFormModel instead of a CActiveRecord, depending on what is in your form.

Edit: can you add a hidden field to each div section that contains the info about what step you are on? Seems like that should work instead of your submit buttons.

like image 111
acorncom Avatar answered Oct 29 '22 20:10

acorncom