Commit 11132cee by Qiang Xue

Fixes #5006.

parent b9b6fbc9
...@@ -194,7 +194,6 @@ Yii Framework 2 Change Log ...@@ -194,7 +194,6 @@ Yii Framework 2 Change Log
- Enh #4436: Added callback functions to AJAX-based form validation (thiagotalma) - Enh #4436: Added callback functions to AJAX-based form validation (thiagotalma)
- Enh #4485: Added support for deferred validation in `ActiveForm` (Alex-Code) - Enh #4485: Added support for deferred validation in `ActiveForm` (Alex-Code)
- Enh #4520: Added sasl support to `yii\caching\MemCache` (xjflyttp) - Enh #4520: Added sasl support to `yii\caching\MemCache` (xjflyttp)
- Enh #4559: Added `beforeValidateAll` and `afterValidateAll` callbacks to `ActiveForm` (Alex-Code)
- Enh #4566: Added client validation support for image validator (Skysplit, qiangxue) - Enh #4566: Added client validation support for image validator (Skysplit, qiangxue)
- Enh #4581: Added ability to disable url encoding in `UrlRule` (tadaszelvys) - Enh #4581: Added ability to disable url encoding in `UrlRule` (tadaszelvys)
- Enh #4602: Added $key param in ActionColumn buttons Closure call (disem) - Enh #4602: Added $key param in ActionColumn buttons Closure call (disem)
...@@ -269,7 +268,7 @@ Yii Framework 2 Change Log ...@@ -269,7 +268,7 @@ Yii Framework 2 Change Log
- Chg #4911: Changed callback signature used in `yii\base\ArrayableTrait::fields()` from `function ($field, $model) {` to `function ($model, $field) {` (samdark) - Chg #4911: Changed callback signature used in `yii\base\ArrayableTrait::fields()` from `function ($field, $model) {` to `function ($model, $field) {` (samdark)
- Chg #4955: Replaced callbacks with events for `ActiveForm` (qiangxue) - Chg #4955: Replaced callbacks with events for `ActiveForm` (qiangxue)
- Removed `beforeValidate()`, `beforeValidateAll()`, `afterValidate()`, `afterValidateAll()`, `ajaxBeforeSend()` and `ajaxComplete()` from `ActiveForm`. - Removed `beforeValidate()`, `beforeValidateAll()`, `afterValidate()`, `afterValidateAll()`, `ajaxBeforeSend()` and `ajaxComplete()` from `ActiveForm`.
- Added `beforeValidate`, `afterValidate`, `beforeSubmit`, `ajaxBeforeSend` and `ajaxComplete` events to `yii.activeForm`. - Added `beforeValidate`, `afterValidate`, `beforeValidateAttribute`, `afterValidateAttribute`, `beforeSubmit`, `ajaxBeforeSend` and `ajaxComplete` events to `yii.activeForm` jQuery plugin.
- Chg: Replaced `clearAll()` and `clearAllAssignments()` in `yii\rbac\ManagerInterface` with `removeAll()`, `removeAllRoles()`, `removeAllPermissions()`, `removeAllRules()` and `removeAllAssignments()` (qiangxue) - Chg: Replaced `clearAll()` and `clearAllAssignments()` in `yii\rbac\ManagerInterface` with `removeAll()`, `removeAllRoles()`, `removeAllPermissions()`, `removeAllRules()` and `removeAllAssignments()` (qiangxue)
- Chg: Added `$user` as the first parameter of `yii\rbac\Rule::execute()` (qiangxue) - Chg: Added `$user` as the first parameter of `yii\rbac\Rule::execute()` (qiangxue)
- Chg: `yii\grid\DataColumn::getDataCellValue()` visibility is now `public` to allow accessing the value from a GridView directly (cebe) - Chg: `yii\grid\DataColumn::getDataCellValue()` visibility is now `public` to allow accessing the value from a GridView directly (cebe)
......
...@@ -245,18 +245,23 @@ new ones save the following code as `convert.php` that should be placed in the s ...@@ -245,18 +245,23 @@ new ones save the following code as `convert.php` that should be placed in the s
You can prefix a date format with `php:` to use the old format of the PHP `date()`-function. You can prefix a date format with `php:` to use the old format of the PHP `date()`-function.
* `beforeValidate()`, `beforeValidateAll()`, `afterValidate()`, `afterValidateAll()`, `ajaxBeforeSend()` and `ajaxComplete()` * `beforeValidate()`, `beforeValidateAll()`, `afterValidate()`, `afterValidateAll()`, `ajaxBeforeSend()` and `ajaxComplete()`
are removed from `ActiveForm`. The same functionality is now achieved via JavaScript event mechanism. For example, are removed from `ActiveForm`. The same functionality is now achieved via JavaScript event mechanism like the following:
if you want to do something before performing validation on the client side, you can write the following
JavaScript code:
```js ```js
$('#myform').on('beforeValidate', function (event, messages, deferreds, attribute) { $('#myform').on('beforeValidate', function (event, messages, deferreds) {
if (attribute === undefined) { // called when the validation is triggered by submitting the form
// the event is triggered when submitting the form // return false if you want to cancel the validation for the whole form
} elseif (attribute.id === 'something') { }).on('beforeValidateAttribute', function (event, attribute, messages, deferreds) {
// the event is triggered before validating "something" // before validating an attribute
} // return false if you want to cancel the validation for the attribute
// if you want to cancel the validation, return a boolean false. }).on('afterValidateAttribute', function (event, attribute, messages) {
// ...
}).on('afterValidate', function (event, messages) {
// ...
}).on('beforeSubmit', function () {
// after all validations have passed
// you can do ajax form submission here
// return false if you want to stop form submission
}); });
``` ```
...@@ -24,44 +24,60 @@ ...@@ -24,44 +24,60 @@
var events = { var events = {
/** /**
* beforeValidate event is triggered before validating the whole form and each attribute. * beforeValidate event is triggered before validating the whole form.
* The signature of the event handler should be: * The signature of the event handler should be:
* function (event, messages, deferreds, attribute) * function (event, messages, deferreds)
* where * where
* - event: an Event object. * - event: an Event object.
* - messages: error messages. When attribute is undefined, this parameter is an associative array * - messages: an associative array with keys being attribute IDs and values being error message arrays
* with keys being attribute IDs and values being error messages for the corresponding attributes. * for the corresponding attributes.
* When attribute is given, this parameter is an array of the error messages for that attribute.
* - deferreds: an array of Deferred objects. You can use deferreds.add(callback) to add a new deferred validation. * - deferreds: an array of Deferred objects. You can use deferreds.add(callback) to add a new deferred validation.
* - attribute: an attribute object. Please refer to attributeDefaults for the structure.
* If this is undefined, it means the event is triggered before validating the whole form.
* Otherwise it means the event is triggered before validating the specified attribute.
* *
* If the handler returns a boolean false, it will stop further validation after this event. * If the handler returns a boolean false, it will stop further form validation after this event. And as
* a result, afterValidate event will not be triggered.
*/ */
beforeValidate: 'beforeValidate', beforeValidate: 'beforeValidate',
/** /**
* afterValidate event is triggered after validating the whole form and each attribute. * afterValidate event is triggered after validating the whole form.
* The signature of the event handler should be: * The signature of the event handler should be:
* function (event, messages, attribute) * function (event, messages)
* where * where
* - event: an Event object. * - event: an Event object.
* - messages: error messages. When attribute is undefined, this parameter is an associative array * - messages: an associative array with keys being attribute IDs and values being error message arrays
* with keys being attribute IDs and values being error messages for the corresponding attributes. * for the corresponding attributes.
* When attribute is given, this parameter is an array of the error messages for that attribute.
* If the array length is greater than 0, it means the attribute has validation errors.
* - attribute: an attribute object. Please refer to attributeDefaults for the structure.
* If this is undefined, it means the event is triggered before validating the whole form.
* Otherwise it means the event is triggered before validating the specified attribute.
*/ */
afterValidate: 'afterValidate', afterValidate: 'afterValidate',
/** /**
* beforeSubmit event is triggered before submitting the form (after all validations pass). * beforeValidateAttribute event is triggered before validating an attribute.
* The signature of the event handler should be:
* function (event, attribute, messages, deferreds)
* where
* - event: an Event object.
* - attribute: the attribute to be validated. Please refer to attributeDefaults for the structure of this parameter.
* - messages: an array to which you can add validation error messages for the specified attribute.
* - deferreds: an array of Deferred objects. You can use deferreds.add(callback) to add a new deferred validation.
*
* If the handler returns a boolean false, it will stop further validation of the specified attribute.
* And as a result, afterValidateAttribute event will not be triggered.
*/
beforeValidateAttribute: 'beforeValidateAttribute',
/**
* afterValidateAttribute event is triggered after validating the whole form and each attribute.
* The signature of the event handler should be:
* function (event, attribute, messages)
* where
* - event: an Event object.
* - attribute: the attribute being validated. Please refer to attributeDefaults for the structure of this parameter.
* - messages: an array to which you can add additional validation error messages for the specified attribute.
*/
afterValidateAttribute: 'afterValidateAttribute',
/**
* beforeSubmit event is triggered before submitting the form after all validations have passed.
* The signature of the event handler should be: * The signature of the event handler should be:
* function (event) * function (event)
* where event is an Event object. * where event is an Event object.
* *
* If the handler returns a boolean false, it will stop further validation after this event. * If the handler returns a boolean false, it will stop form submission.
*/ */
beforeSubmit: 'beforeSubmit', beforeSubmit: 'beforeSubmit',
/** /**
...@@ -136,6 +152,8 @@ ...@@ -136,6 +152,8 @@
validate: undefined, validate: undefined,
// status of the input field, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating // status of the input field, 0: empty, not entered before, 1: validated, 2: pending validation, 3: validating
status: 0, status: 0,
// whether the validation is cancelled by beforeValidateAttribute event handler
cancelled: false,
// the value of the input // the value of the input
value: undefined value: undefined
}; };
...@@ -251,6 +269,7 @@ ...@@ -251,6 +269,7 @@
// client-side validation // client-side validation
$.each(data.attributes, function () { $.each(data.attributes, function () {
this.cancelled = false;
// perform validation only if the form is being submitted or if an attribute is pending validation // 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) { if (data.submitting || this.status === 2 || this.status === 3) {
var msg = messages[this.id]; var msg = messages[this.id];
...@@ -258,8 +277,8 @@ ...@@ -258,8 +277,8 @@
msg = []; msg = [];
messages[this.id] = msg; messages[this.id] = msg;
} }
var event = $.Event(events.beforeValidate); var event = $.Event(events.beforeValidateAttribute);
$form.trigger(event, [msg, deferreds, this]); $form.trigger(event, [this, msg, deferreds]);
if (event.result !== false) { if (event.result !== false) {
if (this.validate) { if (this.validate) {
this.validate(this, getValue($form, this), msg, deferreds); this.validate(this, getValue($form, this), msg, deferreds);
...@@ -267,6 +286,8 @@ ...@@ -267,6 +286,8 @@
if (this.enableAjaxValidation) { if (this.enableAjaxValidation) {
needAjaxValidation = true; needAjaxValidation = true;
} }
} else {
this.cancelled = true;
} }
} }
}); });
...@@ -302,7 +323,7 @@ ...@@ -302,7 +323,7 @@
success: function (msgs) { success: function (msgs) {
if (msgs !== null && typeof msgs === 'object') { if (msgs !== null && typeof msgs === 'object') {
$.each(data.attributes, function () { $.each(data.attributes, function () {
if (!this.enableAjaxValidation) { if (!this.enableAjaxValidation || this.cancelled) {
delete msgs[this.id]; delete msgs[this.id];
} }
}); });
...@@ -457,7 +478,7 @@ ...@@ -457,7 +478,7 @@
if (data.submitting) { if (data.submitting) {
var errorInputs = []; var errorInputs = [];
$.each(data.attributes, function () { $.each(data.attributes, function () {
if (updateInput($form, this, messages)) { if (!this.cancelled && updateInput($form, this, messages)) {
errorInputs.push(this.input); errorInputs.push(this.input);
} }
}); });
...@@ -486,7 +507,7 @@ ...@@ -486,7 +507,7 @@
} }
} else { } else {
$.each(data.attributes, function () { $.each(data.attributes, function () {
if (this.status === 2 || this.status === 3) { if (!this.cancelled && (this.status === 2 || this.status === 3)) {
updateInput($form, this, messages); updateInput($form, this, messages);
} }
}); });
...@@ -508,7 +529,7 @@ ...@@ -508,7 +529,7 @@
if (!$.isArray(messages[attribute.id])) { if (!$.isArray(messages[attribute.id])) {
messages[attribute.id] = []; messages[attribute.id] = [];
} }
$form.trigger(events.afterValidate, [messages[attribute.id], attribute]); $form.trigger(events.afterValidateAttribute, [attribute, messages[attribute.id]]);
attribute.status = 1; attribute.status = 1;
if ($input.length) { if ($input.length) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment