Commit 91002172 by Qiang Xue

Finished ActiveForm.

parent 9fc4c380
......@@ -35,14 +35,12 @@
validatingCssClass: 'validating',
// 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 validating every attribute
beforeValidateAttribute: undefined,
// a callback that is called after validating every attribute
afterValidateAttribute: undefined,
// a callback that is called before validating ALL attributes when submitting the form
// a callback that is called before submitting the form. The signature of the callback should be:
// function ($form) { ...return false to cancel submission...}
beforeSubmit: undefined,
// a callback that is called before validating each attribute. The signature of the callback should be:
// function ($form, attribute, messages) { ...return false to cancel the validation...}
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
ajaxVar: 'ajax'
};
......@@ -66,10 +64,6 @@
enableAjaxValidation: false,
// function (attribute, value, messages), the client-side validation function.
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: 0,
// the value of the input
......@@ -122,6 +116,10 @@
});
},
options: function() {
return this.data('yiiActiveForm').settings;
},
submitForm: function () {
var $form = this,
data = $form.data('yiiActiveForm');
......@@ -135,26 +133,24 @@
clearTimeout(data.settings.timer);
}
data.submitting = true;
if (!data.settings.beforeValidate || data.settings.beforeValidate($form)) {
if (!data.settings.beforeSubmit || data.settings.beforeSubmit($form)) {
validate($form, function (messages) {
var hasError = false;
$.each(data.attributes, function () {
hasError = updateInput($form, this, messages) || hasError;
});
updateSummary($form, messages);
if (!data.settings.afterValidate || data.settings.afterValidate($form, messages, hasError)) {
if (!hasError) {
data.validated = true;
var $button = data.submitObject || $form.find(':submit:first');
// TODO: if the submission is caused by "change" event, it will not work
if ($button.length) {
$button.click();
} else {
// no submit button in the form
$form.submit();
}
return;
if (!hasError) {
data.validated = true;
var $button = data.submitObject || $form.find(':submit:first');
// TODO: if the submission is caused by "change" event, it will not work
if ($button.length) {
$button.click();
} else {
// no submit button in the form
$form.submit();
}
return;
}
data.submitting = false;
}, function () {
......@@ -235,25 +231,20 @@
if (data.submitting || $form.is(':hidden')) {
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 () {
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 () {
if (this.status === 2 || this.status === 3) {
hasError = updateInput($form, this, messages) || hasError;
}
});
if (attribute.afterValidate) {
attribute.afterValidate($form, attribute, messages, hasError);
if (this.status === 2 || this.status === 3) {
hasError = updateInput($form, this, messages) || hasError;
}
});
}
});
}, data.settings.validationDelay);
};
......@@ -271,15 +262,16 @@
$.each(data.attributes, function () {
if (data.submitting || this.status === 2 || this.status === 3) {
var msg = [];
if (this.validate) {
this.validate(this, getValue($form, this), msg);
if (!data.settings.beforeValidate || data.settings.beforeValidate($form, this, msg)) {
if (this.validate) {
this.validate(this, getValue($form, this), msg);
}
if (msg.length) {
messages[this.name] = msg;
} else if (this.enableAjaxValidation) {
needAjaxValidation = true;
}
}
if (this.enableAjaxValidation && !msg.length) {
needAjaxValidation = true;
}
}
});
......
......@@ -896,6 +896,7 @@ class Html
$attribute = static::getAttributeName($attribute);
$label = isset($options['label']) ? $options['label'] : static::encode($model->getAttributeLabel($attribute));
$for = array_key_exists('for', $options) ? $options['for'] : static::getInputId($model, $attribute);
unset($options['label'], $options['for']);
return static::label($label, $for, $options);
}
......
......@@ -85,35 +85,12 @@ class ActiveField extends Component
*/
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.
* The array keys should be "container", "input", and/or "error", and the array values
* 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.
*/
......@@ -149,7 +126,9 @@ class ActiveField extends Component
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);
$validators = array();
foreach ($this->model->getActiveValidators($attribute) as $validator) {
......@@ -164,19 +143,14 @@ class ActiveField extends Component
}
}
if ($this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation) {
if ($enableAjaxValidation) {
$options['enableAjaxValidation'] = 1;
}
if (isset($options['validate']) || isset($options['enableAjaxValidation'])) {
if ($enableClientValidation || $enableAjaxValidation) {
$inputID = Html::getInputId($this->model, $this->attribute);
$options['name'] = $inputID;
if ($this->model instanceof ActiveRecord && !$this->model->getIsNewRecord()) {
$option['status'] = 1;
}
$names = array(
'enableAjaxValidation',
'validateOnChange',
'validateOnType',
'validationDelay',
......@@ -191,15 +165,6 @@ class ActiveField extends Component
} else {
$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;
} else {
return array();
......@@ -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
* 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
*/
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
*
* 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.
* @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
*/
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
*/
public $ajaxVar = 'ajax';
/**
* @var JsExpression|string a [[JsExpression]] object or a JavaScript expression string representing
* 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
* @var array the client validation options for individual attributes. Each element of the array
* represents the validation options for a particular attribute.
* @internal
*/
......@@ -201,21 +157,6 @@ class ActiveForm extends Widget
if ($this->validationUrl !== null) {
$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;
}
......
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