mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 06:15:19 +08:00
Fixes #3410: yii.activeForm.js now supports adding/removing fields dynamically
This commit is contained in:
@ -133,6 +133,7 @@ Yii Framework 2 Change Log
|
||||
- Enh #3380: Allow `value` in `defaultValueValidator` to be a closure (Alex-Code)
|
||||
- Enh #3384: Added callback-style transactions (leandrogehlen, Ragazzo, samdark)
|
||||
- Enh #3399, #3241: Added support for MS SQL Server older than 2012 (fourteenmeister, samdark)
|
||||
- Enh #3410: yii.activeForm.js now supports adding/removing fields dynamically (qiangxue)
|
||||
- Enh #3459: Added logging of errors, which may occur at `yii\caching\FileCache::gc()` (klimov-paul)
|
||||
- Enh #3472: Added configurable option to encode spaces in dropDownLists and listBoxes (kartik-v)
|
||||
- Enh #3518: `yii\helpers\Html::encode()` now replaces invalid code sequences with "<22>" (DaSourcerer)
|
||||
|
@ -22,19 +22,24 @@
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: If you change any of these defaults, make sure you update yii\widgets\ActiveForm::getClientOptions() as well
|
||||
var defaults = {
|
||||
// whether to encode the error summary
|
||||
encodeErrorSummary: true,
|
||||
// the jQuery selector for the error summary
|
||||
errorSummary: undefined,
|
||||
errorSummary: '.error-summary',
|
||||
// whether to perform validation before submitting the form.
|
||||
validateOnSubmit: true,
|
||||
// the container CSS class representing the corresponding attribute has validation error
|
||||
errorCssClass: 'error',
|
||||
errorCssClass: 'has-error',
|
||||
// the container CSS class representing the corresponding attribute passes validation
|
||||
successCssClass: 'success',
|
||||
successCssClass: 'has-success',
|
||||
// the container CSS class representing the corresponding attribute is being validated
|
||||
validatingCssClass: 'validating',
|
||||
// the GET parameter name indicating an AJAX-based validation
|
||||
ajaxParam: 'ajax',
|
||||
// the type of data that you're expecting back from the server
|
||||
ajaxDataType: 'json',
|
||||
// the URL for performing AJAX-based validation. If not set, it will use the the form's action
|
||||
validationUrl: undefined,
|
||||
// a callback that is called before submitting the form. The signature of the callback should be:
|
||||
@ -57,13 +62,10 @@
|
||||
ajaxBeforeSend: undefined,
|
||||
// a function to be called when the request finishes on AJAX-based validation. The signature of the callback should be:
|
||||
// function ($form, jqXHR, textStatus)
|
||||
ajaxComplete: undefined,
|
||||
// the GET parameter name indicating an AJAX-based validation
|
||||
ajaxParam: 'ajax',
|
||||
// the type of data that you're expecting back from the server
|
||||
ajaxDataType: 'json'
|
||||
ajaxComplete: undefined
|
||||
};
|
||||
|
||||
// NOTE: If you change any of these defaults, make sure you update yii\widgets\ActiveField::getClientOptions() as well
|
||||
var attributeDefaults = {
|
||||
// a unique ID identifying an attribute (e.g. "loginform-username") in a form
|
||||
id: undefined,
|
||||
@ -71,16 +73,16 @@
|
||||
name: undefined,
|
||||
// the jQuery selector of the container of the input field
|
||||
container: undefined,
|
||||
// the jQuery selector of the input field
|
||||
// the jQuery selector of the input field under the context of the container
|
||||
input: undefined,
|
||||
// the jQuery selector of the error tag
|
||||
error: undefined,
|
||||
// the jQuery selector of the error tag under the context of the container
|
||||
error: '.help-block',
|
||||
// whether to encode the error
|
||||
encodeError: true,
|
||||
// whether to perform validation when a change is detected on the input
|
||||
validateOnChange: false,
|
||||
validateOnChange: true,
|
||||
// whether to perform validation when the input loses focus
|
||||
validateOnBlur: false,
|
||||
validateOnBlur: true,
|
||||
// whether to perform validation when the user is typing.
|
||||
validateOnType: false,
|
||||
// number of milliseconds that the validation should be delayed when a user is typing in the input field.
|
||||
@ -107,9 +109,12 @@
|
||||
if (settings.validationUrl === undefined) {
|
||||
settings.validationUrl = $form.prop('action');
|
||||
}
|
||||
|
||||
$.each(attributes, function (i) {
|
||||
attributes[i] = $.extend({value: getValue($form, this)}, attributeDefaults, this);
|
||||
watchAttribute($form, attributes[i]);
|
||||
});
|
||||
|
||||
$form.data('yiiActiveForm', {
|
||||
settings: settings,
|
||||
attributes: attributes,
|
||||
@ -117,8 +122,6 @@
|
||||
validated: false
|
||||
});
|
||||
|
||||
watchAttributes($form, attributes);
|
||||
|
||||
/**
|
||||
* Clean up error status when the form is reset.
|
||||
* Note that $form.on('reset', ...) does work because the "reset" event does not bubble on IE.
|
||||
@ -134,6 +137,47 @@
|
||||
});
|
||||
},
|
||||
|
||||
// add a new attribute to the form dynamically.
|
||||
// please refer to attributeDefaults for the structure of attribute
|
||||
add: function (attribute) {
|
||||
var $form = $(this);
|
||||
attribute = $.extend({value: getValue($form, attribute)}, attributeDefaults, attribute);
|
||||
$form.data('yiiActiveForm').attributes.push(attribute);
|
||||
watchAttribute($form, attribute);
|
||||
},
|
||||
|
||||
// remove the attribute with the specified ID from the form
|
||||
remove: function (id) {
|
||||
var $form = $(this),
|
||||
attributes = $form.data('yiiActiveForm').attributes,
|
||||
index = -1,
|
||||
attribute;
|
||||
$.each(attributes, function (i) {
|
||||
if (attributes[i]['id'] == id) {
|
||||
index = i;
|
||||
attribute = attributes[i];
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (index >= 0) {
|
||||
attributes.splice(index, 1);
|
||||
unwatchAttribute($form, attribute);
|
||||
}
|
||||
return attribute;
|
||||
},
|
||||
|
||||
// find an attribute config based on the specified attribute ID
|
||||
find: function (id) {
|
||||
var attributes = $(this).data('yiiActiveForm').attributes, result;
|
||||
$.each(attributes, function (i) {
|
||||
if (attributes[i]['id'] == id) {
|
||||
result = attributes[i];
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
return this.each(function () {
|
||||
$(this).unbind('.yiiActiveForm');
|
||||
@ -256,6 +300,33 @@
|
||||
});
|
||||
};
|
||||
|
||||
var watchAttribute = function ($form, attribute) {
|
||||
var $input = findInput($form, attribute);
|
||||
if (attribute.validateOnChange) {
|
||||
$input.on('change.yiiActiveForm',function () {
|
||||
validateAttribute($form, attribute, false);
|
||||
});
|
||||
}
|
||||
if (attribute.validateOnBlur) {
|
||||
$input.on('blur.yiiActiveForm', function () {
|
||||
if (attribute.status == 0 || attribute.status == 1) {
|
||||
validateAttribute($form, attribute, !attribute.status);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (attribute.validateOnType) {
|
||||
$input.on('keyup.yiiActiveForm', function () {
|
||||
if (attribute.value !== getValue($form, attribute)) {
|
||||
validateAttribute($form, attribute, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var unwatchAttribute = function ($form, attribute) {
|
||||
findInput($form, attribute).off('.yiiActiveForm');
|
||||
};
|
||||
|
||||
var validateAttribute = function ($form, attribute, forceValidate) {
|
||||
var data = $form.data('yiiActiveForm');
|
||||
|
||||
|
@ -693,9 +693,9 @@ class ActiveField extends Component
|
||||
return [];
|
||||
}
|
||||
|
||||
$options = [];
|
||||
|
||||
$enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation;
|
||||
$enableAjaxValidation = $this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation;
|
||||
|
||||
if ($enableClientValidation) {
|
||||
$validators = [];
|
||||
foreach ($this->model->getActiveValidators($attribute) as $validator) {
|
||||
@ -708,39 +708,48 @@ class ActiveField extends Component
|
||||
$validators[] = $js;
|
||||
}
|
||||
}
|
||||
if (!empty($validators)) {
|
||||
$options['validate'] = new JsExpression("function (attribute, value, messages, deferred) {" . implode('', $validators) . '}');
|
||||
}
|
||||
}
|
||||
|
||||
$enableAjaxValidation = $this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation;
|
||||
if ($enableAjaxValidation) {
|
||||
$options['enableAjaxValidation'] = 1;
|
||||
}
|
||||
|
||||
if ($enableClientValidation && !empty($options['validate']) || $enableAjaxValidation) {
|
||||
$inputID = Html::getInputId($this->model, $this->attribute);
|
||||
$options['id'] = $inputID;
|
||||
$options['name'] = $this->attribute;
|
||||
foreach (['validateOnChange', 'validateOnBlur', 'validateOnType', 'validationDelay'] as $name) {
|
||||
$options[$name] = $this->$name === null ? $this->form->$name : $this->$name;
|
||||
}
|
||||
|
||||
$options['container'] = isset($this->selectors['container']) ? $this->selectors['container'] : ".field-$inputID";
|
||||
$options['input'] = isset($this->selectors['input']) ? $this->selectors['input'] : "#$inputID";
|
||||
if (isset($this->selectors['error'])) {
|
||||
$options['error'] = $this->selectors['error'];
|
||||
} elseif (isset($this->errorOptions['class'])) {
|
||||
$options['error'] = '.' . implode('.', preg_split('/\s+/', $this->errorOptions['class'], -1, PREG_SPLIT_NO_EMPTY));
|
||||
} else {
|
||||
$options['error'] = isset($this->errorOptions['tag']) ? $this->errorOptions['tag'] : 'span';
|
||||
}
|
||||
|
||||
$options['encodeError'] = !isset($this->errorOptions['encode']) || $this->errorOptions['encode'] !== false;
|
||||
|
||||
return $options;
|
||||
} else {
|
||||
if (!$enableAjaxValidation && (!$enableClientValidation || empty($validators))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$options = [];
|
||||
|
||||
$inputID = Html::getInputId($this->model, $this->attribute);
|
||||
$options['id'] = $inputID;
|
||||
$options['name'] = $this->attribute;
|
||||
|
||||
$options['container'] = isset($this->selectors['container']) ? $this->selectors['container'] : ".field-$inputID";
|
||||
$options['input'] = isset($this->selectors['input']) ? $this->selectors['input'] : "#$inputID";
|
||||
if (isset($this->selectors['error'])) {
|
||||
$options['error'] = $this->selectors['error'];
|
||||
} elseif (isset($this->errorOptions['class'])) {
|
||||
$options['error'] = '.' . implode('.', preg_split('/\s+/', $this->errorOptions['class'], -1, PREG_SPLIT_NO_EMPTY));
|
||||
} else {
|
||||
$options['error'] = isset($this->errorOptions['tag']) ? $this->errorOptions['tag'] : 'span';
|
||||
}
|
||||
|
||||
$options['encodeError'] = !isset($this->errorOptions['encode']) || !$this->errorOptions['encode'];
|
||||
if ($enableAjaxValidation) {
|
||||
$options['enableAjaxValidation'] = true;
|
||||
}
|
||||
foreach (['validateOnChange', 'validateOnBlur', 'validateOnType', 'validationDelay'] as $name) {
|
||||
$options[$name] = $this->$name === null ? $this->form->$name : $this->$name;
|
||||
}
|
||||
|
||||
if (!empty($validators)) {
|
||||
$options['validate'] = new JsExpression("function (attribute, value, messages, deferred) {" . implode('', $validators) . '}');
|
||||
}
|
||||
|
||||
// only get the options that are different from the default ones (set in yii.activeForm.js)
|
||||
return array_diff_assoc($options, [
|
||||
'validateOnChange' => true,
|
||||
'validateOnBlur' => true,
|
||||
'validateOnType' => false,
|
||||
'validationDelay' => 200,
|
||||
'encodeError' => true,
|
||||
'error' => '.help-block',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +288,17 @@ class ActiveForm extends Widget
|
||||
}
|
||||
}
|
||||
|
||||
return $options;
|
||||
// only get the options that are different from the default ones (set in yii.activeForm.js)
|
||||
return array_diff_assoc($options, [
|
||||
'encodeErrorSummary' => true,
|
||||
'errorSummary' => '.error-summary',
|
||||
'validateOnSubmit' => true,
|
||||
'errorCssClass' => 'has-error',
|
||||
'successCssClass' => 'has-success',
|
||||
'validatingCssClass' => 'validating',
|
||||
'ajaxParam' => 'ajax',
|
||||
'ajaxDataType' => 'json',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user