Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveForm: validation error not showing with Bootstrap 4 when using custom input template #214

Open
david-it opened this issue Mar 22, 2020 · 15 comments
Labels

Comments

@david-it
Copy link

The issue is related to the extension yiisoft/yii2-bootstrap4 but I think it is due to a compatibility issue of yiisoft/yii2

What steps will reproduce the problem?

Model

class TestForm extends \yii\base\Model
{
    public $demo;
    public function rules()
    {
        return [
            // define validation rules here
            [['demo'], 'required'],
            [['demo'], 'string', 'length' => [2, 4]],
        ];
    }
}

View
The input error is manually separated from the input field (for example, the error is needed at the top of the form during ajax validation)

<?php $form = ActiveForm::begin(['id' => 'test-form',
'options' => [
	'class' => 'form-horizontal'],
	'enableAjaxValidation' => true,
	'enableClientValidation' => false,
]) 
?>

<?= $form->errorSummary($model) ?>

<div class="form-row"> 
<div class="form-group col-md-3">
	<?php echo $form->field($model, 'demo', ['template' => "{error}"]); ?>
</div>
</div>

<div class="form-row"> 
<div class="form-group col-md-3">
	<?= $form->field($model, 'demo', ['errorOptions' => ['tag' => false]])->textInput()->label("Demo") ?>
</div>
</div> 

Controller

    public function actionTesterror()
    {
        $model = new TestForm();

        // Ajax validation
        if (Yii::$app->request->isAjax){
            $model->load(Yii::$app->request->post());
            Yii::$app->response->format = Response::FORMAT_JSON;
            $ret = ActiveForm::validate($model);
            Yii::info("Test Error Visualization: " . json_encode($ret));
            return $ret;
        }    
        return $this->render('testerror', ['model' => $model]);
    }

What is the expected result?

The error message Demo cannot be blank is visible.

What do you get instead?

The error message Demo cannot be blank is not visible.

Additional info

The issue is related to how the visualization of the error messages are handled in Bootstrap 4.

The {error} of the input demo should be displayed in a <div> which does not contain the actual input tag.

In Bootstrap 4 this creates a visualization issue due to the fact that the validation errors are set with display:block or display:none depending on the class of a sibling tag (i.e. the input tag). The css code contains in fact the “sibling combinator” ~.

.was-validated :invalid ~ .invalid-feedback,
.was-validated :invalid ~ .invalid-tooltip,
.is-invalid ~ .invalid-feedback,
.is-invalid ~ .invalid-tooltip {
  display: block;
}

Working solution

Two additional lines can be added to the function updateInput in yii.activeForm.js, in order to force the setting of "display": "block":

// Patch: show errors when input {error} is not in the default position
$(attribute.container).children(attribute.error).css({"display": "block"});
// ...
// Patch: hide errors when input {error} is not in the default position
$(attribute.container).children(attribute.error).css({"display": "none"});

I have the modified version of yii.activeForm.js in the branch "custom" of my fork david-it/yii2-framework

Q A
Yii version 2.0.32
yii2-bootstrap4 2.0.8.0
PHP version 7.3.14
Operating system Raspbian Buster
@lubosdz
Copy link
Contributor

lubosdz commented Mar 24, 2020

Have you setup up properly ActiveForm with BS4 horizontal layout with proper namespace?

use yii\bootstrap4\ActiveForm;

$form = ActiveForm::begin([
    'layout' => 'horizontal',
    'fieldConfig' => [
        'template' => "{label}\n{beginWrapper}\n{input}\n{hint}\n{error}\n{endWrapper}",
        'horizontalCssClasses' => [
            'label' => 'col-sm-4 text-right',
            'offset' => 'col-sm-offset-4',
            'wrapper' => 'col-sm-7',
            'error' => '',
            'hint' => '',
        ],
    ],
]);
...
<?= $form->field($model, 'myAttribute')->textInput() ?> // works OK with BS3 & BS4
...
<?php ActiveForm::end() ?>

See documentation.
BS4 has changed some of DOM structures in standard elements. However, changing existing jquery selector could break sites build with BS3. It's sometime necessary to adjust option template for particular element and define own layout. In my experience this is only case with e.g. prepend/append input groups or complex lists (radiolist, checkboxlist). Otherwise Yii2 ActiveForm works out of the box also with BS4.

@david-it
Copy link
Author

I'm afraid that your example is quite different from what I'm trying to point out.
I've shown above a specific example of the issue.

To make it more clear, this works perfect with bootstrap 4:

use yii\bootstrap4\ActiveForm;
...
<?php
  $form = ActiveForm::begin(['id' => 'test-form',
    'options' => [
    'class' => 'form-horizontal'],
    'enableAjaxValidation' => true,
    'enableClientValidation' => false,
  ])
?>
<?= $form->field($model, 'demo')->textInput()->label("Demo") ?>
<?php ActiveForm::end() ?>

This does not work with Bootstrap 4:

use yii\bootstrap4\ActiveForm;
...
<?php
  $form = ActiveForm::begin(['id' => 'test-form',
    'options' => [
    'class' => 'form-horizontal'],
    'enableAjaxValidation' => true,
    'enableClientValidation' => false,
  ])
?>
<?= $form->field($model, 'demo', ['template' => "{error}"]); ?>
<?= $form->field($model, 'demo', ['errorOptions' => ['tag' => false]])->textInput()->label("Demo") ?>
<?php ActiveForm::end() ?>

As a use case, you may want to show the error somewhere far away from the input. For example at the top of the form.

@simialbi
Copy link
Contributor

I think to show the error(s) on top of the form, you should use ErrorSummary.

@david-it
Copy link
Author

david-it commented Mar 25, 2020

During ajax validation the ErrorSummary is not shown.
The function updateSummary (in yii.activeForm.js) is called only if the form is submitted.

In any case, this does not explain why the code above is not supposed to work. Since there is the possibility to separate the {error} from the input there should be also the possibility to display it.

I know many things are different but in Yii1 there is a very similar way to separate the error messages and it works right away.

@lubosdz
Copy link
Contributor

lubosdz commented Mar 25, 2020

Well, if you isolate the error (by changing template content) then you actually break expected DOM elements structure - so it won't work. Though valid, your use case is not standard implementation, unfortunatelly. I see 3 possible solutions:

  • either to modify JS handler for yii.activeForm
  • or dynamically (on page ready) rename original ID and inject DOM element wherever you need to (or if possible move whole field container while keeping DOM structure)
  • add you custom CSS to highlight error

@leocharrua
Copy link

Hello. I don't know if my issue is directly related with this, but I think it can be.
I'm submitting a form with ajax. When the form comes with validation errors, I got this:
form.yiiActiveForm("updateMessages", data.validation, true); in the javascript part.

The issue is, if I use yii\widgets\ActiveForm works ok, but If I use yii\bootstrap4\ActiveForm, the errors not showing up.

<?php $form = ActiveForm::begin([
           'id' => 'orden-items-create-form-id',
           'enableClientValidation' => true,
           'enableAjaxValidation' => true,
           ],
 ]); ?>

Also, I tried with 'enableClientValidation' => false ... the form is submitted but when it comes back with errors, these not showing up either.

Any ideas?

Thanks

@samdark
Copy link
Member

samdark commented Sep 18, 2021

No. I guess further debugging is needed in order to come up with the reason for it.

@leocharrua
Copy link

How I can help?

@samdark
Copy link
Member

samdark commented Sep 22, 2021

  1. Check console. Are there any errors?
  2. Inspect HTML. Are there error classes added? If so, are there styles for these?

@leocharrua
Copy link

leocharrua commented Sep 24, 2021

Hello. After a debug session, I think "form.yiiActiveForm" is not the problem ... really, it nevers called, because "beforeSubmit" is not called eather.
I'm doing a Ajax validation and this is the context:

Form:

$form = ActiveForm::begin([
            'id' => 'orden-items-create-form-id',
            'enableClientValidation' => false,
            'enableAjaxValidation' => true,
            'validationUrl' => ['/orden-items/validate-create-from-orden', 'orden_id' => $orden_id],
            'action' => ['/orden-items/create-from-orden', 'orden_id' => $orden_id],
        ]);

The controller:

public function actionValidateCreateFromOrden($orden_id)
    {
        $model = new OrdenItems(['scenario' => 'insert']);
        $model->estado_c = 'A';
        $model->orden_id = $orden_id;

        if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
            Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
            return \yii\bootstrap4\ActiveForm::validate($model);
            // return \yii\widgets\ActiveForm::validate($model);
        }
    }

(here I tested with boostrap4 and widgets validate, but the result its the same).

In the form when I use yii\widgets\ActiveForm works ok, but when I use yii\bootstrap4\ActiveForm, nothing happens (no error is shown).

Any ideas?
Thanks

widget_form_1
widget_form_2
bs4_form_1
bs4_form_2
bs4_form_3

@samdark
Copy link
Member

samdark commented Sep 24, 2021

It is in HTML in both cases, right? If so, that's CSS issue. There's basically no class for it.

@leocharrua
Copy link

I don't understand ... The css is the same ... why is working ok with yii\widgets\ActiveForm but not with yii\bootstrap4\ActiveForm?
Can you explain a little more?
Thanks

@samdark samdark transferred this issue from yiisoft/yii2 Sep 24, 2021
@samdark samdark added the type:bug Bug label Sep 24, 2021
@mgrechanik
Copy link

the same problem with bootstrap5

@kcarewicz
Copy link

I had the same issue but when I've added a CSS class tweak to my site's CSS then I was able to see errors in my Boostrap 5 form.

.invalid-feedback {
display: block;
}

For some odd reason .invalid-feedback class in _forms.scss has display: none.

@mgrechanik
Copy link

mgrechanik commented Dec 10, 2024

Hi. Just created a library to solve problems like this one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants