Ajax-валидация со сценариями: не отображаются ошибки валидации на последующих сценариях

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
walkman
Сообщения: 1
Зарегистрирован: 2023.11.04, 00:02

Ajax-валидация со сценариями: не отображаются ошибки валидации на последующих сценариях

Сообщение walkman »

Написал небольшую форму регистрации.
Модель:

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

    public const SCENARIO_STEP_1 = 1;
    public const SCENARIO_STEP_2 = 2;
    public const SCENARIO_STEP_3 = 3;

    public function scenarios(): array
    {
        $scenarios = parent::scenarios();
        $scenarios[self::SCENARIO_STEP_1] = ['email'];
        $scenarios[self::SCENARIO_STEP_2] = ['username', 'password'];
        $scenarios[self::SCENARIO_STEP_3] = ['firstname', 'lastname', 'organization'];

        return $scenarios;
    }
    
        public function rules(): array
    {
        return [
            ['step', 'number'],
            ['username', 'trim'],
            ['username', 'required'],
            [
                'username',
                'unique',
                'targetClass' => Users::class,
                'message'     => 'This username has already been taken.',
            ],
            ['username', 'string', 'min' => 2, 'max' => 255],

            ['email', 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            ['email', 'string', 'max' => 255],
            [
                'email',
                'unique',
                'targetClass' => '\common\entity\Users',
                'message'     => 'This email address has already been taken.',
            ],

            ['password', 'required'],
            ['password', 'validatePassword'],

            [['firstname', 'lastname'], 'required'],
        ];
    }

Имеется контроллер:

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


    $model = new SignupForm();

    $result = [
        'status' => 'success',
        'cur_step' => 0,
        'next_step' => 1,
        'final_step' => false,
    ];

    $session = Yii::$app->session;
    $session_signup_form = $session->get(self::SIGNUP_FORM);

    if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
        Yii::$app->response->format = Response::FORMAT_JSON;

        switch ($model->step) {
            case SignupForm::SCENARIO_STEP_1:
                $model->setScenario(SignupForm::SCENARIO_STEP_1);
                break;
            case SignupForm::SCENARIO_STEP_2:
                $model->setScenario(SignupForm::SCENARIO_STEP_2);
                break;
            case SignupForm::SCENARIO_STEP_3:
                $model->setScenario(SignupForm::SCENARIO_STEP_3);
                break;
            default:
                $model->setScenario(SignupForm::SCENARIO_STEP_1);
        }

        if (Yii::$app->request->get('validate')) {

            return ActiveForm::validate($model);
        }

        if ($model->validate()) {
            $session_model = new SignupForm();

            if ((int)$model->step === SignupForm::SCENARIO_STEP_1) {
                $session->set(self::SIGNUP_FORM, $model->getAttributes());
                $result['cur_step'] = SignupForm::SCENARIO_STEP_1;
                $result['next_step'] = SignupForm::SCENARIO_STEP_2;
            }

            if ((int)$model->step === SignupForm::SCENARIO_STEP_2) {
                $session_model->setAttributes($session_signup_form, false);
                $session_model->step = (int)$model->step;
                $session_model->username = $model->username;
                $session_model->password = $model->password;

                $model->setAttributes($session_signup_form, false);
                $session->set(self::SIGNUP_FORM, $session_model->getAttributes());
                $result['cur_step'] = SignupForm::SCENARIO_STEP_2;
                $result['next_step'] = SignupForm::SCENARIO_STEP_3;
            }

            if ((int)$model->step === SignupForm::SCENARIO_STEP_3) {
                $session_model->setAttributes($session_signup_form);
                $session_model->step = (int)$model->step;
                $session_model->firstname = $model->firstname;
                $session_model->lastname = $model->lastname;
                $session_model->organization = $model->organization;

                $model->setAttributes($session_model->getAttributes(), false);
                $session->set(self::SIGNUP_FORM, $session_model->getAttributes());
                $result['cur_step'] = SignupForm::SCENARIO_STEP_3;
                $result['final_step'] = true;
                if ($model->signup()) {
                    $session->remove(self::SIGNUP_FORM);
                    Yii::$app->session->setFlash(
                        'success',
                        'Thank you for registration. Please check your inbox for verification email.'
                    );
                }
            }

            return $result;
        }
    }

Представление:

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


    <?php
    $form = ActiveForm::begin([
        'id' => 'form-signup',
        'enableAjaxValidation' => true,
        'enableClientValidation' => false,
        'validateOnSubmit' => true,
        'validateOnChange' => false,
        'validateOnBlur' => false,
        'validationUrl' => Url::to(['/signup-steps', 'validate' => '1']),
        'action' => ['/signup-steps']
    ]); ?>
    <ul id="form-signup-steps" class=" uk-switcher uk-margin">
        <li>
            <div class="uk-width-1-1 uk-margin">
                <?= $form->field($model, 'email', [
                    'inputOptions' => [
                        'placeholder' => 'Enter email address',
                        'type' => 'email',
                        'autocomplete' => 'email',
                    ]
                ])->label(false) ?>
            </div>
            <div class="uk-width-1-1 uk-text-center">
                <a id="btn-nextstep1" class="uk-button uk-button-text uk-button-large">Next</a>
            </div>
        </li>
        <li>
            <div class="uk-width-1-1 uk-margin">
                <?= $form->field($model, 'username', [
                    'inputOptions' => [
                        'placeholder' => 'Enter username',
                        'type' => 'text',
                        'autocomplete' => 'username',
                    ]
                ])->label(false) ?>
            </div>
            <div class="uk-width-1-1 uk-margin">
                <?= $form->field($model, 'password', [
                    'inputOptions' => [
                        'placeholder' => 'Enter password',
                        'type' => 'password',
                        'autocomplete' => 'new-password',
                        'uk-tooltip' => Yii::t('app', 'At least 8 characters of letters, numbers and special characters'),
                    ]
                ])->label(false) ?>
            </div>
            <div class="uk-width-1-1 uk-text-center">
                <a id="btn-nextstep2" class="uk-button uk-button-text uk-button-large">Next</a>
            </div>
        </li>
        <li>
            <div class="uk-width-1-1 uk-margin">
                <?= $form->field($model, 'firstname', [
                    'inputOptions' => [
                        'placeholder' => 'Enter firstname',
                        'type' => 'text'
                    ]
                ])->label(false) ?>
            </div>
            <div class="uk-width-1-1 uk-margin">
                <?= $form->field($model, 'lastname', [
                    'inputOptions' => [
                        'placeholder' => 'Enter lastname',
                        'type' => 'text'
                    ]
                ])->label(false) ?>
            </div>
            <div class="uk-width-1-1 uk-margin">
                <?= $form->field($model, 'organization', [
                    'inputOptions' => [
                        'placeholder' => 'Enter organization (optional)',
                        'type' => 'text'
                    ]
                ])->label(false) ?>
            </div>
            <div class="uk-width-1-1 uk-text-center">
                <a id="btn-nextstep3" class="uk-button uk-button-primary uk-button-large"><?= Yii::t('app', 'Signup') ?></a>
            </div>
            <?= $form->field($model, 'step', ['inputOptions' => [
                'value' => SignupForm::SCENARIO_STEP_1,
            ]])->label(false)->hiddenInput() ?>
            <div hidden class="uk-width-1-1 uk-text-center">
                <button class="uk-button uk-button-primary uk-button-large">Sign up</button>
            </div>
            <div class="uk-width-1-1 uk-margin uk-text-center">
                <p class="uk-text-small uk-margin-remove">By signing up you agree to our <a class="uk-link-border" href="#">terms</a> of service.</p>
            </div>
        </li>
    </ul>
    <?php ActiveForm::end(); ?>

JS:

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

 

    $form.on('afterValidate', function (event, messages, deferreds) {
        console.log(event);
        console.log(messages);
        console.log(deferreds);
        switchIconSpin();
    }).on('beforeSubmit', function (event, messages, deferreds) {
        console.log(event);
        console.log(messages);
        console.log(deferreds);
        $.ajax({
            url: $form.attr('action'),
            type: 'POST',
            data: $form.serialize(),
            /** @param {{status: string, cur_step: number, next_step: number, final_step: boolean}} data */
            success: function (data) {
                console.log(data);
               if (data) {
                    if (data.status && !data.final_step) {
                        activeCurrentStep(data.next_step)
                    }
                    if (data.final_step) {
                        activeCurrentStep(data.cur_step + 1);
                        window.location.href = '/login';
                    }
                }
            },
            error: function (jqXHR, errMsg) {
                alert(errMsg);
            }
        });
        return false; // prevent default submit
    });
Первый шаг регистрации:
Изображение

Второй шаг регистрации:
Изображение

Получается следующее:
Мы проходим первый шаг регистрации, где по сценарию проверяется только почта. Валидация корректная, сообщение с ошибкой выводится.
Далее переходим ко второму шагу регистрации, где по сценарию логин и пароль. Вот тут валидация в модели проходит корректно, но почему-то в JS в событии AfterValidate в deferreds не возвращается текст сообщений об ошибках, который должен выводиться в форме. Хотя в параметре messages, эти сообщения об ошибках есть.

Как это исправить? Понимаю, что можно это всё обрабатывать вручную, но хотелось бы использовать валидацию из под коробки.
Ответить