dojo dijit client side validation onchange - validation

So I followed the example in the Dojo - Using the Dojo JavaScript Library to Build Ajax Applications to add server-side validation to the username validationtextbox field on my form. Basically I added a usernameOnChange function that submitted an xhrGet request, the xhrGet returns JSON and is handled by the usernameValidationHandler.
It works great, but the usernameValidationHandler only sets the tooltip display message to an error. It doesn't set the field to be invalid and thus the user can still submit the form. How do I set the field to be invalid so the form won't submit?
<input type="text" id="userName" name="userName" size="20"
dojoType="dijit.form.ValidationTextBox"
trim="true"
required="true"
onChange="userNameOnChange"
regExp="\w+"
invalidMessage="User name is required"
/>
function userNameOnChange() {
var userName = dijit.byId("userName").getValue();
if (userName == "") {
return;
}
dojo.xhrGet( {
url: "validateUserName.jsp?userName=" + userName,
handleAs: "json",
handle: userNameValidationHandler
});
}
function userNameValidationHandler(response) {
dijit.byId("userName").displayMessage();
if (!response.valid) {
var errorMessage = "User name already taken";
// Display error message as tooltip next to field
dijit.byId("userName").displayMessage(errorMessage);
// HOW DO I SET THE FIELD TO BE INVALID NOW???
}
}

It seems I was having the same issues when I used the validation method (validator) for the control. I think the issue is with the nature of the xhrGet method as it is asychronous, so the method for determining if value is valid returns before the query is complete. Anyways, this is what I did to get it working. If there is another way, hopefully someone can post.
dojo.require("dijit.form.ValidationTextBox");
function validateUsername(value, constraint) {
// I believe the whole reason you have to hack at control to get it to
// display an error is due to the nature of the xhrGet call. Since the
// call is being made asychronously, the method would have already
// returned a result to the html control before query has finished.
// Therefore you have to do the extra method calls below. Also note
// that when the form goes for submission, it calls each controls validator
// method again! Meaning it will query the webpage again.
var loginID = dijit.byId("user_login");
var bNoNameFound = ("Error" == loginID.get("state")) ? false : true;
if ("" == loginID.value) {
// for some required=true is not kicking in, so we are forcing it.
bNoNameFound = false;
} else {
if (false == loginID._focused) {
console.log("Checking username...");
dojo.xhrGet({
url: "functions/does_user_exist.php?user_login=" + value,
handleAs: 'text',
content: {
l: value
},
timeout: 10000,
load: function(data) {
if (1 == data) {
// setting the state to error since the username is already taken
bNoNameFound = false;
loginID.set("state", "Error");
loginID.displayMessage("The username is already taken...");
// used to change the style of the control to represent a error
loginID._setStateClass();
console.log("Invalid username");
} else {
bNoNameFound = true;
loginID.set("state", "");
loginID.displayMessage("");
}
}
});
}
}
return bNoNameFound;
}
<input dojoType="dijit.form.ValidationTextBox" type="text" name="user_login" id="user_login" value="" minSize="1" maxSize="25" tabindex="10" required="true" trim="true" regExp="\w+" validator=validateUsername />
Note: I also tried using "sync" in the xhrGet parameters as well. It also had issues (aside from the obvious of locking the control until query is done)...

You could subclass the widget to override the validator method. Perhaps chain up, if you want the base functionality, then, if the result is true, run your own test with the getXhr and return the result.
An example at dojocampus just clobbers the validator method. That might also work, depending on what you want to do.

Related

Dynamic data storage during input with laravel and vuejs

I have a multistep form on vue js. I need to dynamically during input send requests, validate on the server and receive a response to display validation errors.
My data:
data() {
return {
form: {...this.candidate}, // we get an object with fields already filled *
errors: new Errors(), // object with validation errors
}
},
Now for each input I have computed property:
characteristicFuturePlans: {
get() {
return this.form.characteristic_future_plans;
},
set(value) {
this.saveField('characteristic_future_plans', value);
}
},
saveField method sends data:
saveField(field, value) {
this.form[field] = value; // keep the data in the object relevant
axios.put(`/api/candidate/${this.candidate.token}`, {[field]: value})
.then(() => { this.errors.clear(field) })
.catch((error) => {
this.errors.record(field, error.response.data.errors[field]);
});
},
Now, with each change of input, a request will be sent. But with this approach, when we quickly type text in a field, sometimes the penultimate request sent comes after the last. It turns out that if you quickly write "Johnny", sometimes a query with the text "Johnn" will come after a query with the text "Johnny" and the wrong value will be saved in the database.
Then I made sure that the data was sent 1 second after the termination of text input. Added timerId: {} to data(){} and then:
saveField(field, value) {
if(this.timerId[field]) {
clearTimeout(this.timerId[field]);
}
this.timerId[field] = setTimeout(this.send, 1000, field, value);
},
send(field, value) {
this.form[field] = value;
axios.put(`/api/candidate/${this.candidate.token}`, {[field]: value})
.then(() => { this.errors.clear(field) })
.catch((error) => {
this.errors.record(field, error.response.data.errors[field]);
});
},
But now, if after filling in the input in less than a second, press the button to go to the next step of the form, the page will simply refresh. (the button to go to the next step will send a request to the server to check if the required fields are filled)
How correctly save data to the database during text input? Can I do this without setTimeout()? If so, how can I make sure that the data of the last request, and not the penultimate, is stored in the database?
I will be glad to any tip.
Updated. Attach some template code.
Part of Step[i].vue component:
<div class="row">
<div class="form-group col-md-6">
<element-form title="Title text" :error="errors.get('characteristic_future_plans')" required isBold>
<input-input v-model="characteristicFuturePlans" :is-error="errors.has('characteristic_future_plans')"
:placeholder="'placeholder text'"/>
</element-form>
</div>
</div>
template of input-input component:
<input :value="value" :type="type" class="form-control" :class="isErrorClass"
:placeholder="placeholder" #input="$emit('input',$event.target.value)">
Components of form steps are called from the Page component. Nearby is the component of the button for moving to the next step.
<component :is="currentStep"
:candidate="candidate"
// many props
:global-errors="globalErrors"/>
<next-step :current-step="step" :token="candidate.token"
#switch-step-event="switchStep" #throw-errors="passErrors"></next-step>
NextStep component sends a request to the server, it is checking whether the required fields are filled in the database. If not, throw out a validation error. If so, go to the next form step.
you could try with watching the input values, and then use _.debounce() from underscore.js (src: https://underscorejs.org/#debounce) to delay the method call that makes a server request:
watch: {
fieldName: _.debounce(function(value) {
if(value === ''){
return;
}
this.saveField(this.fieldName, value);
},
...

Return a warning (not an error) via Ajax remote validation

I have an MVC Ajax callback that checks to see if a user input is valid. This callback is invoked via the [Remote] attribute on the associated model property.
I've changed my design, and I've decided that I would really like to warn the user if the value is incorrect, but I don't want the incorrect value to prevent model validation.
A quick search turns up several threads describing very involved solutions to the general problem of wiring up "unobtrusive warnings" similar to the "unobtrusive validation" magic baked into MVC (for example this SO post). I'm not looking for a general solution, and I don't want to spend a lot of time and energy on this, but I'm wondering if some Ajax guru knows of something I can return from the Ajax server routine that would have the effect of causing the unobtrusive validation client-side code to put up the message without triggering the validation error.
FYI, my existing server-side code looks like this:
public async Task<ActionResult> CouponCodeExists(string couponCode, int? planId)
{
if (some_logic) {
return Json("Coupon code is already taken", JsonRequestBehavior.AllowGet);
} else {
return Json(true, JsonRequestBehavior.AllowGet);
}
}
A RemoteAttribute is a validation attribute and triggering it will add a validation error to jQuery.validate.js and prevent the form from submitting. If you just want a warning message, and still allow the form to submit, you can just make your own ajax call in the inputs .change() event.
Assuming you have #Html.TextBoxFor(m => m.couponCode) in the view, add a placeholder for the message - say <div id="couponwarning">Coupon code is already taken</div> and style in as display: none;. Then add the following scripts
var url = '#Url.Action("CouponCodeExists")';
var warning = $('#couponwarning');
$('#couponCode').change(function() {
var code = $(this).val();
var id = .... // the value of your planId input?
$.get(url, { couponCode: code, planId: id }, function(response) {
if (response) {
warning.show();
}
});
});
$('#couponCode').keyup(function() {
warning.hide();
});
and the method can just return true to display the message, or null
if (some_logic) {
return Json(true, JsonRequestBehavior.AllowGet);
} else {
return Json(null, JsonRequestBehavior.AllowGet);
}

about jquery post method

(please don't mark it as a duplicate before you read it through thanks ^_^)
I would like to implement a user registration form. Before the user clicks "submit", it calls a function check_username() which checks whether the username is available. If the function returns true, the form will be submitted otherwise not.
The form is like this:
<form action="register/sumbit" method="post" onsubmit="return check_username()">
...
</form>
To implement the check_username function, I adopted the method proposed in How to return the response from an AJAX call? , but it did not work for me. Maybe I misunderstood it. Here is my function:
function check_username()
{
return $.post(url, {data}); //post the input username to sever to see whether it exists. I omitted something for simplicity here
}
check_username().done(
function(data){
if(data=="true")//username doesn't exist
return true;
else if(data=="false")
return false;
}).fail(function(){});
Basically, I want check_username to return a boolean value, and the value (true or false) will determine whether the form is going to be submitted or not.
The result is that "check_username.done()" is implemented even if I don't click "submit" (so that it should not fire check_username) . And the the form is always submitted no matter whether the username exists or not. What I want is, if the username exists, check_username will return false so that the form will not be submitted.
could anyone tell me how to solve the problem? Thanks for your attention!
$.post doesn't return a boolean value, so the result (a jqXHR object) will be converted to a boolean and be true, and thus the form will submit every time.
Update:
Since the submit() method will trigger the onsubmit event even when it's invoked manually, you'll need to store the successful results from the $.post and add a condition in check_username().
One of many possible ways to accomplish this:
<form action="register/sumbit" method="post" onsubmit="return check_username()">
...
</form>
var isUsernameValid = false;
function check_username()
{
if(isUsernameValid){
return true;
}else{
$.post(url, data, successHandler);
return false;
}
}
...
// in your $.post().done() handler...
if(data=="true"){ //username doesn't exist
isUsernameValid = true;
$("form").submit();
}
It's never going to work that way. You're not going to get a synchronous return from an asynchronous operation.
What you can do is have the handler in check_username() submit the form on success.
<form action="register/sumbit" method="post" onsubmit="return check_username(this);">
...
</form>
function check_username(frm)
{
var myform = $(frm);
if (myform.data('checked')) // already checked username? go ahead and submit
return true;
// otherwise, start the username check then return false
// initialize url, data, etc.
$.post(url, data,
function(data) {
if (data == "true")
{
myform.data('checked', true);
myform.submit();
}
}
);
return false;
}

Calling multiple action methods (using ajax) and showing the result of last in a new tab

I have a form in which I need to call two action methods, one after the other. This is how the flow goes.
First I check if the prerequisite data is entered by the user. If not then I show a message that user needs to enter the data first.
If all the prerequisite data is entered, I call an action method which return data. If there is no data returned then I show a message "No data found" on the same page.
If data is returned then I call another action method present in a different controller, which returns a view with all the data, in a new tab.
The View:
#using (Ajax.BeginForm("Index", "OrderListItems", null, new AjaxOptions { OnBegin = "verifyRequiredData"}, new { #id = "formCreateOrderListReport", #target = "_blank" }))
{
//Contains controls and a button
}
The Script in this View:
function verifyRequiredData() {
if ($("#dtScheduledDate").val() == "") {
$('#dvValidationSummary').html("");
var errorMessage = "";
errorMessage = "<span>Please correct the following errors:</span><ul>";
errorMessage += "<li>Please enter Scheduled date</li>";
$('#dvValidationSummary').append(errorMessage);
$('#dvValidationSummary').removeClass('validation-summary-valid').addClass('validation-summary-errors');
return false;
}
else {
$('#dvValidationSummary').addClass('validation-summary-valid').removeClass('validation-summary-errors');
$('#dvValidationSummary').html("");
$.ajax({
type: "GET",
url: '#Url.Action("GetOrderListReport", "OrderList")',
data: {
ScheduledDate: $("#dtScheduledDate").val(),
Crews: $('#selAddCrewMembers').val(),
Priorities: $('#selPriority').val(),
ServiceTypes: $('#selServiceTypes').val(),
IsMeterInfoRequired: $('#chkPrintMeterInfo').val()
},
cache: false,
success: function (data) {
debugger;
if (data !== "No data found") {
//var newUrl = '#Url.Action("Index", "OrderListItems")';
//window.open(newUrl, '_blank');
return true;
} else {
//Show message "No data found"
return false;
}
}
});
return false;
}
}
The "GetOrderListReport" Action method in "OrderList" Controller:
public ActionResult GetOrderListReport(OrderListModel model)
{
var contract = new OrderReportDrilldownParamDataContract
{
ScheduledDate = model.ScheduledDate
//Setting other properties as well
};
var result = OrderDataModel.GetOrderList(contract);
if (string.IsNullOrWhiteSpace(result) || string.IsNullOrEmpty(result))
{
return Json("No data found", JsonRequestBehavior.AllowGet);
}
var deserializedData = SO.Core.ExtensionMethods.DeserializeObjectFromJson<OrderReportDrilldownDataContract>(result);
// send it to index method for list
TempData["DataContract"] = deserializedData;
return Json(deserializedData, JsonRequestBehavior.AllowGet);
}
The last action method present in OrderListItems Controller, the result of which needs to be shown in a new tab:
public ActionResult Index()
{
var deserializedData = TempData["DataContract"] as OrderReportDrilldownDataContract;
var model = new OrderListItemViewModel(deserializedData);
return View(model);
}
The problem is that I am not seeing this data in a new tab, although I have used #target = "_blank" in the Ajax.BeginForm. I have also tried to use window.open(newUrl, '_blank') as can be seen above. But still the result is not shown in a new tab.
Please assist as to where I am going wrong?
If you are using the Ajax.BeginForm you shouldn't also be doing an ajax post, as the unobtrusive ajax library will automatically perform an ajax post when submitting the form.
Also, if you use a view model with data annotation validations and client unobtrusive validations, then there would be no need for you to manually validate the data in the begin ajax callback as the form won't be submitted if any validation errors are found.
The only javascript code you need to add in this scenario is a piece of code for the ajax success callback. That will look as the one you currently have, but you need to take into account that opening in new tabs depends on the browser and user settings. It may even be considered as a pop-up by the browser and blocked, requiring the user intervention to allow them as in IE8. You can give it a try on this fiddle.
So this would be your model:
public class OrderListModel
{
[Required]
public DateTime ScheduledDate { get; set; }
//the other properties of the OrderListModel
}
The form will be posted using unobtrusive Ajax to the GetOrderListReport of the OrderList controller. On the sucess callback you will check for the response and when it is different from "No data found", you will then manually open the OrderListItems page on a new tab.
This would be your view:
#model someNamespace.OrderListModel
<script type="text/javascript">
function ViewOrderListItems(data){
debugger;
if (data !== "No data found") {
var newUrl = '#Url.Action("Index", "OrderListItems")';
//this will work or not depending on browser and user settings.
//passing _newtab may work in Firefox too.
window.open(newUrl, '_blank');
} else {
//Show message "No data found" somewhere in the current page
}
}
</script>
#using (Ajax.BeginForm("GetOrderListReport", "OrderList", null,
new AjaxOptions { OnSucces= "ViewOrderListItems"},
new { #id = "formCreateOrderListReport" }))
{
#Html.ValidationSummary(false)
//input and submit buttons
//for inputs, make sure to use the helpers like #Html.TextBoxFor(), #Html.CheckBoxFor(), etc
//so the unobtrusive validation attributes are added to your input elements.
//You may consider using #Html.ValidationMessageFor() so error messages are displayed next to the inputs instead in the validation summary
//Example:
<div>
#Html.LabelFor(m => m.ScheduledDate)
</div>
<div>
#Html.TextBoxFor(m => m.ScheduledDate, new {id = "dtScheduledDate"})
#Html.ValidationMessageFor(m => m.ScheduledDate)
</div>
<input type="submit" value="Get Report" />
}
With this in place, you should be able to post the data in the initial page using ajax. Then based on the response received you will open another window\tab (as mentioned, depending on browser and user settings this may be opened in a new window or even be blocked) with the second page content (OrderListItems).
Here's a skeleton of what I think you are trying to do. Note that window.open is a popup though and most user will have popups blocked.
<form id="formCreateOrderListReport">
<input type="text" vaule="testing" name="id" id="id"/>
<input type="submit" value="submit" />
</form>
<script type="text/javascript">
$('#formCreateOrderListReport').on('submit', function (event) {
$.ajax({
type: "POST",
url: '/home/test',
data: { id: $('#id').val()},
cache: false
}).done(function () {
debugger;
alert("success");
var newUrl = '/home/contact';
window.open(newUrl, '_blank');
}).fail(function () {
debugger;
alert("error");
});
return false;
});
</script>
Scale down the app to get the UI flow that you want then work with data.

Trying to check each form input and blank its default value in jquery ajaxform()

I am using the ajaxform() plugin, which so far is working well. However, my input fields have default values, and if the user just submits the untouched form, I need to blank them out before the form is submitted using the beforeSubmit: callback.
In nutshell, I don't know the syntax to check the forms input fields and stop the submit if necessary. I have an idea its using the each() method and this.defaultValue, and maybe a return false? but I'm not sure of the details.
Could anyone perhaps give me an idea? Thanks. Heres my code so far, its the checkValues() function that I'm stuck with.
$(document).ready(function(){
//========= Functions =========
function styleForm() {
$('.quickcontact label').hide();
$('input[type="text"],textarea').addClass("idleField");
$('input[type="text"],textarea').focus(function() {
$(this).removeClass("idleField").addClass("focusField");
if (this.value == this.defaultValue){
this.value = '';
}
if(this.value != this.defaultValue){
this.select();
}
});
$('input[type="text"],textarea').blur(function() {
$(this).removeClass("focusField").addClass("idleField");
if ($.trim(this.value) == ''){
this.value = (this.defaultValue ? this.defaultValue : '');
}
});
}
//options for ajaxform() function
var options = {
target: '.quickcontactDisplay', // target element(s) to be updated with server response
beforeSubmit: checkValues, // pre-submit callback
success: reBind // post-submit callback
// other available options:
//url: url // override for form's 'action' attribute
//type: type // 'get' or 'post', override for form's 'method' attribute
//dataType: null // 'xml', 'script', or 'json' (expected server response type)
//clearForm: true // clear all form fields after successful submit
//resetForm: true // reset the form after successful submit
// $.ajax options can be used here too, for example:
//timeout: 3000
};
//rebinds the ajax functionality to updated form html
function reBind() {
// re-do the form, as it has just been replaced
$('form.quickcontact').ajaxForm(options);
styleForm();
}
//checks for default values of form on submit to prevent them being submitted
function checkValues(){
}
// ==== logic =====
$('form.quickcontact').ajaxForm(options);
styleForm();
});
And my form html:
<form action="/enquiries/add" method="post" id="EnquiryAddForm" class="quickcontact">
<input type="hidden" value="POST" name="_method"/>
<input type="hidden" id="EnquiryVisitorId" value="276" name="data[Enquiry][visitor_id]"/>
<input type="text" id="EnquiryName" maxlength="200" value="Your name" name="data[Enquiry][name]"/>
<input type="text" id="EnquiryEmailAddress" maxlength="200" value="Your Email" name="data[Enquiry][emailAddress]"/>
<textarea id="EnquiryEnquiry" rows="6" cols="30" name="data[Enquiry][enquiry]">Your Email Address</textarea>
<input type="submit" value="Ok, I'm done"/>
</form>
You are abusing the default value as a label. This is causing you problems. Rather then trying to work around those problems, I suggest fixing the cause instead.
When setting default values — set default values. Don't use the default value as a pseudo-label. Use a <label> element instead.
Haven't you looked at the documentation?
beforeSubmit:
Callback function to be invoked before the form is submitted. The
'beforeSubmit' callback can be
provided as a hook for running
pre-submit logic or for validating the
form data. If the 'beforeSubmit'
callback returns false then the form
will not be submitted. The
'beforeSubmit' callback is invoked
with three arguments: the form data in
array format, the jQuery object for
the form, and the Options Object
passed into ajaxForm/ajaxSubmit. The
array of form data takes the following
form:
[ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
Default value: null
Here the idea, didn't check it yet.
function checkValues(formData, jqForm, options)
{
for( var i in formData)
if ( formData[i].value == "")
return false;
return true;
}
sounds as if you need to:
run through all the inputs / textarea at the start and grab the default values, then stick it into an associative array with the element id as key
within checkValues, iterate through inputs once again and compare the pre-submit value against your array - when finding a match, you can set the value to "".

Resources