Валидация некоторых полей на стороне клиента

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
yura1976
Сообщения: 134
Зарегистрирован: 2012.08.06, 13:24

Валидация некоторых полей на стороне клиента

Сообщение yura1976 »

Есть форма

Код: Выделить всё

$form = ActiveForm::begin([
   'id' => 'tender-form',
   'enableClientValidation' => true,
   'action' => Url::to(['/tenders/add'])
])
 ......

	<button type="button" class="button btn btn-default btntoexpand" data-target="#pricerangeform" id="pricerange">Указать стоимость</button>
       
       <div class="dialog container-fluid" id="pricerangeform">
          <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
          <div class="row">
              <div class="col-md-8">
               <?= kartik\field\FieldRange::widget([
                  'form' => $form,
                  'model' => $model,
                  'separator' => '-',
                  'label' => false,
                  'attribute1' => 'pricestart',
                  'attribute2' => 'priceend',
                  'options1' => ['placeholder' => Yii::t('tenders','PRICESTART')],
                  'options2' => ['placeholder' => Yii::t('tenders','PRICEEND')],
                  'type' => FieldRange::INPUT_TEXT,
               ]); ?>
             </div>
	     <div class="col-md-4">
	       <?= $form->field($model, 'currency_id', [
                  'template' => '<div class="col-xs-8 col-sm-4 col-md-4">{label} {input}{error}{hint}</div>',
                    'enableLabel'=>false
                    ])->radioList(
                      ArrayHelper::map(Currencies::getcurrencieslist(), 'id', 'title')
                  , [
                      'class' => 'btn-group',
                      'data-toggle' => 'buttons',
                      'item' => function ($index, $label, $name, $checked, $value) {
                          return '<label class="' . ($checked ? ' active' : '') . '">' .
                              Html::radio($name, $checked, ['value' => $value, 'class' => 'paramprice1','id'=>'paramprice1_'.$value,'label-value'=>$label]) . $label . '</label>';
                      },
                    ]);
                ?>
	      </div>
	 </div>
	 
	 <input type="button" value="<?=Yii::t('tenders','CHOOSE')?>" id="btnChooseprice" class="btn btnChooseprice">
	 
   </div> //закрыли #pricerangeform.dialog
	 
	 //другие элементы формы
	 
	 <?= Html::submitButton(Yii::t('tenders', 'SENDORDER'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
	 
При клике на #pricerange раскрывается dialog. Там пользователь должен указать стоимость "от" (pricestart) и стоимость "до" (priceend), а также выбрать валюту (currency_id). В правилах валидации я указал, что priceend не может быть меньше pricestart:

Код: Выделить всё

['priceend', 'compare', 'compareAttribute' => 'pricestart', 'operator' => '>=', 'type' => 'number']
Это отлично работает. Ошибка отображается сразу после потери фокуса в поле priceend.
Необходимо еще отображать ошибку в случае, если пользователь указал цену (хотя бы одну из "от" или "до"), но не выбрал валюту. Думаю, что это лучше делать в результате клика на кнопке "btnChooseprice". Но, когда пользователь кликает на ней, то окно закрывается, а вместо указать стоимость появляется "начальная_цена - конечная_цена undefined" (это сделал с помощью js). В принципе это можно сделать с помощью отдельного js-скрипта. А можно ли это каким-то образом сделать средствами фреймворка? Пробовал копать в сторону "'whenClient' => "function (attribute, value) ..." в правилах валидации, но не могу сообразить как. Либо у меня мозгов на это не хватает, либо таким способом эта проблема не решается, и придется продолжить извращаться с js. Подскажите, пожалуйста, как все-таки лучше решить такую задачу. Заранее спасибо!
Последний раз редактировалось yura1976 2019.07.26, 22:51, всего редактировалось 1 раз.
yura1976
Сообщения: 134
Зарегистрирован: 2012.08.06, 13:24

Re: Валидация на стороне клиента

Сообщение yura1976 »

Фактически задача сводится, как я понимаю, к тому, чтобы запустить валидацию одного поля (currency_id) в результате клика на один из элементов (кнопка #btnChooseprice). Но, как это сделать, не соображу.
yura1976
Сообщения: 134
Зарегистрирован: 2012.08.06, 13:24

Re: Валидация на стороне клиента

Сообщение yura1976 »

Сделал так:

Код: Выделить всё

$('#tender-form').yiiActiveForm('validateAttribute','tenders-currency_id')
Ошибка валидации отображается. Но мне нужно еще чтобы, если ошибки нет, то производить определенные действия. Т.е., нужно, чтобы приведенный код возвращал true либо false, но она возвращает независимо от результата валидации undefined. Пробовал отлавливать c помощью jquery класс has-error (через form.find('.has-error').length и через .is('.has-error'), но тоже не удалось.
Подскажите, кто знает, что я делаю не так.
masson
Сообщения: 545
Зарегистрирован: 2012.07.03, 15:59

Re: Валидация на стороне клиента

Сообщение masson »

У меня такой js :

смотрите в дебаггере что такое messages & errorAttributes

Код: Выделить всё

$(document).ready(function () {
    // Валидация yiiActiveForm при нажатии на кнопку
    $("#myForm button").on("click", function () {
        $("#myForm").data("yiiActiveForm").submitting = true;
        $("#myForm").yiiActiveForm("validate");
    });

    $("#myForm").on("afterValidate", function (event, messages, errorAttributes) {
        if (errorAttributes == []) {
        
            .... 
            
            return true;        // Выполняем Submit формы
        }

        .... 

        return false;           // Блокируем отправку формы
    });

});
Отрезал лишние куски, но смысл д.б.понятен
yura1976
Сообщения: 134
Зарегистрирован: 2012.08.06, 13:24

Re: Валидация на стороне клиента

Сообщение yura1976 »

masson писал(а): 2019.07.25, 00:56
Спасибо! Но это не совсем мне подходит. Все поля формы валидируются. В моем случае это в принципе допустимо, но лучше, если будет принудительно запускаться валидация только полей внутри окна .dialog. Я частично решил проблему. Приведу часть кода и опишу оставшуюся проблему. Может кто-нибудь сможет подсказать.

Код: Выделить всё

   
   $('.btnChooseprice').on('click',function(e){ //в открытом окне (.dialog) пользователь кликнул на "Выбрать"
        var form = $('#tender-form');
        if($('#tenders-pricestart').val()>0 || $('#tenders-priceend').val()>0) //если хотя бы одна из цен (начальная или конечная) указана, 
        { //проверяем, выбрана ли валюта
           form.yiiActiveForm('validateAttribute','tenders-currency_id');
        }
   }
В правилах валидации:

Код: Выделить всё

['priceend', 'compare', 'compareAttribute' => 'pricestart', 'operator' => '>=', 'type' => 'number']
['currency_id', 'required',
                'when' => function($data){
                    return $data->pricestart>0 || $data->priceend>0;
                },
                'message' => Yii::t('tenders','ERROR_CURRENCY_IS_REQUIRED')],

Вроде бы все норм, как только кликаю на кнопку "Цена" , ввожу значение начальной и конечной цен такие, что конечная меньше начального, то появляется сообщение об ошибке, как и должно быть. А вот дальше самое интересное. Когда я кликаю на "руб" или "$", сообщение об ошибке "Значение «до» должно быть больше или равно значения «от»." пропадает, но border у priceend - красный как у текстового поля с ошибкой. Я пробовал такой код:

Код: Выделить всё

$('#tenders-currency_id').on('change', 'input[type=radio]', function(){
    $('#tender-form').yiiActiveForm('validateAttribute','tenders-priceend');
}
Результата никакого.
Проблема в виджете FieldRange от kartik. Я попробовал вместо него поставить просто $form->field($model, 'pricestart')->textInput() и аналогично для priceend. В этом случае проблем не наблюдаю, все работает так как мне нужно. В самом крайнем случае можно конечно , отказаться от FieldRange и использовать $form->field($model, 'pricestart')->textInput(). Но, тогда придется чуть повозиться с оформлением, в частности ошибок.
Подскажите, пожалуйста, если кто знает, как решить проблему (с FieldRange).
Ответить