Kendo UI Validator - Handling inputs with identical name attributes - javascript

I currently have a form similar to the below:
<form action="/" method="post" id="myForm">
<div class="row">
<input type="text" name="rowValue" class="rowValue">
</div>
<div class="row">
<input type="text" name="rowValue" class="rowValue">
</div>
<div class="row">
<input type="text" name="rowValue" class="rowValue">
</div>
<div class="row">
<input type="text" name="rowValue" class="rowValue">
</div>
<input type="submit" value="Submit">
</form>
A little background: JS is used to inject X amount of new "rows" into the form.
I tried using:
var myForm = $('#myForm').kendoValidator({
/* rules/messages go here*/
}).data('kendoValidator');
myForm.validate();
I only get one error message showing up on the first input[name='rowValue'].
JS Fiddle
My suspicion is Kendo Validator needs unique name attributes to validate correctly. This is a shame, since lots of backend languages have the ability to accept identical name attributes, as they concatenate the values or convert them into an array or collection (ASP.NET).
Is there a way to have Kendo UI Validator validate form fields with identical name attributes?

Your suspicion is correct. You could adjust the validator for your use case like this:
kendo.ui.Validator.prototype.validateInput = function (input) {
input = $(input);
var that = this,
template = that._errorTemplate,
result = that._checkValidity(input),
valid = result.valid,
className = ".k-invalid-msg",
fieldName = (input.attr("name") || ""),
lbl = input.parent().find("span" + className).hide(),
messageText;
input.removeAttr("aria-invalid");
if (!valid) {
messageText = that._extractMessage(input, result.key);
that._errors[fieldName] = messageText;
var messageLabel = $(template({
message: messageText
}));
that._decorateMessageContainer(messageLabel, fieldName);
if (!lbl.replaceWith(messageLabel).length) {
messageLabel.insertAfter(input);
}
messageLabel.show();
input.attr("aria-invalid", true);
}
input.toggleClass("k-invalid", !valid);
return valid;
};
Note that there are a few simplifications in this method, so it may break on certain corner cases.
(demo)

Related

An ASP.NET MVC with a view with a dynamic number of inputs

Ok, this is a complicated question and I'm so new at this, maybe I don't even know how to ask it properly.
I'm building an app using ASP.NET MVC. I have a view that contains a form that has an textbox input that asks for a user to name a recipient to their estate for a will. Below the input, I want a button ("Add another recipient") that adds another input to the form.
Here's how I've mocked this up using Javascript (Click "Yes" and then click "Cash" to see what I'm talking about): https://jsfiddle.net/cinerobert/409k27ot/7/
<p>Would you like to leave specific gift in your will?</p>
<p>
<button onclick="q1AnswerYes()">Yes</button>
<button onclick="q1AnswerNo()">No</button>
</p>
<br />
<!-- If answer to Q1 is Yes show this block -->
<div id="q1AnswerYesBlock" style="display:none; text-align:center;
margin:auto;">
<div>What specific gifts would you like to leave?</div>
<hr>
<div>
<button onclick="q2AnswerCash('q2AnswerCashDisp')">Cash</button>
<button>Car</button>
<button>Real Estate</button>
<button>Jewelry</button>
<button>Other</button>
</div>
<div id="q2AnswerCashDisp">
<!-- Recipient forms get added here -->
</div>
<div style="text-align:center;
margin:auto; display:inline">
<button id="addCashRecipient" style="display:none" onclick="addCashRecipientForm('q2AnswerCashDisp')">Add another recipient</button>
</div>
</div>
<!-- If answer to Q1 is No show this block -->
<div id="q1AnswerNoBlock" style="display:none">
<p>Some other crap</p>
</div>
<div id="testFormHTML" style="display:none">
<form id="testFormId" method="get" style="border-syle:none">
<fieldset id="fieldsetid" style="border-style:none">
<div class="formPrompt">
Amount:
</div>
<input type="text">
<div class="formPrompt">
Recipient:
</div>
<input onblur="submit()" type="text" name="recipient">
<input style="display:none" class="testFormParamInputId" name="testFormParam" value="">
<br>
<br>
</fieldset>
</form>
</div>
<script>
var cashRecipientNum = 1;
function q1AnswerYes() {
document.getElementById("q1AnswerYesBlock").style.display = "block";
document.getElementById("q1AnswerNoBlock").style.display = "none";
}
function q1AnswerNo() {
document.getElementById("q1AnswerYesBlock").style.display = "none";
document.getElementById("q1AnswerNoBlock").style.display = "block";
}
function q2AnswerCash(blockId) {
if (cashRecipientNum == 1) {
addForm(blockId, "testForm", cashRecipientNum++);
document.getElementById("addCashRecipient").style = "display:inline";
};
}
function addCashRecipientForm(blockId) {
addForm(blockId, "testForm", cashRecipientNum++);
}
// Function to add a new form with unique action argument to the document.
function addForm(blockId, formArg, numInstance) {
var formHTML = formArg + "HTML";
var formId = formArg + "Id";
var formParamInputId = formArg + "ParamInputId";
var newFormId = formArg + "Id" + numInstance;
var div = document.getElementById(formId),
clone = div.cloneNode(true); // true means clone all childNodes and all eventhandlers
clone.id = newFormId;
clone.style = "border-style:none";
document.getElementById(blockId).appendChild(clone);
var x = document.getElementById(newFormId).getElementsByClassName(formParamInputId);
x[0].value = numInstance;
console.log(x[0].class);
console.log("here");
}
</script>
What I can't wrap my head around is this:
since I'm using a hardcoded html form (using tags) rather than html helpers (like #Html.TextBoxFor) each input isn't bound to anything in the model. I can read the inputs in using FormCollection, but when the form submits I don't know how to repost the form with the values the user entered still in place.
if I use html helpers (#Html.TextBoxFor) and bind each input to a property in the model, then I don't understand how to allow the form to add an unlimited number of input fields
I've searched around for examples of dynamic views, but the examples I've found have to do with a view that responds to changes in the model. I haven't found one that involves adding an unlimited number of input fields based on a user action.
I know this is kind of a shaggy dog of a question, but if someone could help point me in the right direction I would be very grateful. Thank in advance for being patient with a newbie.
The scenario you're describing screams View Model. Even a simple one like this would help
public class WillRecipientsViewModel
{
public List<string> Recipients { get; set; }
}
Then your controller would need the corresponding action of your form
// Controller
public ActionResult SetWillRecipients()
{
// Added this to force at least 1 text box into the view
WillRecipientsViewModel model = new WillRecipientsViewModel
{
Recipients = new List<string> { "" };
};
return View(model);
}
[HttpPost]
public ActionResult SetWillRecipients(WillRecipientsViewModel model)
{
// Business logic
// Go back to the view if an error occurs
return View(model);
}
// View
#model WillRecipientsViewModel
#using(Html.BeginForm("SetWillRecipients"))
{
#Html.DisplayFor(model => model.Recipients)
for (int i = 0; i < Model.Recipients.Count; i++)
{
// Generate the textboxes for any values that were previously submitted
Html.InputFor(model => model.Recipients[i])
}
}
This will bind the values in your form to the Model object which will make things easier. Then you just need to make sure your view is using <input type="text" name="Recipients" /> to capture the data in the model object in your post. I would also recommend doing your best to have the input name attributes closely match the casing of the view models to avoid and conflicts
Updated
I included some more code to exemplify how you can pull the previous recipients into the interface

Jquery not returning the values in form INPUTs

I have a login form on a modal jquery dialog with the usual 2 text INPUTs. When I enter a login name and password then click the submit, the call back function is called.
The first thing the callback does is try to extract the values of the two INPUTs, but the values returned are empty strings (I have a breakpont here, and have even stepped through the jquery processing of the objects - they objects are correctly identified as the fields on the form, but value="" for both).
At this point I can still see the values in the form, and when the callback exits and the focus goes back to the form, the values are still in the INPUTS. I also tried .prop("value") rather than .val(), but the result was the same.
I just can't figure why I can't read the values - any help appreciated.
<form id="cp-loginform" action="/cypo/index.php" method="POST" >
<input type="hidden" name="Login" value="Login">
<input type="hidden" name="pp" value="0" />
<input type="text" id="cp-loginname" name = "loginname" placeholder = "Login ID" class="loginforminput cp-width-50" autofocus >
<input type="password" id="cp-password" name = "password" placeholder = "password" class="loginforminput cp-width-50"></p>
<input type="submit" id="cp-submit" name = "submit" onclick="ProcessLogin()" ></p>
</form>
function ProcessLogin() {
var loginval = $("#cp-loginname").val();
var passwordval = $("#cp-password").val();
console.log(loginval.concat(" ",passwordval));
}
PROBLEM RESOLVED:
I felt that this was a scope issue. The form itself was obviously OK (if submitted from the dialog it worked) - it was just the attempt to check the INPUT values using jquery that wasn't working.
I found that my select had to start with the dialog element and include a descendent path to my INPUTs. It's as if the dialog puts a wrapper around the elements inside so they are no longer visible as owned by the document.
If I login with xxx and zzz and step therough the following code I see this:
var loginval = $("#cploginname").val(); << = ""
var passwordval = $("#cppassword").val(); << = ""
var loginval = $("#cp-loginform #cploginname").val(); << = ""
var passwordval = $("#cp-loginform #cppassword").val(); << = ""
var loginval = $("#cpdialog #cp-loginform #cploginname").val(); << = "xxx"
var passwordval = $("#cpdialog #cp-loginform #cppassword").val(); << = "zzz"
console.log(loginval.concat(" ",passwordval));
I can't say I understand what's going on, but I have a solution so I am happy. Thanks to all who answered.
FINAL WORD
Thanks to #CMedina, I now understand. The form was defined in a hidden DIV at the top of my BODY section, and I passed $("#loginform") to a f() that created the dialog. The dialog was added to the DOM just before the . I had missed the fact that my original form was still in the DOM, so I was referencing that, not the dialog copy. When I included the dialog wrapper in the path, I finally 'found' the second copy.
Your button is the type submit (their natural behavior is to send the form). Remove the onclick in your button html.
<input type="submit" id="cp-submit" name = "submit">
You must add preventDefault to prevent submit the form and do what you want. Add the code JS for the button onclick event
$("#cp-submit").on("click", function(e){
e.preventDefault();
var loginval = $("#cp-loginname").val();
var passwordval = $("#cp-password").val();
console.log(loginval.concat(" ",passwordval));
});
Result: https://jsfiddle.net/cmedina/svjqb2a4/
Try it :
<html>
<head>
</head>
<body>
<form id="cp-loginform" action="/cypo/index.php" method="POST">
<input type="hidden" name="Login" value="Login">
<input type="hidden" name="pp" value="0" />
<input type="text" id="cp-loginname" name = "loginname" placeholder = "Login ID" class="loginforminput cp-width-50" autofocus >
<input type="password" id="cp-password" name = "password" placeholder = "password" class="loginforminput cp-width-50">
<input type="submit" id="cp-submit" name ="submit" onclick="ProcessLogin(event)">
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>
function ProcessLogin(e) {
e.preventDefault();
var loginval = $("#cp-loginname").val();
var passwordval = $("#cp-password").val();
alert(loginval.concat(" ",passwordval));
}
</script>
</body>
</html>

Selecting specific element within a div?

I am building a contact form, and I am having problems with jQuery. I want to select specific input fields that have an error and apply the class err. Unfortunately, my code selects all inputs when there is an error. I am having trouble identifying which part of my logic is wrong.
$('#send_mail').click(function(){
$("#contact_body").find('label').each(function(){
var contact_label = $('input[required=true], textarea[required=true]');
var label_check = $(this).find(contact_label);
$(contact_label).removeClass('err');
if (!$.trim($(label_check).val())){
$(contact_label).addClass('err');
}
});
});
The order of my HTML goes something like so:
#contact_body
<label>
<input>
</label>
This selects all input and textarea elements:
var contact_label = $('input[required=true], textarea[required=true]');
Instead, you should restrict it to the elements within the label:
var contact_label = $(this).find('input[required=true], textarea[required=true]');
Note that $(contact_label) and contact_label are equivalent in your code, as well as $(label_check) and label_check.
Also, you can use the state parameter of toggleClass() to simplify this:
contact_label.removeClass('err');
if (!$.trim(label_check.val())){
contact_label.addClass('err');
}
… to this:
contact_label.toggleClass('err', !$.trim(label_check.val()));
Here's the updated event:
$('#send_mail').click(function(){
$('#contact_body').find('label').each(function(){
var contact_label = $(this).find('input[required=true], textarea[required=true]');
var label_check = $(this).find(contact_label);
contact_label.toggleClass('err', !$.trim(label_check.val()));
});
});
I think your original code would work if you just changed this line:
$(contact_label).addClass('err');
To this:
$(label_check).addClass('err');
Because $(contact_label) references all the required inputs, whereas $(label_check) references only the input being checked.
But your code could be simplified, and you make unnecessary calls to $(), giving it an argument that is already a JQuery object.
I also do not see that you need to loop through the labels. You could loop through the required inputs instead.
$('#send_mail').click(function(){
$("#contact_body").find(':input[required]').each(function() {
var $input = $(this);
$input.removeClass('err');
if (!$.trim($input.val())){
$input.addClass('err');
}
});
});
Which could be shortened by using the .toggleClass() function:
$('#send_mail').click(function(){
$("#contact_body").find(':input[required]').each(function() {
$(this).toggleClass('err', !$.trim($input.val()));
});
});
Notes:
The selector ':input' matches <input>, <select> and <textarea> elements.
This is a slightly different approach. Gives a bit more flexibility.
arr = ['first', 'last', 'email', 'msg']; //IDs of fields to check
$('#send_mail').click(function(){
$('input, textarea').removeClass('err');
for (var i=0; i<arr.length-1; i++) { //Loop through all field IDs
if ( $('#'+arr[i]).val() == '' ) {
$('#'+arr[i]).addClass('err').focus();
return false;
}
}
//AJAX to send email goes here
alert('Email sent');
});
.err{background:yellow;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<label for="first">First Name:</label>
<input id="first" type="text" required /><br>
<label for="last">Last Name:</label>
<input id="last" type="text" required/><br>
<label for="email">Email:</label>
<input id="email" type="email" required /><br>
<label for="msg">Message:</label>
<textarea id="msg" required></textarea>
<button id="send_mail">Send</button>
you can simplify the code, there will be less mistakes:
$('#send_mail').click(function(){
$("#contact_body").find('label').each(function(){
var field = $(this).find('[required=true]');
if ($.trim($(field).val())){
$(this).removeClass('err');
}
});
});

Need to redirect form to multiple URLs based on input

I have a basic HTML form and I need help creating a bit of JS to redirect my form to different URLs based on the string typed in a text field.
<form class="form-inline">
<div class="form-group">
<input type="text">
<button type="submit">Go</button>
</div>
</form>
There will be 3 or 4 strings of text - to be entered in the input field - that are "valid" and I need them to make the form redirect to various pages on the site.
For instance, typing valid string "STRING1" would make the page redirect to example.com/something.html on form submit, or "STRING2" to example.com/otherpage.html.
But invalid strings would need to go to a page like "example.com/invalid.html."
The most useful thing I've seen so far is this guide: http://www.dynamicdrive.com/forums/showthread.php?20294-Form-POST-redirect-based-on-radio-button-selected
<script type="text/javascript">
function usePage(frm,nm){
for (var i_tem = 0, bobs=frm.elements; i_tem < bobs.length; i_tem++)
if(bobs[i_tem].name==nm&&bobs[i_tem].checked)
frm.action=bobs[i_tem].value;
}
</script>
In that code, each radio has a value assigned to it. But this doesn't help with text fields or having a blanket redirect if the string is invalid.
Thanks so much for your help.
You could define the routes in an object :
<form class="form-inline">
<div class="form-group">
<input type="text">
<button id="submit-form" type="button">Go</button>
</div>
</form>
var urlMapping = {
"STRING1" : "./first.html",
"STRING2" : "./second.html",
"STRING3" : "./third.html"
}
$("#submit-form").click(function(){
var input = $("input").val().trim().toUpperCase();
if (urlMapping.hasOwnProperty(input)){
window.location = urlMapping[input];
}
else {
//if url not found, redirect to default url
window.location = "./default.html";
}
});
Note : I added .toUpperCase() to make it case-insensitive, so you have to be careful to define the urlMapping keys ("STRING1",..) in uppercase.
This should do what you want:
// Define routing:
var validValues = [{
value: 'STRING1',
url: './something.html'
}, {
value: 'STRING2',
url: './otherpage.html'
}];
var $myInput = $('#my-input');
$('#my-button').click(function(ev) {
ev.preventDefault(); // Prevent submitting the form already
// Look for a valid value
var url = './invalid.html';
$.each(validValues, function(i, validValue) {
if ($myInput.val() === validValue.value) {
url = validValue.url;
return false;
}
});
// Submit the form
$('#my-form').prop('action', url).submit();
alert('Redirecting to ' + url);
});
<form class="form-inline" id="my-form">
<div class="form-group">
<input type="text" id="my-input">
<button type="submit" id="my-button">Go</button>
</div>
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Yii2: Adding two fields and populating third field on the fly

I am trying to populate a total field with id- #appointment-total_amount using javascript/jquery. Referring this Jsfiddle add two fields together - Which is working fine.
I am using this code in my _form.php
<?php
$script = <<<EOD
$(function() {
$('#appointment-doctor_fee').keyup(function() {
updateTotal();
});
$('#appointment-discount').keyup(function() {
updateTotal();
});
var updateTotal = function () {
var input1 = parseInt($('#appointment-doctor_fee').val());
var input2 = parseInt($('#appointment-discount').val());
$('#appointment-total_amount').text(input1 + input2);
};
});
EOD;
$this->registerJs($script);
?>
But nothing is happening on the page.
I am not able to see what I am missing here.
Thanks.
Related HTML
<div class="form-group field-appointment-doctor_fee">
<label class="control-label" for="appointment-doctor_fee">Doctor Fee</label>
<input type="text" id="appointment-doctor_fee" class="form-control" name="Appointment[doctor_fee]" maxlength="10">
</div>
<div class="form-group field-appointment-discount">
<label class="control-label" for="appointment-discount">Discount</label>
<input type="text" id="appointment-discount" class="form-control" name="Appointment[discount]" maxlength="10">
<div class="form-group field-appointment-total_amount">
<label class="control-label" for="appointment-total_amount">Total Amount</label>
<input type="text" id="appointment-total_amount" class="form-control" name="Appointment[total_amount]" maxlength="10">
The error is in this line:
$('#appointment-total_amount').text(input1 + input2);
Should be:
$('#appointment-total_amount').val(input1 + input2);
Besides that add at least simple check for illegal numbers, because you will get NaN if one of the fields is empty or input value is not valid number. Some range limit will be good too.
var updateTotal = function () {
var doctorFee = parseInt($('#appointment-doctor_fee').val());
var discount = parseInt($('#appointment-discount').val());
var totalAmount = doctorFee + discount;
if (isNaN(totalAmount) || totalAmount < 0 || totalAmount > 100000) {
totalAmount = '';
}
$('#appointment-total_amount').val(totalAmount);
};
One more error is in script registration. Change to this:
use yii\web\View;
$this->registerJs($script, View::POS_END);
Otherwise it will be inserted before jQuery (and your script depends on it) and will not be working.
Registering scripts that way is not good practice, it's even mentioned in official documentation. Separate file and using assets is definitely better than struggling with dependencies and inserting js as string (the errors are hard to detect, no autocomplete, etc.).

Categories