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.
-
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.
-
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.
-
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.
-
I’m basically building the input blocks one by one explicitly.
-
Bottom 2 buttons don’t do anything at this point (no controller action right now)
-
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.
-
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
-
yiiActiveForm can only be called ONCE. Any subsequent call will be ignored. (took a while to figure this out)
-
yiiActiveForm MUST be under $(document).ready otherwise, it will not work.
-
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.