I have the form:
<?php $form = ActiveForm::begin([
'enableClientValidation' => false,
'enableAjaxValidation' => true,
'validationUrl' => ['validate-form-step-2'],
'validateOnBlur' => false,
'validateOnChange' => false
]) ?>
This form validates only on button click. Button click handler:
// Getting my form
var $form = $('#<?= $form->id ?>');
// Run validation
$form.yiiActiveForm('validate');
This does nothing. yiiActiveForm has method 'validate', nothing happens.
Validation runs only if I call this:
$form.yiiActiveForm('submitForm')
I tried to subscribe on event 'beforeValidate' on my form. Event triggered only on call 'submutForm'.
How to run form validation programmatically? Is it a bug?
You have it turned off:
'enableClientValidation' => false,
In order to validate via AJAX you have to submit form.
No, even if I set enableClientValidation = true it still does not work.
Chrome console:
$('#w0').yiiActiveForm('validate')
undefined
Yes, it returns nothing but it triggers validation.
If it triggers validation then handler of 'beforeValidate' event should be triggered. It does not happened.
$form.on('beforeValidate', function (event, messages, deferreds) {
console.log('IT WORKS');
});
Also there is no any validation result messages (red color highlighting and error summary updating).
Any progress here?
No.
Just ran into the same issue.
I need to trigger the form validation without actually submitting the form and
$('#my-form').yiiActiveForm('validate');
does not seem to do any validation.
I looked into the JS-code and it looks like the issue is the this.status that gets checked in the validate function:
// client-side validation
$.each(data.attributes, function () {
this.cancelled = false;
// perform validation only if the form is being submitted or if an attribute is pending validation
if (data.submitting || this.status === 2 || this.status === 3) {
and the updateInputs function:
} else {
$.each(data.attributes, function () {
if (!this.cancelled && (this.status === 2 || this.status === 3)) {
updateInput($form, this, messages);
}
});
}
Basically if a field has not been "touched" its status is still 0 and the validation just skips it.
For my needs adding this.status === 0 to both functions would solve the problem I think.
Will do a pull-request so you can decide if it's a valid solution...
As a "workaround" I'm currently using this code in my JS-function which basically sets all "fields" to dirty before validation is triggered:
var $form = $("#my-form"),
data = $form.data("yiiActiveForm");
$.each(data.attributes, function() {
this.status = 3;
});
$form.yiiActiveForm("validate");
This seems to have not been fixed yet. We need more people of the community pitching in, making pull-requests, and helping the team out. This would make their job a lot easier 馃憤 I am in the middle of a project, but I will try and make some time to dig deeper into this.
It seems that $("#w0").yiiActiveForm("validate") does not work.. at all..??!!??
However, calling $("#w0").yiiActiveForm("validateAttribute", "userprofile-gravatar_email"); directly does.
I also want to point out that @bluezed 's solution to set the status to 3, worked. I tried this first and went OMG it fixed my form validation :)
My use case:
I have a checkbox for "use gravatar" where the user can enable/disable using a Gravatar profile pic. If disabled, it will use a stock profile pic, unless they upload their own. If enabled, it will override the in-house option and replace it with their Gravatar.
It is possible that the user's Gravatar account matches a different email address than they registered on my site with. So, I make them enter in the email address. Also, for accounts, the email must be unique, but if they had more than one account, they _could_ use the same Gravatar for all of them.
So I also needed to have them enter the Gravatar email address.
Now, the checkbox for "use_gravatar" is not required, they can check/uncheck it. However, if checked, that then means that the "gravatar_email" text input must be required!
I struggled getting this to work. When I clicked the checkbox, it didn't pop up red on the email box. It would show the error only after I: clicked into the email box then clicked off of it. So I had to focus, then blur, the email box... when toggling my checkbox.. grrr
I messed with whenClient on my models rules... following guides, issues, stack overflow, google (I almost used Yahoo!!).. still not working right. It was off, and clunky..
So then I added registerJS to my view, to a jQuery onChange, prop, keyup.. tested a few. Something like this:
$this->registerJs('
jQuery( "#userprofile-use_gravatar" ).change(function() {
$("#w0").yiiActiveForm("validate");
});
jQuery( "#userprofile-gravatar_email" ).keyup(function() {
$("#w0").yiiActiveForm("validate");
});
');
So this, you would think, would make Yii re-validate the form on every key press as the user types in the email box, or every time they click my checkbox... NOPE!
I about pulled my hair out. This is something that should be so simple..
Then after I went and watched fireworks with the family, got home, and started searching again, I stumbled across this issue (luckily).
First I reset the status as mentioned:
var $form = $("#w0"),
data = $form.data("yiiActiveForm");
$.each(data.attributes, function() {
this.status = 3;
});
$form.yiiActiveForm("validate");
Wow, it worked... This means that 'validate' is flat out broken
So I removed this little hack, and tried to validate just the attribute of the other input (my email). Here is the final code (note: I still needed the whenClient in my model, or it will break again lol)
$this->registerJs('
jQuery( "#userprofile-gravatar_email" ).keyup(function() {
$("#w0").yiiActiveForm("validateAttribute", "userprofile-gravatar_email");
});
jQuery( "#userprofile-use_gravatar" ).change(function() {
$("#w0").yiiActiveForm("validateAttribute", "userprofile-gravatar_email");
});
');
Finally, it works like a charm. I click the checkbox to use gravatar emails and boom, the email box pops up red. So I fill in an email, boom it goes green. I remove the email, it goes red. I uncheck the checkbox, email input goes green because it's no longer required..
So we need to inspect the two functions, because validateAttribute is doing something that validate isn't.
final note: You probably should use validateAttribute in most cases, unless you really need to re-validate the entire form. I suspect, if you have an AJAX call on a field that it will re-run it again too. This could cause unnecessary requests that could lead to performance issues in the future. However, validate is broken and needs fixed.
After reviewing the code, maybe we shouldn't touch the validate function? Also, maybe we shouldn't set the status to 3.
attributeDefaults
// status of the input field, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating
Looks like the hack that @bluezed illustrated should set the status to 0 or 2, not 3..
Does calling resetForm before validateForm work? If so, then we could do this:
$("#w0").yiiActiveForm("resetForm");
$("#w0").yiiActiveForm("validate");
Maybe it would be best to create a revalidate helper function, that resets the status of all elements in the form and then runs validate.
My concern with looping through element in the form, is what will this do to disabled or hidden fields? Should we have it skip them when resetting the status?
Ok Ok.. I think I solved this, but I don't like it either lol.
Tucked in the docs, there is a note to refer to the Yii2 Cookbook. It shows this:
$('#contact-form').data('yiiActiveForm').submitting = true;
$('#contact-form').yiiActiveForm('validate');
I think this is maybe what we have missed? Is _this_ even correct? Why would we set the form to "submitting"? We haven't clicked submit, we are still filling out the form. Only reason I can see, is because it satisfies this check, which must happen to validate the form:
// perform validation only if the form is being submitted or if an attribute is pending validation
if (data.submitting || this.status === 2 || this.status === 3) {
I don't like it because we are not submitting the form, and it just doesn't make sense.
I tested resetForm, it removes any other validations. ie: i click checkbox and it turns green. I go to another field, and the checkbox (any any other already green/red validated field) goes back to normal. So that doesn't work.
It looks the best way is to create a helper function (revalidate), to reset the status and run validate.
On a side note, after chasing my tail.. I solved my issue. Though, I still think a revalidate function should be added.
How to use Client Side Validation to make an input required only when a checkbox is checked:
In my model:
public function rules()
{
return [
['gravatar_email', 'required', 'when' => function($model) {
return $model->use_gravatar == true;
}, 'whenClient' => "isUseGravatarChecked"
],
];
}
In my view:
<?php $this->registerJs('
function isUseGravatarChecked (attribute, value) {
return $("#userprofile-use_gravatar").prop("checked") ? true : false;
};
jQuery( "#userprofile-use_gravatar" ).change(function() {
$("#w0").yiiActiveForm("validateAttribute", "userprofile-gravatar_email");
});
'); ?>
@WadeShuler could you update from master and verify if it's fixed or not?
Yeah later tonight. I wasn't aware this was addressed. I will copy my project to a test dir and update Yii off latest instead of 2.0.9.
In any case, to re-validate only one or a few inputs on a form, it's probably best practice to use 'validateAttribute'.
I wrote a quick wiki guide on how to use client validation, from a practical real life scenario. I will link to it later when I am on my Mac.
Sent from my iPhone 6S Plus
seems to be solved
I had a problem where
I have do an jquery.load to get an Yii active form.
Then in a wizard,module on the step validator, i wanted the step validator to trigger the yii form validation.
Then if i want to trigger only the form by javascript here is the way i found to work.
wizard.get("#wizard_last_step").setValidator(function() {
#---Begin this here is the part for trigger : activate yii client validation ---
jQuery('#my-form').yiiActiveForm('init');
jQuery('#my-form').yiiActiveForm('validate',true);
jQuery('#my-form').yiiActiveForm('validate');
#---End this here is the part for trigger---
if ($('#my-form').find(".has-error").length > 0 )
{
console.log($('#my-form').find(".has-error").length );
return false;
}
else{
console.log($('#my-form').find(".has-error").length );
return true;
}
}
@scastel83 that looks like application specific issues. If you'll find it's something within Yii itself, please open a separate detailed issue. Thanks.
Hi there, if you need me to create a new issue just say but i think my problem fits in this issue too.
From the Yii cookbook if we want to perform validation on form we just need to call
$('#contact-form').yiiActiveForm('validate', true);
we assume that it should validate the whole form and send the event afterValidate because the form was validated. But that doesn't happen and its because of this lines in yii.activeForm.js
that clearly say that the event afterValidate can only be launched if we are trying to submit the form which we don't because we only want the validation itself.
Currently i have a workaround by playing with the events that looks like this
var validationHandler = function () {
var form = $('#criar-tarefa-form');
var data = form.data('yiiActiveForm');
data.submitting = true;
data.shouldSubmit = false;
form.on('afterValidate', handler);
form.on('beforeSubmit', function () {
var data = $('#criar-tarefa-form').data('yiiActiveForm');
if (!data.shouldSubmit) {
data.shouldSubmit = true;
return false;
}
});
$form.yiiActiveForm('validate', false);
};
but its dirty and unnecessary if the event could be fired after the validation ends.
To solve that problem it could be as simple as
var updateInputs = function ($form, messages, submitting) {
var data = $form.data('yiiActiveForm');
if (data === undefined) {
return false;
}
var errorAttributes = [];
$.each(data.attributes, function () {
if (!$(this.input).is(":disabled") && !this.cancelled && updateInput($form, this, messages)) {
errorAttributes.push(this);
}
});
$form.trigger(events.afterValidate, [messages, errorAttributes]);
if (submitting) {
updateSummary($form, messages);
if (errorAttributes.length) {
if (data.settings.scrollToError) {
var top = $form.find($.map(errorAttributes, function(attribute) {
return attribute.input;
}).join(',')).first().closest(':visible').offset().top - data.settings.scrollToErrorOffset;
if (top < 0) {
top = 0;
} else if (top > $(document).height()) {
top = $(document).height();
}
var wtop = $(window).scrollTop();
if (top < wtop || top > wtop + $(window).height()) {
$(window).scrollTop(top);
}
}
data.submitting = false;
} else {
data.validated = true;
if (data.submitObject) {
applyButtonOptions($form, data.submitObject);
}
$form.submit();
if (data.submitObject) {
restoreButtonOptions($form);
}
}
} else {
$.each(data.attributes, function () {
if (!this.cancelled && (this.status === 2 || this.status === 3)) {
updateInput($form, this, messages);
}
});
}
submitFinalize($form);
};
we just pass the errorAttributes setting and the form event before checking if we are submitting. because we want validation only.麓
Thanks in advance
Hey @samdark should i make a new issue of the comment i made?
Just to know what to do.
Thanks
Yes. Because this one is closed and it already was lost by me somehow.
Thanks @samdark its issue #14186
Most helpful comment
it should be fixed https://github.com/yiisoft/yii2/commit/e8bcb93507a7859f2a5115ec2179182353e9cc04