0

I am generating many forms on a page dynamically using JS.

Each form corresponds to a yii2 model (which has its rules set up for each attribute).

I would like each form to validate all elements (name, email text inputs) to validate with ajax (as they would normally do as if I only had one form).

I am finding that using the method pointed out here: https://yii2-cookbook.readthedocs.io/forms-activeform-js/

`$('#contact-form').yiiActiveForm('validateAttribute', 'contactform-name');`

gives JS error:

 yii.activeForm.js:276 Uncaught TypeError: Cannot read property 'attributes' of undefined
    at jQuery.fn.init.find (yii.activeForm.js:276)
    at jQuery.fn.init.validateAttribute (yii.activeForm.js:268)
    at jQuery.fn.init.$.fn.yiiActiveForm (yii.activeForm.js:16)
    at HTMLInputElement.<anonymous> (search-follow.js:130)

Is there a way to bind dynamically generated forms to yii2 validation? (not just individual fields but rather entire forms, each with a unique ID).

The documentation on Yii2 js form validation seems not so much unfortunately

help much appreciated

g

gvanto
  • 1,936
  • 3
  • 21
  • 26

2 Answers2

1

In short you can accomplish modal validation by utilizing the native gii generated activeforms using the following steps:

 Example Model Form w/ Validation

Create File at Location: app/web/js/ajax-modal-popup.js

$(function(){
     $(document).on('click', '.showModalButton', function(){
        if ($('#modal').data('bs.modal').isShown) {
            $('#modal').find('#modalContent')
                .load($(this).attr('value'));
            document.getElementById('modalHeader').innerHTML = '<h4>' + $(this).attr('title') + '</h4>';
        } else {
            $('#modal').modal('show')
                .find('#modalContent')
                .load($(this).attr('value'));
            document.getElementById('modalHeader').innerHTML = '<h4>' + $(this).attr('title') + '</h4>';
        }
    });
});

Update File at Location: app/asset/AppAsset.php

class AppAsset extends AssetBundle {

    public $basePath = '@webroot';
    public $baseUrl = '@web';
    public $css = [
    ];
    public $js = [
        'js/ajax-modal-popup.js', //<<<------- Register the script.
    ];
    public $depends = [
        'yii\web\YiiAsset',
    ];

}

Add code chunk to file location: app/views/index.php

<?php
    Html::button('Close', 
      ['value' => Url::to(['ticket/close', 'id'=>$model->ticket_id]), 
      'class' => 'showModalButton btn btn-success']
    );

    Modal::begin([
        'header' => '<h2>Ticket Manager</h2>',
        'id' => 'modal',
        'size' => 'modal-md',
    ]);
    echo "<div id='modalContent'></div>";
    Modal::end();
?>

Add to Controller: app/controllers/ticketController.php

public function actionClose($id) {
    $model = $this->findModel($id);
    $model->scenario = 'close'; //Applied by Ticket model rules.
    if ($model->load(Yii::$app->request->post()) && $model->save()) {
        $model->record_void = 1;
        $model->save();
        return $this->redirect(['index']);
    }elseif (Yii::$app->request->isAjax) {
        return $this->renderAjax('close', [
            'model' => $model
        ]);
    } else {
        return $this->render('close', [
            'model' => $model
        ]);
    }
}

Your Form File Location: app/views/ticket/close.php

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model app\models\Ticket */
/* @var $form ActiveForm */
?>
<div class="ticket-_close">
<h3>Close Ticket</h3>
<?php
    $form = ActiveForm::begin(['options' => [
        'id' => 'takeModal',
        'enableClientValidation' => true,
        'enableAjaxValidation' => true,
    ]]);?>

        <?= $form->field($model, 'ticket_id')->textInput(['readonly' => true, 'value' => $model->ticket_id]) ?>
        <?= $form->field($model, 'problem') ?>
        <?= $form->field($model, 'solution') ?>

        <div class="form-group">
            <?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>
        </div>
    <?php ActiveForm::end(); ?>

</div><!-- ticket-_close -->

See below article for additional details.

http://www.yiiframework.com/wiki/806/render-form-in-popup-via-ajax-create-and-update-with-ajax-validation-also-load-any-page-via-ajax-yii-2-0-2-3/

Jairus
  • 816
  • 8
  • 27
  • Thanks Jairus, but this doesn't show how to bind js-generated forms to Yii2 JS validation. The form IDs are generated (in JS) as follows: id="form-1", id="form-2", etc each of these
    s need to somehow bind with yiiActiveForm JS
    – gvanto Jun 07 '17 at 05:39
  • I added an example to the top, is that what your looking for? The solution above now lets you add as many buttons as you have forms. The forms are then dynamically loaded and work as if you went to the form page. Now it will validate using ajax as @egulhan pointed out you need the enableAjaxValidation parameter set on the activeForm. – Jairus Jun 07 '17 at 10:36
  • I think the solution would need to be JS-based. Not sure Yii allows for more than one form (pointing to same model validator) to be ajax-validated. I couldn't find any documentation suggesting it could anyway. something like yiiActiveForm.bind('form-id1') would have been great ... – gvanto Jun 09 '17 at 08:25
  • So this uses all the native JS generated by ActiveForm widget. Maybe I am missing what you are trying to do. Could you upload some code and describe your application? – Jairus Jun 10 '17 at 02:22
  • No, JS is not generated by activeForm widget, it needs to be bound by it. I'm not sure how to better explain it than above. I've done manual JS validation (ajax) for now so got it working but yeah, would be good to be able to do it without reinventing the wheel for error showing on the form (where yiiActiveForm already has methods for all this stuff). – gvanto Jun 10 '17 at 07:33
  • When you use the activeForm widget, it inherits any validation rules put in model. The JS is automatically generated when you use activeForm. That is one feature that makes the activeForm so useful. See [client-side-validation with active form](]http://www.yiiframework.com/doc-2.0/guide-input-validation.html#client-side-validation). – Jairus Jun 10 '17 at 13:51
0

I put shortly some code snippets which do ajax form operations using Yii 2.

View (_ajaxForm.php: ajax form)

<?php $form = \yii\widgets\ActiveForm::begin([
    // pay attention this config.
    'id' => 'account-form',
    'enableClientValidation' => true,
    'enableAjaxValidation' => true,
    // validation URL via ajax
    'validationUrl' => \yii\helpers\Url::to(['/account/do-ajax-request', 'userId'=>$model->id, 'op' => 'validate-form']),
]); ?>

<?= $form->field($model, 'email')->textInput(['maxlength' => true]) ?>

<?= $form->field($model, 'password')->passwordInput(['maxlength' => true]) ?>

<?= $form->field($model, 'passwordConfirm')->passwordInput(['maxlength' => true]) ?>

<?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>

<div class="form-group">
    <?= \yii\helpers\Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>

<?php \yii\widgets\ActiveForm::end(); ?>

<script type="text/javascript">
    $(function () {
        // URL to save form via ajax
        var formSaveUrl='<?= \yii\helpers\Url::to(['/account/do-ajax-request', 'userId'=>$model->id, 'op' => 'save-form']) ?>';

        // post form via ajax to save
        $(document).on('beforeSubmit', '#account-form', function () {
            $.post(formSaveUrl, $(this).serialize(), function (data) {
                alert(data.message);
            });
            return false;
        });

    });
</script>

Controller (AccountController.php: function which performs ajax operations)

public function actionDoAjaxRequest($userId, $op)
{
    Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;

    $model = $this->findModel($userId);
    $model->load(Yii::$app->request->post());

    // validate form
    if ($op == 'validate-form') {
        $data = \yii\widgets\ActiveForm::validate($model);
    // save form
    } else if ($op == 'save-form') {
        if ($model->save()) {
            $data['result'] = true;
            $data['message'] = 'Success!';
        } else {
            $data['result'] = false;
            $data['message'] = 'Failed!';
        }
    }

    return $data;
}
mrdev
  • 617
  • 7
  • 8