Form Validation With Ajaxsubmitbutton

Hi I would like to share a simple tip to all those who still dint get a simple straight-forward method to show validation errors of the form which has a ajaxSubmitButton.

Step 1: @ your controller action




  public function actionMyAction()

	{       

		$model=new User;             

                $this->performAjaxValidation($model);  

                

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

		{

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

                        $valid=$model->validate();            

			if($valid){

                                          

                           //do anything here

                             echo CJSON::encode(array(

                                  'status'=>'success'

                             ));

                            Yii::app()->end();

                            }

                            else{

                                $error = CActiveForm::validate($model);

                                if($error!='[]')

                                    echo $error;

                                Yii::app()->end();

                            }

               }

     

	}



Step 2: @ your view

Your form may look like this




<?php

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

	'id'=>'user-form',

	'enableAjaxValidation'=>true,

        'action'=>$this->createUrl('myController/MyAction'),

        'enableClientValidation'=>true,

     

));

   ?>

        <div class="errorMessage" id="formResult"></div>

        <div id="AjaxLoader" style="display: none"><img src="<?php echo Yii::app()->request->baseUrl; ?>/images/spinner.gif"></img></div>

	<div class="row-user-single">

		<?php echo $form->labelEx($model,'attribute1'); ?>

		<?php echo $form->passwordField($model,'attribute1',array('size'=>60,'maxlength'=>500)); ?>

		<?php echo $form->error($model,'attribute1'); ?>

	</div>


	<div class="row-user-single">

		<?php echo $form->labelEx($model,'attribute2'); ?>

		<?php echo $form->passwordField($model,'attribute2',array('size'=>60,'maxlength'=>500)); ?>

		<?php echo $form->error($model,'attribute2'); ?>

	</div>

        <div class="buttons">

		

         <?php echo CHtml::ajaxSubmitButton('Save',CHtml::normalizeUrl(array('myController/MyAction','render'=>true)),

                 array(

                     'dataType'=>'json',

                     'type'=>'post',

                     'success'=>'function(data) {

                         $("#AjaxLoader").hide();  

                        if(data.status=="success"){

                         $("#formResult").html("form submitted successfully.");

                         $("#user-form")[0].reset();

                        }

                         else{

                        $.each(data, function(key, val) {

                        $("#user-form #"+key+"_em_").text(val);                                                    

                        $("#user-form #"+key+"_em_").show();

                        });

                        }       

                    }',                    

                     'beforeSend'=>'function(){                        

                           $("#AjaxLoader").show();

                      }'

                     ),array('id'=>'mybtn','class'=>'class1 class2')); ?>

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




This is working just fine for me :)

And all the code written above are my style of writing. You may change them as required but,

there are only two things to be noted as follows:

  1. @ your controller call validate of your model and return a json object if form is invalid

  2. @ your view break this json object (OR traverse through it) and show the error messages under respective elements of the form.

Keep it simple :)

[font="Trebuchet MS"]Hi Riyaz,

Thanks a lot for your valuable code…

SivaMurugan B.[/font]

Doesn’t this line generate an error in case there are form errors ?


 if(data.status=="success"){

In case of errors you don’t have data.status

Instead you should check if the first position of your data is {

You are welcome Sivam :)

And Welcome to the Yii family :)

Nope. It is not giving any error in any case :)

Tested many times before posting it in the forum buddy :)

And in both the cases, the json data starts from "{" . So I hope we shall not rely on "{".

Thanks a lot though :)

My bad you’re totally right. In an ajax webapp, I returned a string or the json errors, so it was slightly different.

Dear Friends.

I have made an attempt to make it simple.

Kindly check this.

I am not using CHtml::ajaxSubmitButton. Instead I am using normal CHtml::submitButton.

I am making the needed ajax call in afterValidate method of yiiActiveForm.js.

The following is one example.




<div class="form">


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

	'id'=>'user-form',

	'enableAjaxValidation'=>true,

	'clientOptions'=>array(

	    'validateOnSubmit'=>true,

	    'afterValidate'=>'js:function(form,data,hasError){

			if(!hasError){

				$.ajax({

					"type":"POST",

					"url":"'.CHtml::normalizeUrl(array("test/eleven")).'",

					"data":form.serialize(),

					"success":function(data){$("#test").html(data);},

					

					});

				}

			}'

	),

)); ?>


	<p class="note">Fields with <span class="required">*</span> are required.</p>


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


	<div class="row">

		<?php echo $form->labelEx($model,'username'); ?>

		<?php echo $form->textField($model,'username',array('size'=>60,'maxlength'=>64)); ?>

		<?php echo $form->error($model,'username'); ?>

	</div>


	<div class="row">

		<?php echo $form->labelEx($model,'age'); ?>

		<?php echo $form->textField($model,'age'); ?>

		<?php echo $form->error($model,'age'); ?>

	</div>


	<div class="row">

		<?php echo $form->labelEx($model,'email'); ?>

		<?php echo $form->textField($model,'email',array('size'=>60,'maxlength'=>128)); ?>

		<?php echo $form->error($model,'email'); ?>

	</div>


	<div class="row buttons">

		<?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>

	</div>


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


</div><!-- form -->


<!-- AJAX update div element -->


<div id="test"></div>




Ajax is going to update the div element with id of "test".

"test/eleven"denotes my conrollerId and actionId in my case.

Upon submitting the form and if there is no validation error, normal submit action is discarded

and Ajax call only is being made.

Regards.

[size=2]Hi, thanks for sharing. When you need ajax and form submit together, we can directly use ajaxsubmitbutton rather than "button and writing seperate jquery ajax function". And how do you show error messages of the form straight from the model itself in your case ?[/size]

Please don’t mind I m just curious to know the info, since I could not find anything as straight forward as my post for this kind of requirement :)

Dear Friend

First I should appreciate you for sharing a very useful tip.

Regarding your apprehensions, I need to clarify with you on certain aspects of my code.




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

        'id'=>'user-form',

        'enableAjaxValidation'=>true,

        'clientOptions'=>array(

            'validateOnSubmit'=>true,

            'afterValidate'=>'js:function(form,data,hasError){

                        if(!hasError){

                                $.ajax({

                                        "type":"POST",

                                        "url":"'.CHtml::normalizeUrl(array("test/eleven")).'",

                                        "data":form.serialize(),

                                        "success":function(data){$("#test").html(data);},

                                        

                                        });

                                }

                        }'

        ),

)); ?>







'validateOnSubmit'=>true,



The above ensures that the form will not be submitted even there is a single error.




 if(!hasError)



The above also ensures that the form will not be submitted by ajax enen there is single error.

Till the submission is error free, form will display errors.

Regards.

Cool :)

Works too :)

nice solution! :lol:

really helpful !

nice thanks!

here is my practice.

Thanks a lot riyazMuhammed and Seenivasan. Very helpful tips!!!

Just a little tip, if you want to make sure you get JSON data: add dataType: "json" in jquery ajax attributes.


function(form,data,hasError){

                        if(!hasError){

                                $.ajax({

                                        "type":"POST",

                                        "url": "/yourUrl/yours/",

                                        "data":form.serialize(),

                                        "dataType":"json",

                                        "success":function(data){

                                                console.log(data); // now this is a js object

                                                // You can get the number of errors like this in modern browsers:

                                                var errors = Object.keys(data).length;

                                                if (!errors) {

                                                    // form is ready to go (unless it is already gone, depending on your controller)

                                                }

                                            },

                                        });

                                }

                        }'



Great topic! I haven’t seen anyone cover this so well. Thank you.

Nice solution!

In my case I had to change some things in the view like parsing the JSON object and clearing any error divs


$(".errorMessage").text("");$(".errorMessage").hide();

if a future ajax call was successfull.


'success'=>'function(data){

                                                $("#statusMsg").html("");

                                                var obj = $.parseJSON(data);

                                                if(obj.status == "success"){

                                                    $(".errorMessage").text("");

                                                    $(".errorMessage").hide();

                                                    $("#recentId").html(obj.id);

                                                    $("#statusMsg").html(obj.msg);

                                                    $("#statusMsg").show();

                                                    $("#statusMsg").delay(3000).fadeOut("slow");

                                                    /*$("#expenses-grid").yiiGridView("update");*/

                                                    $("#enableSubmitButton").trigger("click");

                                                }else{

                                                    $("#recentId").html(obj.id);

                                                    $("#statusMsg").html(obj.msg);

                                                    $("#statusMsg").show();

                                                    $.each(obj.errors, function(key, val) {

                                                        $("#expenses-form #"+key+"_em_").text(val);                                                    

                                                        $("#expenses-form #"+key+"_em_").show();

                                                    });

                                                }

                                             }'

I wanted to share here my solution to the problem. Here is the js function that can be called to validate any CActiveForm.




var validateYiiForm = function(form, success, failure) {

	var $form = $(form);

	var settings = $form.data('settings');

	settings.submitting = true;

	$.fn.yiiactiveform.validate($form, function (data) {

		var hasError = false;

		$.each(settings.attributes, function () {

			hasError = $.fn.yiiactiveform.updateInput(this, data, $form) || hasError;

		});

		$.fn.yiiactiveform.updateSummary($form, data);

		settings.submitting = false;

		if (hasError) {

			failure === undefined || failure(data);

		} else {

			success === undefined || success(data);

		}

	});

};



You can use it this way:




$('#submit_button_id').bind('click', function() {

  validateYiiForm(

    '#form_id', 

    function(data) {

      // the form is valid, data parameter should be empty

      $.ajax({

        // whatever code you use to submit your form

      })

    },

    function(data) {

      // failed to validate

      // validation messages are shown in appropriate divs

      // data argument contains json with validation errors 

    }

  )

});



It’s strait forward to make your own ajaxSubmitButton using this function.

Have a good time!

This is my solution, based on riyazMuhammed solution and others.

In my applications always need return messages.

In Controller:





    public function actionCreate()

    {

        $model = new User;


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

        {

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

            

            try

            {

                $error = CActiveForm::validate($model);

                

                if ($error == '[]')

                    //No errors

                    $model->save();

                else

                    //Return messages from form validation

                    echo $error;

            }

            catch (Exception $e)

            {

                //Return messages from try/catch

                echo CJSON::encode(array(

                    'status' => 'error',

                    'msg' => $e->getMessage()

                ));

            }

        }

        else

            $this->render('create', array(

                'model' => $model,

            ));

    }



In View:





//CREATE

if ($model->isNewRecord)

{

    //CONFIRM

    $js = "if (confirm('Success. Do you like insert another?')) {

        form[0].reset();

    }

    else

        //REDIRECT to INDEX

        location.href = '{$this->createUrl('user/index')}';

    ";

}

//UPDATE

else

{

    $js = "alert('Updated!');

        location.href = '{$this->createUrl('user/index')}';";

}


//BOOTSTRAP ACTIVEFORM

$form = $this->beginWidget('bootstrap.widgets.TbActiveForm', array(

    'id' => 'user-form',

    'layout' => TbHtml::FORM_LAYOUT_INLINE,

    'enableAjaxValidation' => true,

    //VERY IMPORTANT        

    'enableClientValidation' => false,

    'focus' => 'input:text:enabled:visible:first',

    'clientOptions' => array(

        'validateOnSubmit' => true,

        'validateOnChange' => false,

        'validateOnType' => false,

        //Receive and show return messages

        'afterValidate' => "js:function(form, data, hasError){

            //RECEIVE FORM VALIDATION MESSAGES

            if (hasError) {


                //DO SOMETHING IF NECESSARY

                return false;

            }

            //RECEIVE other validation messages from try/catch: database constraints, unknown messages

            else {

                //SHOW MESSAGES

                if (typeof data.status !== 'undefined')

                    alert(data.msg);

                //NO ERROR, DO SOMETHING (SHOW ABOVE CONDITIONS)

                else {

                    " . $js . "

                }

            }

        }"

    ),

));




In this way CActiveForm do everything. I don’t need ajax submit buttons and parse the JSON object to show error messages.

[]'s

do this like

                                                                   var obj = &#036;.parseJSON(data);


                                                              //alert(obj.registerUser_username);


                                                            &#036;.each(obj, function(key, val) {


                                                                 //alert(key);


                                                                &#036;(&quot;#&quot;+key+&quot;_em_&quot;).text(val);


                                                                &#036;(&quot;#&quot;+key+&quot;_em_&quot;).show();


                                                                //alert(data);


                                                            });

then display errors on your modal