[EXTENSION] Javascript form Validation

I once asked if the extension could be modified in order to limit the error messages shown at any one time. Here is a quick and dirty solution I just found.

In EJFValidate.php change


private $_jqueryPluginFile = 'jquery.validate.min.js';

to


private $_jqueryPluginFile = 'jquery.validate.js';

And in jquery.validate.js change


for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {

  this.check( elements[i] );

}

to


var countErrors = 0;

for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i] && countErrors<3; i++ ) {

  countErrors += !(this.check( elements[i] ));

}

.

Hi Raoul,

could you please point me to some documentation regarding the options, in particluar for placing the error message?

(I want to use inline error messages (“span”), however I do not like the error position with radiobuttons and checkboxes. With radiobuttons, the error message is placed between the first radiobutton and its label. I’d like to have it placed before the entire list of options.)

Thanks

hi mh469,

the only documentation I can think of is the one of the JQuery Validate plugin itself. It can be found here. Check the ‘Demo’ part, it contains some positioning for error messages.

Hope this helps

8)

Hey all,

I’ve ran into problems with hidden fields that I add myself - their names were shortened onSubmit (normalized names were restored). Here’s patch to leave off non-normalized hidden fields:




--- a/src/php/protected/extensions/jformvalidate/js/jquery.jfvalidate.helper.js

+++ b/src/php/protected/extensions/jformvalidate/js/jquery.jfvalidate.helper.js

@@ -21,7 +21,7 @@ $.fn.EJFValidate = {

 		if($.fn.EJFValidate.normalizedNames == true)

 		{

 			//alert('restoreName'+$.fn.EJFValidate.normalizedNames);

-			$('input[type="hidden"]').each(function(){		

+			$('input[type="hidden"][name^="' + $.fn.EJFValidate.spice + '"]').each(function(){		

 				var e = $(this);

 				e.attr('name',e.attr('name').substring($.fn.EJFValidate.spice.length));

 			});	



Other thing, sometimes I want to simply change only a couple of plugin options and leave others intact. Here’s patch:




--- a/src/php/protected/extensions/jformvalidate/EJFValidate.php

+++ b/src/php/protected/extensions/jformvalidate/EJFValidate.php

@@ -120,8 +120,9 @@ class EJFValidate extends CApplicationComponent {

 	 */

 	public function setOptions($opt){

 		if( count($opt))

-			$this->_pluginOptions = $opt;

+			$this->_pluginOptions = array_merge( $this->_pluginOptions, $opt);

 	}



… and usage (surprise surprise!:) )




				<?php EHTML::getJValidateInstance()->setOptions(array(

					'errorContainer' => 'div#userErrorSummary',

					'errorLabelContainer' => 'div#userErrorSummary ul',

				)); ?>



Hi mindeh;

good job !! I will include both patches in the next release.

Thanks for contributing ;)

B)

Greetings,

Great Extension!!!

I was able to port the validation code I had from another non-Yii app and get things going quick.

However,

I am trying to figure out how to create a cancel button that will submit the form but skip/bypass the validation. I would like to use the controller to handle which button is clicked.

I found that the jquery.validate plugin will filter buttons that have the class "cancel" and change validator.cancelSubmit to true, but adding the class to my cancel button seems to only stop the submit and validation keeping my in the form.

I may be off base with how I am trying to accomplish canceling out of a form.

Any suggestions welcome,

Oorastard

Hi oorastard,

glad you appreciate the plugin ;)

I’ve been doing some tests on your scenario and I think I’ve done what you want. I’m not a JQuery expert, and in fact, you did half of the job when you deciced to use this ‘cancel’ class. As you said, it does cancel JS validation but doesn’t submit the form, so what I did is simply add a submitHandler function to the Validate Plugin options, and manually submit the form.

Here is the result :




<div class="form">

<?php	

$submitHandler=<<<END

function(form){

	// explicitly submit the form !

	form.submit();

}

END;

	echo EHtml::beginForm(); 

	EHtml::setOptions(array(

		'errorContainer'	=> 'div.errorSummary',

		'wrapper' 		=> 'li',

		'errorLabelContainer' 	=> 'div.errorSummary ul',

		'errorClass' 		=> 'invalid',

		'submitHandler' 	=> $submitHandler

	));

?>

<?php echo EHtml::errorSummary($model); ?>

<div class="errorSummary" style="display:none">

	<p>Please fix the following input errors:</p>

	<ul/>

</div>

<div class="row">

	<?php echo EHtml::activeLabel($model,'username'); ?>

	<?php echo EHtml::activeTextField($model,'username') ?>

</div>


<div class="row">

	<?php echo EHtml::activeLabel($model,'password'); ?>

	<?php echo EHtml::activePasswordField($model,'password') ?>

</div>

<div class="row button">

	<?php echo EHtml::submitButton('Submit'); ?>

	<input type="submit" class="cancel" name="cancel" value="Submit (skip JS Validation)" />

</div>

<?php  echo EHtml::endForm(); ?>

</div>



See the [size=“4”]Demo form[/size] and tell me if that is what you wanted to achieve. By the way, I’m curious to know in what kind of situation it could be intresting (required ?) to provide the user with 2 submit buttons, where one would skip validation ?

Hope this helps…

ciao

B)

Raoul,

That was it! Thanks!

The cancel button can even be created using CHtml or EHtml.




<?php echo EHtml::submitButton('Submit (skip JS Validation)',array('class'=>'cancel','name'=>'cancel')); ?>



I was able to bypass the validation and submit the empty form.

I did run into one thing…

It looks like jQuery doesn’t include the clicked button name when submitting the form. The model array of fields is passed, just not the clicked button. This is the same whether the validation runs or not…

I have some testing to do. May have to use the submitHandler to capture what button is clicked and set some hidden field before submission.

Thanks,

Oorastard

Yes, you’re right about the button value that is not submitte, I’ve noticed that too … I guess that with some JS test, this can be corrected.

By the way, could you please explain me why you needed to provide a submit button that would skip validation ? I’m curious to understand possible scenario regarding form submition, and I must confess that I had never thought that a not-validating submit button could be used (in particular, together with a validating one ;) )

Thanks

B)

Raoul,

I am trying to contain all actions within the controller. I have a 2 step registration. Step 1, agree/disagree with our disclaimer, and step 2 is the registration form.

For the disclaimer page I have 2 buttons, agreeDisclaimer and disagreeDisclaimer. If agreeDisclaimer is clicked the form is displayed, and disagreeDisclaimer will redirect the user to the home page.

If the user decides to cancel during the registration form, I would like to skip validation and redirect the user to the home page.

The controller for requestAccount is an if statement:




if (isset($_POST['agreeDisclaimer'])) {

    //render registration form

} else if ((isset($_POST['disagreeDisclaimer']) || (isset($_POST['cancelReqAccount'])) {

    //redirect user to home page

} else if (isset($_POST['submitReqAccount'])) {

    //process registration form

} else {

    //render disclaimer

}



I know I can use javascript on the buttons to take the user back to the home page, but I like keeping all traffic through the controller. Not to mention I couldn’t figure out how to add an onClick script to the CHtml::button. ???

HTH,

Oorastard

ok, I see now.

In order to know which button is clicked by the user (with or without Js Validation), you could for instance add an hidden field to your form. Then, on the ‘onClick’ event, set the value for this hidden field.

This is how it could look like :




<?php echo EHtml::hiddenField('buttonClickedByUser','JsValidation',array('id'=> 'buttonClickedByUser')); ?>


<?php echo EHtml::submitButton('Submit'); ?>

<?php echo EHtml::submitButton('Submit (skip JS Validation)',array(

	'class'   => 'cancel',

	'onclick' => '$(\'#buttonClickedByUser\').val(\'skipJsValidation\')',

)); ?>



If the user clicks on the non-Js-validation submit button, the onclick handler sets the hidden field value to ‘skipJsValidation’, so on the server side, you can use the hidden field value.

On Submit, the POST array would contain :




buttonClickedByUser = 'skipJsValidation' or 'JsValidation'



Hope this helps

ciao

B)

Perfect!!!

Thank you!

GREAT WORK ON THE EXTENSION

Here is how i made this compatible with Yii-1.1 form.css

EJFValidate.php


	private $_jqueryPluginFile 		= 'jquery.validate.js';//'jquery.validate.min.js';

/config/main.php


		'jformvalidate'=>array (

			'class'=>'ext.jformvalidate.EJFValidate',

			'enable'=>true,

			'pluginOptions'=>array(

				'errorClass'=>'errorMessage',

				'errorElement'=>'div',

				'inputErrorClass'=>'error',

			),

		),

jquery.validate.js patch:


201d200

< 		inputErrorClass: "error",

213c212

< 				this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.inputErrorClass/*errorClass*/ );

---

> 				this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass );

376c375

< 			this.elements().removeClass( this.settings.inputErrorClass/*errorClass*/ );

---

> 			this.elements().removeClass( this.settings.errorClass );

584c583

< 				this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.inputErrorClass/*errorClass*/ );

---

> 				this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass );

597c596

< 					this.settings.unhighlight.call( this, elements[i], this.settings.inputErrorClass/*errorClass*/ );

---

> 					this.settings.unhighlight.call( this, elements[i], this.settings.errorClass );

Thank you.

thanks phpdevmd,

and you did a good job to by providing this patch … I will include it in a future release !

Thanks

B)

Hi Raoul,

This is great extension for Yii, I’m using this in all my project. But I have the little confuse.

  • In Yii 1.1.1, when call EHtml::setScenario(‘scenario’) in views, Yii error with message “scenario already set”, in old version Yii, it works fine. I find I can set scenario by $model->scenario = ‘scenario’, so EHtml::setScenario() can use in what case?

  • When I need to create a form with two models has individual rules, JFVFormValidate send error message "scenario already set", can plz help me

Thanks much

Hi giz,

since 1.1 you should always set the scenario at the model level, using (like you did) $model->scenario = ‘myScenario’.

You’re right, the method EHtml::setScenario() is useless and it has no other purpose than to preserve backward compatibility with older version of the extension.

Remember that you can’t change scenario between a beginForm() and a endForm() … and that’s what may cause your second error. I don’t know how you’ve contructed your form with mixed models, but the [color="#0000FF"]JFormValidate[/color] extension is not supposed to work if you mix models inside the same form : you’ll have to create 2 forms.

Now tell me, did you notice it used to work with older extension version ? or is it the first time you mix models ?

If you post the code of your view, maybe I could help better …

ciao

B)

ps: please note that I’ll be away since next Monday …so be patient for next reply ;)

[size="4"][color="#0000FF"]about CActiveForm and jformvalidate extension[/color][/size]

Rangel Reale has released a new extension that adds client-side validation features to the CActiveForm Widget instead of using jformvalidate, because as he said, jformvalidate is not compatible with CActiveForm. That’s true !

So I did some tests :

  • copy CActiveForm from the framework to the myWebApp/extensions/jformvalidate folder

  • rename it EJFActiveForm

  • replace all calls to CHtml:: with EHtml::

  • update configuration

  • call beginWidget(‘extensions.jformvalidate.EJFActiveForm’)

With some adjustments to set jformvalidate options, everything seemed to work fine ‘out of the box’ on simple forms (I’ve used the Contact form).

Ok, so next step would be to make some more tests and to release a new version of the jformvalidate extension. So why am I not doing it right now instead of posting endless messages on the forum ? hum … good question !

The thing is, I don’t really see why ! I mean, which scenario could justify to mix pure client-side validation, ajax validation and pure server-side validation ? Moreover, jformvalidate does support ajax validation rules (as shown in this example) so if some attributes need to be validated on the server side, that’s easy to achieve be defining an apropriate rule on the model.

If you need 3 form validation methods (client-side, ajax, pure server-side) you have 2 options :

  • go with jformvalidate and a ‘remote’ validation rule

  • use extension wvActiveForm

Consequently, and unless I really see strong positive points in doing so, I will not provide the EJFActiveForm class in a next release of jformvalidate.

ciao

B)

Just one thing, I never used CActiveForm’s AJAX validation, so I don’t even know how it works, or even if it is compatible at all with my extension :rolleyes:

So if someone wants to do all 3 type of validations, he will need to test the AJAX one!

I think it should be easy to convert your extension to CActiveForm, as it is mainly only forwarding class to CHtml.

Only reason I could see the use of adding the client side js validation is for high traffic sites, it would reduce server load.

Hi Raoul,

I’ve been trying to get the following dropdownlist to work with a form that uses the validation extension but it seems that the extension prevents the dropdownlist from working.

Here is the dropdownlist code:




<?php

echo CHtml::dropDownList(

  CHtml::activeId($customContentAR,'id').'_ddl', 

  $customContentAR['id'],

  $customContentDDL['data'],

  array(

    'ajax'=>array( 

      // ajaxOptions

      'type' => 'POST',

      'url' => CController::createUrl('home/getCustomContentAjax'),

      'beforeSend' => '

        function(request)

        {

          return dropDownListBeforeSend(request);

        }// function()

      ',

      'dataType'=>'json',

      'success'=>'

        function(data)

        {

          dropDownListSuccess(data);

        }// function()

      ',

      'data' => array(

        'customContentId' => "js:jQuery('#".CHtml::activeId($customContentAR,'id').'_ddl'."').val()",

      ),

    )

  )

);

?>



When this is used by itself in a view it works as expected, the ajax code submits the selection to the controller. But if I add a form validated with the extension it stops working, that is changing the selected value of the dropdownlist does not cause the ajax code to be submit the change to the controller. In fact, just adding the following causes the problem.




echo EHtml::beginForm(

  '',

  'post',

  array(

    'class'=>'customContentForm',

  )

); 

EHtml::setOptions(

  array(

    'errorContainer' => 'div.errorSummary',

    'wrapper' => 'li',

    'errorLabelContainer' => 'div.errorSummary ul',

    'errorClass' => 'invalid',

    'onkeyup' => false,

    'onfocusout' => false

  )

);    

echo EHtml::endForm();

?>




What am I doing wrong?

Thanks for you help!