Multi Step Form

Requirements:

  • A multi step ONE PAGE form (using DIVs that show/hide with jQuery)

  • Validation on each step (validate step-specific attributes only)

I have managed to implement this as follows:

View:


<div class="form">

	

	<?php $form=$this->beginWidget('CActiveForm', array(

		'id'=>'listing-form',

		'enableClientValidation'=>false,

		'enableAjaxValidation'=>true,

		'clientOptions'=>array(

			'validateOnChange'=>false,

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

JS:


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;

	

	// ajax validation

	if(isset($_POST['step1']))

	{

		$attributes = array('name', 'address1', 'etc');

			

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

	}

		

	// ajax validation

	if(isset($_POST['step2']))

	{

		$attributes = array('category', 'type', 'etc');

		

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

	}

		

	// ajax validation

	if(isset($_POST['step3']))

	{

		$attributes = array('details', 'source', 'etc');

			

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

	}

		

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

	}

}

This works well, except it won’t perform validation if you set ‘validateOnChange’ to true. Also I was wondering whether this is actually the best way to do this, or if anyone knows of a better way?

Thanks.

You’d better go with separate forms for each step and have separate model instances(same class) for them with separate scenarios. This will ease your job a lot.

I don’t know how client side validation will behave in this case having in mind that it will produce only #YourModel* identifiers for the javascript validation engine.

If that’s a problem, you can even go further and have a MyModelStepOne extends MyModel, MyModelStepTwo extends MyModel. This will generate #MyModelStepOne* and #MyModelStepTwo* identifiers and you will have the finest control you could ask over your forms.

Separate forms would of course be a lot easier, but it lacks the user experience (in terms of speed / transition effects) that is possible with one form spread over multiple blocks.

The code I posted works well but is achieved in a “hacky” kind of way - I’m sure there is a better way to go about it. Anyone?

Anybody got any ideas? Thanks :)

Probably you have misunderstood the suggestion of twisted1919.

He suggested a separated form with a separated model for each step, but in a single page. It won’t harm the user experience.

How would you then gather all the data in the final submit for massive assignment? Since all form attributes will have a different model name so simply doing:


if(isset($_POST['Listing']))

	$model->attributes = $_POST['Listing'];

Isn’t going to work.

Ah, yes. You are right. Massive assignment won’t work.

You have to populate the model for saving manually from the 3 models for the forms.

I have signup form on modal(lightbox) with 3 steps.

How signup form will render if ajax validation fails?

CJuiAccordion. I have some a tutorial on how to make a multi step form using it.

Hi

Sorry to bring up this old post. May I know can this is be implemented on Yii 2?

Has anyone done multistep form in yii2?

Please help.

I am having the same question, does anyone had ever achieved this kindly help.