How to make remote jquery validation for tied fields? - javascript

Let's say I have couple of input fields - their combination must be unique.
Each of them causes remote validation method triggering - and it's the same method for both fields. If combination is unique - it returns true.
The problem is following: when after validation error I change the field, that is not marked as erroneous, the erroneous field keeps being considered erroneous, even if method returns true (the couple is unique)!
I even don't need to make extra request to server, because the couple is unique! I just need to clear error for field, marked erroneous. However, I have not managed to do this - seems like jquery does not offer functionality for this.
Any ideas?
The relevant code is pretty huge, but the key parts are here:
this.clearErrors = function ($elements) {
var $validator = $elements.first().closest('form').validate();
$elements.each(function(index, item) {
var $parent = $(item).parent();
var element = $(item).get(0);
if ($parent.is('td')) {
$parent.removeClass(window.resources.errorCellClass);
}
$parent.find('span.' + window.resources.errorSpanClass).remove();
$validator.successList.push(element);
delete $validator.invalid[element.name];
delete $validator.submitted[element.name];
});
};
//Fixing remote method, since original one returned "pending" status all the time, as reported in other stackoverflow question
$.validator.addMethod('synchronousRemote', function (value, element, param) {
if (this.optional(element)) {
return 'dependency-mismatch';
}
var previous = this.previousValue(element);
if (!this.settings.messages[element.name]) {
this.settings.messages[element.name] = {};
}
previous.originalMessage = this.settings.messages[element.name].remote;
this.settings.messages[element.name].remote = previous.message;
if (typeof param == 'string') {
param = { url: param }
}
if (previous.old === value) {
return previous.valid;
}
previous.old = value;
var validator = this;
this.startRequest(element);
var data = {};
data[element.name] = value;
var valid = 'pending';
$.ajax($.extend(true, {
url: param,
async: false,
mode: 'abort',
port: 'validate' + element.name,
dataType: 'json',
data: data,
success: function (response) {
validator.settings.messages[element.name].remote = previous.originalMessage;
valid = response === true || response === 'true';
if (valid) {
var submitted = validator.formSubmitted;
validator.prepareElement(element);
validator.formSubmitted = submitted;
validator.successList.push(element);
delete validator.invalid[element.name];
validator.showErrors();
} else {
var errors = {};
var message = response || validator.defaultMessage(element, 'remote');
errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
validator.invalid[element.name] = true;
validator.showErrors(errors);
}
previous.valid = valid;
validator.stopRequest(element, valid);
}
}, param));
return valid;
});
$root.filter(':input[data-excluded-values-method]:not([readonly])').add($root.find(':input[data-excluded-values-method]:not([readonly])')).each(function () {
var $element = $(this);
$element.validate({
onkeyup: false
})
var $entityContainer = $element.closest('[data-entity]');
var $keyFields = $entityContainer.filter('INPUT[data-is-key]:not([disabled])').add($entityContainer.find('INPUT[data-is-key]:not([disabled])'));
var localizedNames = [];
$keyFields.each(function () {
localizedNames.push($(this).attr('localized-name'));
});
$element.rules('add',
{
synchronousRemote: function () {
var key = [];
var keyIsUnique = true;
$keyFields.each(function () {
key.push($(this).val());
});
return {
url: $element.attr('data-excluded-values-method'),
type: 'POST',
async: false,
data: JSON.stringify({
key: key,
entityType: $entityContainer.attr('data-entity')
}),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
dataFilter: function (isUnique) {
keyIsUnique = isUnique;
return isUnique;
},
complete: function () {
if (keyIsUnique === 'true') {
window.commonUtils.clearErrors($keyFields.filter('[name!="' + $element.attr('name') + '"]:input[data-excluded-values-method]:not([readonly])'));
}
}
}
},
messages: {
synchronousRemote: $.validator.format(window.resources.notUniqueValidationError)(localizedNames.join(' + '))
}
});
});

I've debugged jquery validate method and found what yet should be set to clear validation error:
$validator.previousValue(element).valid = true;
Now everything works.

Related

Function with AJAX run last, how to make it run first

I have one function with AJAX Call(Below Code):
function get_type(Name) {
var field_name;
$.ajax({
type: "GET",
url: "../home/Asset/MR/MR.xml",
dataType: "xml",
success: function(xml) {
$(xml).find('pub').each(function() {
if (Name == $(this).find('pro').text()) {
$(this).find('metadata field').each(function() {
field_name = $(this).find('name').text();
if (field_name == "little") {
type = "L";
} else if (field_name == "Big") {
type = "b";
}
});
}
});
}
});
}
This code works well but the problem is it run after all the functions finished. I want to run this code first I need to get data from the XML. I need to stop the loop of $(xml).find('pub').each(function() this once the Name== $(this).find('pro').text() text is matched. Because this loop execute even I get the answers.
Calling Function codes:
var rd = new FileReader();
rd.onload = function(e) {
var xmlDoc = $.parseXML(this.result);
var $xml = $(xmlDoc);
var J_Name = $xml.find('meta').text();
get_type(J_Name);
//check allowed child of front tag
check_allowed_direct_child("places", "Tirunelveli,Tiruchendur,Alwar", "RULE_002", "Fail");
};
rd.readAsText(this.files[i]);
Callbacks to the rescue!
function get_type(name, cb) {
cb = cb || function () {};
var field_name;
var type;
var types_map = {
'little': 'L',
'Big': 'b'
};
$.ajax({
type: 'GET',
url: '../home/Asset/MR/MR.xml',
dataType: 'xml',
success: function (xml) {
$(xml)
.find('pub')
.each(function () {
if (name == $(this).find('pro').text()) {
$(this)
.find('metadata field')
.each(function () {
field_name = $(this)
.find('name')
.text();
if (types_map.hasOwnProperty(field_name)) {
type = types_map[field_name];
return false; // break out of each()
}
});
return false; // break out of each()
}
});
cb(type); // execute provided callback
}
});
}
var rd = new FileReader();
rd.onload = function (e) {
var xmlDoc = $.parseXML(this.result);
var $xml = $(xmlDoc);
var J_Name = $xml.find('meta').text();
get_type(J_Name, function (type) {
// do stuff once get_type() resolves, type being either matched type or undefined
check_allowed_direct_child('places', 'Tirunelveli,Tiruchendur,Alwar', 'RULE_002', 'Fail');
});
};
rd.readAsText(this.files[i]);
If interested, read on how to make use of Promises, to make callback code a lot more digest: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
If still interested, read on how to use async / await to make Promises code a lot more digest: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Javascript not recognizing self.function when inside onclick event

I have a Javascript file that I added to. It's for a twitter plugin, and I'm adding a filter function.
This is my script (the relevant parts):
;(function ( $, window, document, undefined ) {
var pluginName = "twitterFeed",
defaults = {
username: null,
webservice_url: "/services/Scope/Country_United_States_Internet/TwitterService.svc/DoTwitterSearch",
num_tweets: 3
};
// The actual plugin constructor
function Plugin( element, options ) {
this.element = element;
this.options = $.extend( {}, defaults, options );
this._defaults = defaults;
this._name = pluginName;
this.init();
}
Plugin.prototype = {
init: function() {
//if username is unknown
if(this.options.username == null) {
// do nothing
try{console.log('twitter username not found')}catch(err){};
return false;
}
// Get the tweets to display
this.getTweets();
$(".twitter-search input").on("change", function () {
var filters = this.formatFilters($(this).val());
this.getTweets(filters);
});
},
formatFilters : function(filterString) {
var hashtags = filterString.split(" ");
var hashtagString = "";
for (var i = 0; i < hashtags.length; i++) {
var hashtag = hashtags[i];
hashtag = hashtag.replace(",", "");
if (hashtag[0] !== "#") {
hashtag = "#" + hashtag;
}
hashtagString += " " + hashtag;
}
return hashtagString;
},
getTweets : function(filters){
var self = this;
var query = "from:" + self.options.username;
if (filters) {
query += filters;
}
var post_data = JSON.stringify(
{
"PageSize" : self.options.num_tweets,
"TwitterQuery" : query
}
);
$.ajax({
type: "POST", // Change to POST for development environment
url: this.options.webservice_url,
data: post_data,
contentType: "application/json; charset=utf-8",
dataType: "json",
timeout:2000,
success: function(data) {
// render the tweets
self.renderTweets(data.ContentItems);
},
error: function(error, type){
try{console.log(type);}catch(err){}
}
});
},
I added the $(".twitter-search input") on change event (in init) and I added the formatFilters() function. However, in the onchange function, I get the error "this.formatFilters() is not defined". I tried removed this but still got "formatFilters() is not defined.
Remember that this inside of an event handler means whatever HTML element the event was activated on.
Instead, you need to keep track of the actual Plugin object, not the HTML element.
var self = this;
$(".twitter-search input").on("change", function () {
var filters = self.formatFilters($(this).val());
self.getTweets(filters);
});
The problem you are experiencing is with function scope. When you refer to this in the event handler it points to the callback scope and not the scope of your formatFilters function.
To fix it - In the init function add var self = this; on the first line and then change the call to use self.formatFilters instead of this.formatFilters

Use Parse Query in then() when using Cloud Code

I can’t seem to get this simple Parse query to work in my cloud code then() it works outside of this but when i place the code inside of this then function nothing happens. The variables are just placeholders for now in terms of testing but i have the default TestObject class you get when you start Parse from the beginning but for some reason it just keeps on returning nothing.
Here is the full function that i am currently using.
// Function which will get the data from all the links passed into the function
Parse.Cloud.define("myNews", function (request, response) {
var promises = _.map(import_io_keys, function (news_api_key) {
return Parse.Cloud.httpRequest({
method: 'GET',
url: "https://api.import.io/store/connector/" + news_api_key + "/_query?input=webpage/url:https%3A%2F%2Fwww.designernews.co%2Fnew&&_apikey=xxxxxxxxxxxxxxxxxx",
headers: {
'Content-Type': 'application/json;charset=utf-8'
}
}).then(function (httpResponse) {
result = JSON.parse(httpResponse.text);
var success = false;
var news_icon = "";
var news_source_name = "";
var query = new Parse.Query("TestObject");
query.find({
success: function(results) {
success = true;
news_icon = results[0].get("foo");
news_source_name = results[0].get("foo");
response.success("done" + news_icon);
},
error: function() {
success = false;
response.error("Query lookup failed");
}
});
for (var story in result.results) {
if(story.length > 0){
if (story["article_link/_text"] !== "" && story["article_link"] !== "" && story["article_time"] !== "") {
if(success){
// Do the stuff later
}
}
}
}
});
});
Parse.Promise.when(promises).then(function () {
console.log("Got all the stories");
response.success(newsJsonData);
}, function () {
response.error("No stories");
console.log("API KEY IS: " + request.params.keys);
});
});

jquery ajax call is not working in IE9

I am having some trouble with the code below. It will not run in IE9. It works fine in other browsers though.
I have placed an alert inside the code but that piece of code is not reached.
anyone has got any idea how to solve this issue?
NWF$.ajax({
url: 'http://pdfservice/training/',
data: JSON.stringify(dataJSON),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
type: 'POST',
cache: false,
success: function (fileName) {
alert('ok!');
window.location.href = 'http://pdfservice/training/?meeting=' + fileName;
},
error: function (result) {
alert(JSON.stringify(result));
}
});
I just changed the fail to error and this is the error that I get:
{"readyState":0,"status":0,"statusText":"No Transport"}
Jquery w Ajax for IE9 is broken.
This is supported through a jquery plugin
"Implements automatic Cross Origin Resource Sharing support using the XDomainRequest object for IE8 and IE9 when using the $.ajax function in jQuery 1.5+."
This happens because your JSON data is corrupt.
Fix your JSON data, you can use JSONLint to validate your JSON response to make sure it's valid JSON.
Old post, figured I'd add my findings from AJAX POST request on IE fails with error "No Transport"?
I'm adding the code in the event it gets deleted:
if (!jQuery.support.cors && window.XDomainRequest) {
var httpRegEx = /^https?:\/\//i;
var getOrPostRegEx = /^get|post$/i;
var sameSchemeRegEx = new RegExp('^'+location.protocol, 'i');
var xmlRegEx = /\/xml/i;
// ajaxTransport exists in jQuery 1.5+
jQuery.ajaxTransport('text html xml json', function(options, userOptions, jqXHR){
// XDomainRequests must be: asynchronous, GET or POST methods, HTTP or HTTPS protocol, and same scheme as calling page
if (options.crossDomain && options.async && getOrPostRegEx.test(options.type) && httpRegEx.test(userOptions.url) && sameSchemeRegEx.test(userOptions.url)) {
var xdr = null;
var userType = (userOptions.dataType||'').toLowerCase();
return {
send: function(headers, complete){
xdr = new XDomainRequest();
if (/^\d+$/.test(userOptions.timeout)) {
xdr.timeout = userOptions.timeout;
}
xdr.ontimeout = function(){
complete(500, 'timeout');
};
xdr.onload = function(){
var allResponseHeaders = 'Content-Length: ' + xdr.responseText.length + '\r\nContent-Type: ' + xdr.contentType;
var status = {
code: 200,
message: 'success'
};
var responses = {
text: xdr.responseText
};
try {
if (userType === 'json') {
try {
responses.json = JSON.parse(xdr.responseText);
} catch(e) {
status.code = 500;
status.message = 'parseerror';
//throw 'Invalid JSON: ' + xdr.responseText;
}
} else if ((userType === 'xml') || ((userType !== 'text') && xmlRegEx.test(xdr.contentType))) {
var doc = new ActiveXObject('Microsoft.XMLDOM');
doc.async = false;
try {
doc.loadXML(xdr.responseText);
} catch(e) {
doc = undefined;
}
if (!doc || !doc.documentElement || doc.getElementsByTagName('parsererror').length) {
status.code = 500;
status.message = 'parseerror';
throw 'Invalid XML: ' + xdr.responseText;
}
responses.xml = doc;
}
} catch(parseMessage) {
throw parseMessage;
} finally {
complete(status.code, status.message, responses, allResponseHeaders);
}
};
xdr.onerror = function(){
complete(500, 'error', {
text: xdr.responseText
});
};
xdr.open(options.type, options.url);
//xdr.send(userOptions.data);
xdr.send();
},
abort: function(){
if (xdr) {
xdr.abort();
}
}
};
}
});
};
jQuery.support.cors = true;

JavaScript Revealing Module Pattern Variable Scope

I have an issue where I can't get to a variable inside a function:
EDIT
I forgot to add that I am setting this workerPage.grid = $("#grid").data("kendoGrid"); on the jQuery $(function(){});
I can't use claimsGird variable inside the save function, I have to referenec it by workerPage.grid. Not the other variables like viewModel work fine. Here is the snippet:
save = function () {
saif.kendoGridUtils.addModifiedDataItems(
viewModel.CompanionClaims.Updated,
viewModel.CompanionClaims.Added,
$("#grid").data("kendoGrid").dataSource.data()
);
$.ajax({
url: $("#contentForm").attr("action"),
data: JSON.stringify(viewModel),
type: "POST",
contentType: "application/json"
}).success(function (data) {
//syncs viewModel with changes in model
$.extend(viewModel, kendo.observable(data));
//rebinds the grid data source
claimsGrid.dataSource.data(viewModel.CompanionClaims.Rows);
Here is the full script:
var workerPage = (function () {
var viewModel = kendo.observable(#Html.Raw(Json.Encode(Model))),
claimsGrid = null,
deleteFirm = function (firmModel) {
firmModel.Name = "";
firmModel.AttorneyName = "";
firmModel.Address.Line1 = "";
firmModel.Address.Line2 = "";
firmModel.Address.City = "";
firmModel.Address.State = "OR";
firmModel.Address.ZipCode = "";
firmModel.Address.PlusFourCode = "";
firmModel.PhoneNumber = "";
firmModel.FaxNumber = "";
firmModel.ContactName = "";
},
bind = function () {
kendo.bind($("#main-content"), viewModel);
},
save = function () {
saif.kendoGridUtils.addModifiedDataItems(
viewModel.CompanionClaims.Updated,
viewModel.CompanionClaims.Added,
$("#grid").data("kendoGrid").dataSource.data()
);
$.ajax({
url: $("#contentForm").attr("action"),
data: JSON.stringify(viewModel),
type: "POST",
contentType: "application/json"
}).success(function (data) {
//syncs viewModel with changes in model
$.extend(viewModel, kendo.observable(data));
//rebinds the grid data source
claimsGrid.dataSource.data(viewModel.CompanionClaims.Rows);
//rebinds view elements to view model so changes are visible
//kendo.bind($("#main-content"), viewModel);
bind();
// Errors and Warnings
var results = messageUtils.parseMessages(
viewModel.Messages.Errors,
viewModel.Messages.Informationals,
viewModel.Messages.Warnings
);
var errs = $("#errors").html(results.errorMessages);
$("#informationals").html(results.informationalMessages);
$("#warnings").html(results.warningMessages);
$.each(saif.kendoGridUtils.processErrors(viewModel.CompanionClaims.Rows), function (i, message) {
errs.html(errs.html() + message + "<br>");
});
// End Errors and Warnings
});
},
deleteRow = function () {
var row = claimsGrid.select(),
rowDataItem = claimsGrid.dataItem(row),
rowIndex = $(row).index(),
addedItemIndex = $.inArray(rowDataItem, viewModel.CompanionClaims.Added);
//add to Deleted if not new
if (addedItemIndex == -1 && $.inArray(rowDataItem, viewModel.CompanionClaims.Rows) != -1) {
viewModel.CompanionClaims.Deleted.push(rowDataItem);
}
//remove from Added if exists
if (addedItemIndex != -1) {
viewModel.CompanionClaims.Added.splice(addedItemIndex, 1);
}
claimsGrid.removeRow(row);
//select the next row, eg. if you delete row 2, select the row that took that rows poisition after it was deleted.
claimsGrid.select(claimsGrid.tbody.find(">tr:eq(" + rowIndex + ")"));
};
return {
bind: bind,
deleteFirm: deleteFirm,
deleteRow: deleteRow,
grid: claimsGrid,
save: save,
viewModel: viewModel
};
}());
The issue is that claimsGrid is never set to anything other than null. And setting workerPage.grid won't change the value of claimsGrid -- it's not a pointer, just a copy.
You'll instead have to use a getter/setter. With newer browsers/engines, that can be done with get and set:
// ...
return {
// ...
get grid() {
return claimsGrid;
},
set grid(grid) {
claimsGrid = grid;
},
// ...
};
You can also define grid as a function:
// ...
function getOrSetGrid(grid) {
if (typeof newGrid === 'undefined') {
return claimsGrid;
} else {
claimsGrid = grid;
}
}
return {
// ...,
grid: getOrSetGrid,
// ...
};
// ...
// rather than: workerPage.grid = ...;
workerPage.grid(...);
Or split it into getGrid and setGrid functions.
Scope in javascript works differently than other languages like Java or C#. In your case claimsGrid is not in scope for the save function. Does this help? http://coding.smashingmagazine.com/2009/08/01/what-you-need-to-know-about-javascript-scope/

Categories

Resources