How to enable validation for dynamically created field?

Hi,

So I’m working on a form that I am creating manually via html/php (not using activeform or activefield). How do I enable validation on these fields?

Further, this particular form will be able to add and delete more fields should the user decide to do so. How do I ensure that validation occurs on the newly created fields?

I understand that there may be solutions for this, but I would really prefer to learn how to do it myself, as simply using someone else’s solutions doesn’t teach me anything.

Thanks in advance!

So you are using the framework, but not using it to generate fields (doing it manually), but expecting the framework to validate your fields for you?

Good luck with that.

Here is what won’t work:

-What you are doing

-Client-side cloning

What will work:

-Ajax-based cloning by creating a new model instance, and appending it to your form

Look at this for inspiration - you will need to adapt code to Yii2.

http://www.eha.ee/labs/yiiplay/index.php/en/site/extension?view=tabular

I can handle ajax based cloning (I think… I’ve done something similar but it wasn’t cloning and I used pjax and it broke the validation). However, I don’t know how to get validation working again.

I appreciate the link for the example, but at my current level of expertise, I’m unable to really break down what I’m looking at and replicate just the functions I need (without just copying it outright).

Yes, an extension would be easier, but my goal is to learn to do it myself. I want to learn as much as I can before I start using extensions.

So, this brings me back to my second question. Using activefield/form and ajax cloning, how do I get validation to work on the new fields? What would the code look like? Where would I put it? (controller, model, view?) etc.

Better way create a model and take variable name same as ur HTML fields and put rules in models

Alright guys, I’ve been working on this for quite some time and I still do not have a solid solution. I thought I was clever, but there seems to be some limitations on how Yii handles client-side validation.

I’m going to be as detailed as possible cause I know others are working on this and hopefully they can follow what I’ve done and perhaps add some insight.

If you see gross errors, please point them out. Like my signature says, I’m new to Yii/PHP and coding in general, so I’m sure this isn’t the best approach, however, it’s what I could come up with so far.

Here is the view

newEntry.php


<?php


use yii\helpers\Html;

use yii\widgets\ActiveForm;

use yii\web\View;

use yii\web\JqueryAsset;


/**

 * @var yii\web\View $this

 * @var yii\widgets\ActiveForm $form

 * @var \frontend\models\ThoughtsList $title

 * @var \frontend\models\Thoughts $thoughts

 * 

 */

$this->title = 'New Thoughts';

$this->params['breadcrumbs'][] = $this->title;

?>

<div class="new thoughts">

    <h1><?= Html::encode($this->title) ?></h1>


    <p>What's on your mind?</p>


    <div class="row">

        <div class="col-lg-5">

<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data'],'id' => 'new-thoughts','enableClientValidation' => false]); ?>


<?= $form->field($title, 'title'); ?>


          <button type='button' id='button-add-another' class='btn btn-primary'> New Thought </button>

                <br>

                <br>

                <?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'submit-button']) ?>

        </div>

            <?php ActiveForm::end(); ?>

    </div>

</div>


<?php

$this->registerJsFile('/CMT/frontend/web/assets/cd58df46/yii.validation.js', ['depends' => [JqueryAsset::className()]]);

$this->registerJsFile('/CMT/frontend/js/newEntry.js', ['depends' => [JqueryAsset::className()]]);

?>

        

Nothing really fancy here, but I do need to make a few comments.

  1. You must set ‘enableClientValidation’ to false. But once you do this, yii will not include ‘yii.validation.js’ when the page is generated. If you still want to use yii validation, you’ll need this script.

  2. It must be turned off because you’re going to add in the code explicitly and if you don’t turn it off, you’ll have repetitive code. You have to add it in explicitly in order to manipulate how it’s called.

  3. Make sure you include your script file (that calls upon validation) AFTER you’ve included ‘yii.validation.js’. It will not work otherwise.

Now, here is the script. Validation does not work the way I intend it, but it’s as far as I’ve gotten. Details at the bottom.

newEntry.js


var entMax = 10; //maximum number of entries

var entMin = 5;  //minimum number of entries


// This is the html to be inserted into the form

var ent1 = '<div class="form-group field-thoughts-cmh0-thought required">';

var ent2 = '<label class="control-label" for="thoughts-cmh0-thought">cmh0.</label>';

var ent3 = '<input type="text" id="thoughts-cmh0-thought" class="form-control" name="Thoughts[cmh0][thought]">';

var ent4 = '<button type="button" class="entAdd"> Add Above </button> <button type="button" class="entDelete"> Delete </button>';

var ent5 = '<div class="help-block"></div>';

var ent6 = '</div>';

var ent = ent1 + ent2 + ent3 + ent4 + ent5 + ent6;


// This is to add another entry above

function addAbove(abo) {

    var count = $('[class^="form-group"]:last').attr('class');

    count = count.replace(/\D/g,'');

    count = parseInt(count);

    if (count >= entMax) { // Make sure the limit of entries hasn't been reached

        alert("Too many thoughts!");}

    else {

        $(abo).before(ent);

    }

}


// This sets all the identifying numbers properly

function setCount() {

    $.each($('#new-thoughts>div[class^="form-group field-thoughts-"]'), function(ind) {

        ind++;

        var container = $(this);                                    

        var classn = container.attr('class');

        var text = $(this).find('input').val();                     //Get value from user (so it's not lost when updated)

       

        var regex = /\d+/g;                                         //Regex in order to search tags for numbers

        

        if (container.html().indexOf('cmh0') > -1) {                //Check if random string present

            container.html(container.html().replace(/cmh0/g, ind)); //Replace all instances of random string


            var upcs = classn.replace(/cmh0/g,ind);                 //Replace random string in class attribute with index

            container.attr('class', upcs);                          //Replace random string in class attribute with index

            $(this).find('input').val(text);                        //Set input to user value so it's not lost

        }

        else if (regex.test(container.html())) {                    //Check if number is present

            container.html(container.html().replace(/\d+/g,ind));   //Replace all instaces of number with updated index

            var upcn = classn.replace(/\d+/g,ind);                  //Replace number in class attribute with updated index

            container.attr('class',upcn);                           //Replace number in class attribute with updated index

            $(this).find('input').val(text);                        //Set input to user value so it's not lost

        }

        


    }); 

}


jQuery(document).ready(function() {

    

    // Build form one by one

    for (var i = 0; i < 5; i++){

        $('div[class*="title"]').after(ent);

    }

    i = undefined;

    setCount();

    

    // Add individual entry activation

    $('#new-thoughts').on('click', '.entAdd', function(){

       addAbove($(this).parent());

       setCount();

    });

    

    // Delete individual entry actiation

    $('#new-thoughts').on('click', '.entDelete', function() {

        $(this).parent().remove();

        if ($('[class^="form-group field-thoughts-"]').length < entMin)  // Make sure that at least 5 entries show at all times

        {

            $('[class^="form-group field-thoughts-"]:last').after(ent);

        }

        setCount();

    });

    

    // Yii validation code

    var ind = 5;

    $('#new-thoughts').yiiActiveForm([{

            'id': 'thoughts-' + ind + '-thought', 

            'name': '[' + ind + ']thought',

            'container': '.field-thoughts-' + ind + '-thought',

            'input': '#thoughts-' + ind + '-thought',

            'validate': function (attribute, value, messages, deferred) {

                yii.validation.required(value, messages, {

                    'message': 'Thought cannot be blank.'

                });

                yii.validation.string(value, messages, {

                    'message': 'Thought must be a string.',

                    'min': 1,

                    'tooShort': 'Thought should contain at least 1 character.',

                    'max': 10,

                    'tooLong': 'Thought should contain at most 10 characters.',

                    'skipOnEmpty': 1

                });

            }

    }]);

});

Some more comments.

  1. I’m basically building the input blocks one by one explicitly.

  2. Bottom 2 buttons don’t do anything at this point (no controller action right now)

  3. Validation only works on the 5th field BUT only if you haven’t clicked “add above” or “delete”. Once you click any one of those, validation is broken.

  4. The yiiValidation code is pretty much exactly what Yii would put out if ‘enableClientValidation’ were set to true.

Things I found out about yii validation

  1. yiiActiveForm can only be called ONCE. Any subsequent call will be ignored. (took a while to figure this out)

  2. yiiActiveForm MUST be under $(document).ready otherwise, it will not work.

  3. Even though ‘enableClientValidation’ is set to false, the following code is still present in the view.


<script type="text/javascript">jQuery(document).ready(function () {

jQuery('#new-thoughts').yiiActiveForm([], []);

});</script>

Going back to point 1. if you try to add yii validation after this code snippet, it will not work. yiiActiveForm has to come before this code snippet.

My intent was to create a loop for the validation portion of the code to evaluate each entry. This does not work. Somehow, what needs to happen is the validation code be re-evaluated each time a field is added or created. I do not know how to achieve this, especially since it needs to be under $(document).ready in order to work at all.

So, this is as far as I’ve reached and now I’m stuck and refer to those who are more experienced.

And yes, I know I can just write my own javascript to handle this, but this form is going to get more complicated and I’d really like to figure out how to use the tools Yii has available.

Any and all help would be appreciated.

is this what you’re trying to do?

If so read more if not ignore.

I made a dynamic drag and drop dynamic form creator and my company (cant share bc im not allowed to) has been using it for about a year and it works great. i am using only Yii and almost no js/jquery (used yii jquery) but it was for Yii1. I feel your making this really complicated by not using the built in Yii’s features.

As far as the validation goes all i did was allowed only certain types of fields i.e. dropdown, date, text, textarea, select, checkbox, password etc and just did a foreach loop in the validation. I used the same validator when making a form and when submitting a form.

i created a widget to show the dynamic fields from the database using a foreach and using Yii form fields using case switch so it displayed properly (ie as a textbox or password)

Used 2 sortable widget for the drag and drop

originally it was in MySQL (switched it to NoSQL and made it a lot easier) and i had three tables something like this (don’t remember exactly what they where but you will get the idea)

forms

  • id
  • name

formFields

  • id
  • form_id
  • name
  • hint
  • required(i.e. int for validation)
  • placeholder
  • type (i.e. text, password, textarea int for validation)
  • min (i.e. int for validation)
  • max (i.e. int for validation)

formFiledOptions (used for select, booleans, dropdowns)

  • id
  • form_field_id
  • value

used to record the answers

formAnswers

  • id
  • form_field_id
  • value
  • user_id
  • datetime

skworden, thanks but that’s still not quite what I’m looking for.

I think people are getting confused on what I’m trying to do, so I’ve created a fiddle.

http://jsfiddle.net/jLnbyd1q/

Basically, I can get validation to the work on the initial 5 entries (after title). However, the moment you click "add above" or "delete", validation breaks and validation will not work on any of the newly created entry fields.

How do I get validation to work on the newly created fields (and the old ones)?