Commit 91002172 by Qiang Xue

Finished ActiveForm.

parent 9fc4c380
...@@ -35,14 +35,12 @@ ...@@ -35,14 +35,12 @@
validatingCssClass: 'validating', validatingCssClass: 'validating',
// the URL for performing AJAX-based validation. If not set, it will use the the form's action // the URL for performing AJAX-based validation. If not set, it will use the the form's action
validationUrl: undefined, validationUrl: undefined,
// a callback that is called before validating every attribute // a callback that is called before submitting the form. The signature of the callback should be:
beforeValidateAttribute: undefined, // function ($form) { ...return false to cancel submission...}
// a callback that is called after validating every attribute beforeSubmit: undefined,
afterValidateAttribute: undefined, // a callback that is called before validating each attribute. The signature of the callback should be:
// a callback that is called before validating ALL attributes when submitting the form // function ($form, attribute, messages) { ...return false to cancel the validation...}
beforeValidate: undefined, beforeValidate: undefined,
// a callback that is called after validating ALL attributes when submitting the form
afterValidate: undefined,
// the GET parameter name indicating an AJAX-based validation // the GET parameter name indicating an AJAX-based validation
ajaxVar: 'ajax' ajaxVar: 'ajax'
}; };
...@@ -66,10 +64,6 @@ ...@@ -66,10 +64,6 @@
enableAjaxValidation: false, enableAjaxValidation: false,
// function (attribute, value, messages), the client-side validation function. // function (attribute, value, messages), the client-side validation function.
validate: undefined, validate: undefined,
// callback called before validating the attribute
beforeValidate: undefined,
// callback called after validating an attribute.
afterValidate: 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,
// the value of the input // the value of the input
...@@ -122,6 +116,10 @@ ...@@ -122,6 +116,10 @@
}); });
}, },
options: function() {
return this.data('yiiActiveForm').settings;
},
submitForm: function () { submitForm: function () {
var $form = this, var $form = this,
data = $form.data('yiiActiveForm'); data = $form.data('yiiActiveForm');
...@@ -135,26 +133,24 @@ ...@@ -135,26 +133,24 @@
clearTimeout(data.settings.timer); clearTimeout(data.settings.timer);
} }
data.submitting = true; data.submitting = true;
if (!data.settings.beforeValidate || data.settings.beforeValidate($form)) { if (!data.settings.beforeSubmit || data.settings.beforeSubmit($form)) {
validate($form, function (messages) { validate($form, function (messages) {
var hasError = false; var hasError = false;
$.each(data.attributes, function () { $.each(data.attributes, function () {
hasError = updateInput($form, this, messages) || hasError; hasError = updateInput($form, this, messages) || hasError;
}); });
updateSummary($form, messages); updateSummary($form, messages);
if (!data.settings.afterValidate || data.settings.afterValidate($form, messages, hasError)) { if (!hasError) {
if (!hasError) { data.validated = true;
data.validated = true; var $button = data.submitObject || $form.find(':submit:first');
var $button = data.submitObject || $form.find(':submit:first'); // TODO: if the submission is caused by "change" event, it will not work
// TODO: if the submission is caused by "change" event, it will not work if ($button.length) {
if ($button.length) { $button.click();
$button.click(); } else {
} else { // no submit button in the form
// no submit button in the form $form.submit();
$form.submit();
}
return;
} }
return;
} }
data.submitting = false; data.submitting = false;
}, function () { }, function () {
...@@ -235,25 +231,20 @@ ...@@ -235,25 +231,20 @@
if (data.submitting || $form.is(':hidden')) { if (data.submitting || $form.is(':hidden')) {
return; return;
} }
if (!attribute.beforeValidate || attribute.beforeValidate($form, attribute)) { $.each(data.attributes, function () {
if (this.status === 2) {
this.status = 3;
$form.find(this.container).addClass(data.settings.validatingCssClass);
}
});
validate($form, function (messages) {
var hasError = false;
$.each(data.attributes, function () { $.each(data.attributes, function () {
if (this.status === 2) { if (this.status === 2 || this.status === 3) {
this.status = 3; hasError = updateInput($form, this, messages) || hasError;
$form.find(this.container).addClass(data.settings.validatingCssClass);
}
});
validate($form, function (messages) {
var hasError = false;
$.each(data.attributes, function () {
if (this.status === 2 || this.status === 3) {
hasError = updateInput($form, this, messages) || hasError;
}
});
if (attribute.afterValidate) {
attribute.afterValidate($form, attribute, messages, hasError);
} }
}); });
} });
}, data.settings.validationDelay); }, data.settings.validationDelay);
}; };
...@@ -271,15 +262,16 @@ ...@@ -271,15 +262,16 @@
$.each(data.attributes, function () { $.each(data.attributes, function () {
if (data.submitting || this.status === 2 || this.status === 3) { if (data.submitting || this.status === 2 || this.status === 3) {
var msg = []; var msg = [];
if (this.validate) { if (!data.settings.beforeValidate || data.settings.beforeValidate($form, this, msg)) {
this.validate(this, getValue($form, this), msg); if (this.validate) {
this.validate(this, getValue($form, this), msg);
}
if (msg.length) { if (msg.length) {
messages[this.name] = msg; messages[this.name] = msg;
} else if (this.enableAjaxValidation) {
needAjaxValidation = true;
} }
} }
if (this.enableAjaxValidation && !msg.length) {
needAjaxValidation = true;
}
} }
}); });
......
...@@ -896,6 +896,7 @@ class Html ...@@ -896,6 +896,7 @@ class Html
$attribute = static::getAttributeName($attribute); $attribute = static::getAttributeName($attribute);
$label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute)); $label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute));
$for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute); $for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute);
unset($options['label'], $options['for']);
return static::label($label, $for, $options); return static::label($label, $for, $options);
} }
......
...@@ -85,35 +85,12 @@ class ActiveField extends Component ...@@ -85,35 +85,12 @@ class ActiveField extends Component
*/ */
public $validationDelay; public $validationDelay;
/** /**
* @var JsExpression|string a [[JsExpression]] object or a JavaScript expression string representing
* the callback that will be invoked BEFORE validating the attribute associated with this field on the client side.
*
* This callback is called after [[ActiveForm::beforeValidateAttribute]].
*
* The signature of the callback should be like the following:
*
* ~~~
* ~~~
*/
public $beforeValidate;
/**
* @var JsExpression|string a [[JsExpression]] object or a JavaScript expression string representing
* the callback that will be invoked AFTER validating the attribute associated with this field on the client side.
*
* This callback is called before [[ActiveForm::afterValidateAttribute]].
*
* The signature of the callback should be like the following:
*
* ~~~
* ~~~
*/
public $afterValidate;
/**
* @var array the jQuery selectors for selecting the container, input and error tags. * @var array the jQuery selectors for selecting the container, input and error tags.
* The array keys should be "container", "input", and/or "error", and the array values * The array keys should be "container", "input", and/or "error", and the array values
* are the corresponding selectors. For example, `array('input' => '#my-input')`. * are the corresponding selectors. For example, `array('input' => '#my-input')`.
* *
* The selectors are used under the context of the form. * The container selector is used under the context of the form, while the input and the error
* selectors are used under the context of the container.
* *
* You normally do not need to set this property as the default selectors should work well for most cases. * You normally do not need to set this property as the default selectors should work well for most cases.
*/ */
...@@ -149,7 +126,9 @@ class ActiveField extends Component ...@@ -149,7 +126,9 @@ class ActiveField extends Component
protected function getClientOptions() protected function getClientOptions()
{ {
if ($this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation) { $enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation;
$enableAjaxValidation = $this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation;
if ($enableClientValidation) {
$attribute = Html::getAttributeName($this->attribute); $attribute = Html::getAttributeName($this->attribute);
$validators = array(); $validators = array();
foreach ($this->model->getActiveValidators($attribute) as $validator) { foreach ($this->model->getActiveValidators($attribute) as $validator) {
...@@ -164,19 +143,14 @@ class ActiveField extends Component ...@@ -164,19 +143,14 @@ class ActiveField extends Component
} }
} }
if ($this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation) { if ($enableAjaxValidation) {
$options['enableAjaxValidation'] = 1; $options['enableAjaxValidation'] = 1;
} }
if (isset($options['validate']) || isset($options['enableAjaxValidation'])) { if ($enableClientValidation || $enableAjaxValidation) {
$inputID = Html::getInputId($this->model, $this->attribute); $inputID = Html::getInputId($this->model, $this->attribute);
$options['name'] = $inputID; $options['name'] = $inputID;
if ($this->model instanceof ActiveRecord && !$this->model->getIsNewRecord()) {
$option['status'] = 1;
}
$names = array( $names = array(
'enableAjaxValidation',
'validateOnChange', 'validateOnChange',
'validateOnType', 'validateOnType',
'validationDelay', 'validationDelay',
...@@ -191,15 +165,6 @@ class ActiveField extends Component ...@@ -191,15 +165,6 @@ class ActiveField extends Component
} else { } else {
$options['error'] = isset($this->errorOptions['tag']) ? $this->errorOptions['tag'] : 'span'; $options['error'] = isset($this->errorOptions['tag']) ? $this->errorOptions['tag'] : 'span';
} }
foreach (array('beforeValidate', 'afterValidate') as $callback) {
$value = $this->$callback;
if ($value instanceof JsExpression) {
$options[$callback] = $value;
} elseif (is_string($value)) {
$options[$callback] = new JsExpression($value);
}
}
return $options; return $options;
} else { } else {
return array(); return array();
...@@ -359,11 +324,29 @@ class ActiveField extends Component ...@@ -359,11 +324,29 @@ class ActiveField extends Component
* *
* The rest of the options will be rendered as the attributes of the resulting tag. The values will * The rest of the options will be rendered as the attributes of the resulting tag. The values will
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
* @param boolean $enclosedByLabel whether to enclose the radio within the label.
* If true, the method will still use [[template]] to layout the checkbox and the error message
* except that the radio is enclosed by the label tag.
* @return string the generated radio button tag * @return string the generated radio button tag
*/ */
public function radio($options = array()) public function radio($options = array(), $enclosedByLabel = true)
{ {
return $this->render(Html::activeRadio($this->model, $this->attribute, $options)); if ($enclosedByLabel) {
$hidden = '';
$radio = Html::activeRadio($this->model, $this->attribute, $options);
if (($pos = strpos($radio, '><')) !== false) {
$hidden = substr($radio, 0, $pos + 1);
$radio = substr($radio, $pos + 1);
}
$label = isset($this->labelOptions['label']) ? $this->labelOptions['label'] : Html::encode($this->model->getAttributeLabel($this->attribute));
return $this->begin() . "\n" . $hidden . strtr($this->template, array(
'{input}' => Html::label("$radio $label", null, array('class' => 'radio')),
'{label}' => '',
'{error}' => $this->error(),
)) . "\n" . $this->end();
} else {
return $this->render(Html::activeRadio($this->model, $this->attribute, $options));
}
} }
/** /**
...@@ -379,11 +362,29 @@ class ActiveField extends Component ...@@ -379,11 +362,29 @@ class ActiveField extends Component
* *
* The rest of the options will be rendered as the attributes of the resulting tag. The values will * The rest of the options will be rendered as the attributes of the resulting tag. The values will
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
* @param boolean $enclosedByLabel whether to enclose the checkbox within the label.
* If true, the method will still use [[template]] to layout the checkbox and the error message
* except that the checkbox is enclosed by the label tag.
* @return string the generated checkbox tag * @return string the generated checkbox tag
*/ */
public function checkbox($options = array()) public function checkbox($options = array(), $enclosedByLabel = true)
{ {
return $this->render(Html::activeCheckbox($this->model, $this->attribute, $options)); if ($enclosedByLabel) {
$hidden = '';
$checkbox = Html::activeCheckbox($this->model, $this->attribute, $options);
if (($pos = strpos($checkbox, '><')) !== false) {
$hidden = substr($checkbox, 0, $pos + 1);
$checkbox = substr($checkbox, $pos + 1);
}
$label = isset($this->labelOptions['label']) ? $this->labelOptions['label'] : Html::encode($this->model->getAttributeLabel($this->attribute));
return $this->begin() . "\n" . $hidden . strtr($this->template, array(
'{input}' => Html::label("$checkbox $label", null, array('class' => 'checkbox')),
'{label}' => '',
'{error}' => $this->error(),
)) . "\n" . $this->end();
} else {
return $this->render(Html::activeCheckbox($this->model, $this->attribute, $options));
}
} }
/** /**
......
...@@ -106,51 +106,7 @@ class ActiveForm extends Widget ...@@ -106,51 +106,7 @@ class ActiveForm extends Widget
*/ */
public $ajaxVar = 'ajax'; public $ajaxVar = 'ajax';
/** /**
* @var JsExpression|string a [[JsExpression]] object or a JavaScript expression string representing * @var array the client validation options for individual attributes. Each element of the array
* the callback that will be invoked BEFORE validating EACH attribute on the client side.
* The signature of the callback should be like the following:
*
* ~~~
* ~~~
*/
public $beforeValidateAttribute;
/**
* @var JsExpression|string a [[JsExpression]] object or a JavaScript expression string representing
* the callback that will be invoked AFTER validating EACH attribute on the client side.
* The signature of the callback should be like the following:
*
* ~~~
* ~~~
*/
public $afterValidateAttribute;
/**
* @var JsExpression|string a [[JsExpression]] object or a JavaScript expression string representing
* the callback that will be invoked BEFORE validating ALL attributes on the client side when the validation
* is triggered by form submission (that is, [[validateOnSubmit]] is set true).
*
* This callback is called before [[beforeValidateAttribute]].
*
* The signature of the callback should be like the following:
*
* ~~~
* ~~~
*/
public $beforeValidate;
/**
* @var JsExpression|string a [[JsExpression]] object or a JavaScript expression string representing
* the callback that will be invoked AFTER validating ALL attributes on the client side when the validation
* is triggered by form submission (that is, [[validateOnSubmit]] is set true).
*
* This callback is called after [[afterValidateAttribute]].
*
* The signature of the callback should be like the following:
*
* ~~~
* ~~~
*/
public $afterValidate;
/**
* @var array list of attributes that need to be validated on the client side. Each element of the array
* represents the validation options for a particular attribute. * represents the validation options for a particular attribute.
* @internal * @internal
*/ */
...@@ -201,21 +157,6 @@ class ActiveForm extends Widget ...@@ -201,21 +157,6 @@ class ActiveForm extends Widget
if ($this->validationUrl !== null) { if ($this->validationUrl !== null) {
$options['validationUrl'] = Html::url($this->validationUrl); $options['validationUrl'] = Html::url($this->validationUrl);
} }
$callbacks = array(
'beforeValidateAttribute',
'afterValidateAttribute',
'beforeValidate',
'afterValidate',
);
foreach ($callbacks as $callback) {
$value = $this->$callback;
if ($value instanceof JsExpression) {
$options[$callback] = $value;
} elseif (is_string($value)) {
$options[$callback] = new JsExpression($value);
}
}
return $options; return $options;
} }
......
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