async js validation form - javascript

Good morning !
Currently I am trying to create some kind of simple validation using javascript, but I have a problem with using async functionalities.
Below you can see UI method which iterates through validityChecks collections
checkValidity: function(input){
for(let i = 0; i<this.validityChecks.length; i++){
var isInvalid = this.validityChecks[i].isInvalid(input);//promise is returned
if(isInvalid){
this.addInvalidity(this.validityChecks[i].invalidityMessage);
}
var requirementElement = this.validityChecks[i].element;
if(requirementElement){
if(isInvalid){
requirementElement.classList.add('invalid');
requirementElement.classList.remove('valid');
}else{
requirementElement.classList.remove('invalid');
requirementElement.classList.add('valid');
}
}
}
}
Below is specific collection object which is not working as it was intended
var usernameValidityChecks = [
{
isInvalid: function(input){
var unwantedSigns = input.value.match(/[^a-zA-Z0-9]/g);
return unwantedSigns ? true:false;
},
invalidityMessage:'Only letters and numbers are allowed',
element: document.querySelector('.username-registration li:nth-child(2)')
},
{
isInvalid: async function (input){
return await checkUsername(input.value);
},
invalidityMessage: 'This value needs to be unique',
element: document.querySelector('.username-registration li:nth-child(3)')
}
]
function checkUsername (username) {
return new Promise ((resolve, reject) =>{
$.ajax({
url: "check.php",
type: "POST",
data: { user_name: username },
success: (data, statusText, jqXHR) => {
resolve(data);
},
error: (jqXHR, textStatus, errorThrown) => {
reject(errorThrown);
}
});
});
}
The problem is that to var isInvalid in checkValidity method is returned promise. Can anyone advice how to returned there value instead of promise ? Or how to handle promise there ?
Thank you in advance!
EDIT :
Forgive me, but it is my first time with Stack and probably my question is a little bit inaccurate. Some of objects in collections are also synchronous, I changed usernameValidityChecks. How to handle this kind of situation ? Where I have async and sync method at the same time ?

Instead of below line
var isInvalid = this.validityChecks[i].isInvalid(input);
you can use below code to make code much cleaner and store the desired value on the variable however above suggested solutions are also correct
var isInvalid = await this.validityChecks[i].isInvalid(input).catch((err) => { console.log(err) });
After this line you will get the desired value on isInvalid variable
And also add async keyword in all other isInvalid function definition

Use .then to access the result of a promise:
var isInvalid = this.validityChecks[i].isInvalid(input).then(res => res);

Related

API Call is going after providing conditions in JavaScript

I am trying to make API call and sending storeId and consultuntId to backend, its working fine.
This is the code:-
const urlParams = new URL(window.location.href).searchParams;
const live_shopping_events_parameter = Object.fromEntries(urlParams);
$.ajax({
type:'POST',
url: '/aos/lscommission',
data: {
storeId: live_shopping_events_parameter.r,
consultuntId: live_shopping_events_parameter.z,
},
success: function (){
},
error: function(error) {
},
});
But I need to check two condition
the url fields exist and are valid
non-empty numeric for storeId and consultantId
This is the Example URL - https://www.example.com/live-shopping-events?r=MDQyMDE%3d&z=56
Where r is is the storeId and z is the consultuntId.
This is my code to check non-empty numeric for storeId and consultantId
const urlParams = new URL(window.location.href).searchParams;
const live_shopping_events_parameter = Object.fromEntries(urlParams);
if(live_shopping_events_parameter !== 'undefined' || live_shopping_events_parameter !== null ) {
$.ajax({
type:'POST',
url: '/aos/lscommission',
data: {
storeId: live_shopping_events_parameter.r,
consultuntId: live_shopping_events_parameter.z,
},
success: function (){
},
error: function(error) {
},
});
}else{
console.log("error");
}
for this code if I remove parameters (https://www.example.com/live-shopping-events), then still it is making API call.
How to solve this issue.
You are trying to condition in the whole object returning from Object.entries which will always return an object. That is why your condition is becoming true always. You should try to factor in the property you are trying to find, like this:
if (live_shopping_events_parameter.r !== undefined || live_shopping_events_parameter.z !== undefined) {
/* You code here */
}
SOULTION EXPLAINATION:
Thats because irrespective if there are url params or not the Object.fromEntries(urlParams) will return an object. If you want to check if params exist you need to check individually like live_shopping_events_parameter.r, this will return true is param exists and false if param is absent and make API call only when it is true.
SOULTION CODE:
const urlParams = new URL(window.location.href).searchParams;
const live_shopping_events_parameter = Object.fromEntries(urlParams);
if(live_shopping_events_parameter.r && live_shopping_events_parameter.z) {
//Params exist
//Make API call here..
console.log('Calling API')
}else{
// No params found
console.log('Params missing')
}

how to get return value of a non-blocking function with an await on it

Lets say i have the following function:
async function commentMediaId(ig, media_id, commentContent, commentIfAlreadyCommented = false, extraInfo = new Object(), callback) {
try {
let alreadyExists = ig.db.get('comments').find({media_id: media_id}).value();
alreadyExists == undefined ? false : true;
if(alreadyExists && !commentIfAlreadyCommented) {
console.log('Already commented'.yellow);
return "already_commented";
}
if(commentIfAlreadyCommented || !alreadyExists){
await ig.media.comment({
module_name: 'profile',
mediaId: media_id,
text: commentContent,
});
return callback('success');
}
} catch (e) {
return callback('not success');
}
}
then I am calling the following function (i do not want to use await here because it will block):
commentMediaId(object, 'someid', 'some string');
how can i seet a callback on commentMediaId such that when it returns i get the value of success or not success ?
I basically wanted to do as follows
commentMediaId(object, 'someid', 'some string', function(result) {
if (result == 'success')
});
but I am getting a bunch of syntax error.. any ideas on how to implement it ?
When you use an async function, you are working with the Promise interface, which exposes a .then() method. You can pass a callback into this method, and the callback will automatically be invoked for you when a value is returned from the commentMediaId function:
commentMediaId(object, 'someid', 'some string')
.then(function (result) {
console.log(result);
})
;
Finally, you need not take in a callback parameter in the commentMediaId function. You can omit the callback and simply return the values directly. That is the whole idea of the async/await "syntactic sugar", it makes it feel more like a regular function without the need for callbacks.
E.g. the following:
return callback('success');
...should be changed to the following:
return 'success';
it looks like you have some typos - asyn should be async and you also have an extra parenthesis in the last argument to the function commentMediaId(...otherArgs, callback(){} instead of commentMediaId(...otherArgs, callback){}

Need help understanding the scope of this javascript variable

In javascript, within a VueJS SPA, I'm trying to create a method that will allow me to reduce redundant code by passing the Google Maps Places Service only a place_id and the fields I would like returned.
getPlaceDetails (place, fields) {
this.$refs.mapRef.$mapPromise.then((map) => {
var placesServices = new window.google.maps.places.PlacesService(map)
placesServices.getDetails({ placeId: String(place.place_id), fields: fields }, (result, status) => {
if (status === window.google.maps.places.PlacesServiceStatus.OK) {
alert(JSON.stringify(result))
return result
}
})
})
}
I'm calling the above method from within another method:
var place = this.getPlaceDetails(place, ['name', 'geometry', 'place_id'])
It is invoked successfully... and the alert shows the desired JSON.. but place is null. I've tried using
var vm = this
above
var placesServices
and assigning the result to an app level variable... even inside of a .then after the first promise... like so:
getPlaceDetails (place, fields) {
this.$refs.mapRef.$mapPromise.then((map) => {
var vm = this
var placesServices = new window.google.maps.places.PlacesService(map)
placesServices.getDetails({ placeId: String(place.place_id), fields: fields }, (result, status) => {
if (status === window.google.maps.places.PlacesServiceStatus.OK) {
alert(JSON.stringify(result))
vm.tempPlace = result
}
})
}).then(function () {
return this.tempPlace
})
}
How can I get the method to return the result object??
Promises
A promise is an object that will resolve (or reject) in some point in the future. This is necessary to execute asynchronous tasks (e.g. http-calls) that take an undefined amount of time to finish.
Promises can be chained i.e. get executed one after another. This is what the .then method does. With .then you pass a function that will be executed as soon as the promise is finished. This function will receive the object that was returned by the previous promise.
Your method
getPlaceDetails (place, fields) {
return this.$refs.mapRef.$mapPromise.then((map) => {
var vm = this;
var placesServices = new window.google.maps.places.PlacesService(map);
placesServices.getDetails({ placeId: String(place.place_id), fields: fields }, (result, status) => {
if (status === window.google.maps.places.PlacesServiceStatus.OK) {
alert(JSON.stringify(result));
return result;
}
});
});
}
This Method will return a promise that - at some point in the future - will yield the desired result.
When you want to call the method you get that promise and have to handle it, again by passing a function (using .then) that will be executed once the result is ready.
this.getPlaceDetails(...).then((result) => {
// handle your result
}}
Alternatively you could use the await operator to wait until the promise is finished:
var place = await this.getPlaceDetails(...);
Instead of returning the data, you may consider assigning the JSON to a Vue watched data variable, like so:
var somePlace = new Vue({
el: '#someEl',
data: {
message: 'Hello robwolf.io',
tempPlace: {} // <- variable waiting for your asynchronous data if it comes thru
},
methods: {
getPlaceDetails (place, fields) {
// [...your promise code here...]
}).then((result) => {
this.tempPlace = JSON.stringify(result)
// return this.tempPlace
})
// [...the .then function must also be an arrow function to have scope access to tempPlace...]

Access Backbone fetch parameters

I need to be able to pass the server some info when calling fetch on a Backbone collection. On my front-end I have this code, passing data:{} to the fetch function along with the success and error callbacks. Looking at the docs for Backbone fetch:
http://backbonejs.org/#Collection-fetch
perhaps I am not passing in the parameters correctly to the fetch function? Perhaps they should be in an array []...
var lineupCollection = new LineupCollection([]);
var loadTeamsOnPageLoad = (function() {
lineupCollection.url = '/api/lineups';
lineupCollection.fetch(
{
processData: true,
data: {
team_id:$("#team_id").attr("value")
}
},
{
success: function (result) {
if(window.teamDashboardTableView === undefined){
window.teamDashboardTableView = new TeamDashboardTableView({el: $("#team-dashboard-table-div")});
}
window.teamDashboardTableView.render();
},
error: function (err) {
setTimeout(function () {
alert('error fetching lineupCollection - ' + err.message);
}, 1);
}
}
);
})();
which will load a Backbone collection into a table.
It's call this method on the back-end:
exports.getBackboneLineups = function(req,res,next){
var user_id = req.mainUser._id;
console.log('req.params',req.params); //empty {}
console.log('req.team',req.team); //undefined
console.log('team_id',req.team_id); //undefined
console.log('req.data',req.data); //undefined
console.log('req.body',req.body); //empty {}
var team_id = req.team._id; //undefined EXCEPTION, can't read property _id of undefined
var system_db = req.system_db;
var Lineup = LineupModel.getNewLineup(system_db,user_id);
Lineup.find({team_id:team_id}, function (err, lineups) {
if (err) {
return next(err);
}
res.json(lineups);
});
};
however, I am totally failing as far as how to access the data that I supposedly past to the server from the client. I can't really find a good example and I am getting tired of guessing. What's the best way to do this?
edit:
also tried passing {data:{}, success: function(), error: function()} all in the same JavaScript object, but that didn't work.
fetch only takes one argument (see http://backbonejs.org/#Collection-fetch). Instead, you should probably alter your backend to specify the team id in the resource's URL. For example:
lineupCollection.url = function(){ return '/api/lineups?team_id=' + this.team_id; };
lineupCollection.team_id = $("#team_id").attr("value");
lineupCollection.fetch({ success: ..., error: ...});

Angular construct ViewModel from different promises

In my Angular project i have a Promise that returns an array of Products:
{"Products":[{"Code":123},{"Code":456}]}
Then for each product code, i must call two other promises that return Price and Quantity respectively.
I use ui-router and my current implementation code in my resolve is below :
$stateProvider.state('root.products', {
abstract: true,
url: '/products',
template: '<div data-ui-view=""></div>',
resolve: {
products: ['ProductsService', function (ProductsService) {
return ProductsService.getProducts()
.then(function (response){
var data = response.data;
return data.Products.map(function (product) {
var viewModelProduct = {};
angular.copy(product, viewModelProduct);
//get Price for Each Product
ProductsService.getPrice(product.Code)
.then(function (response) {
viewModelProduct.Price = response.data.Info.Price;
})
//get Quantity for Each Product
ProductsService.getQuantity(product.Code)
.then(function (response) {
viewModelProduct.Quantity = response.data.Info.Quantity;
})
return viewModelProduct;
});
});
}]
}
})
It works but my question is if i can write it better. I read about the use of $q and $q.all but I don't really know how to use them.
Is there any way i can write the above code, better and safer??
Thanks in advance for your help!
It works but my question is if i can write it better.
Yes. You can write promise chain. The advantage is: Error propagates, you can catch it on the end of the chain and its not piramide like async call in Javascript
See flow example bellow:
[EDIT]
if the order is not critical you can use $q.all that gets list of promises and notify you when all of them resolved successfully.
It should be something like:
$q.all([
ProductsService.getPrice(product.Code),
ProductsService.getQuantity(product.Code)
])
.then(function(result){
viewModelProduct.Price = result[0].data.Info.Price;
iewModelProduct.Quantity = result[1].data.Info.Quantity;
}, function(error) {
alert(error.message);
});
or even:
var promises = [];
promises.push(ProductsService.getPrice(product.Code));
promises.push(ProductsService.getQuantity(product.Code));
$q.all(promises).then(function(result){
viewModelProduct.Price = result[0].data.Info.Price;
iewModelProduct.Quantity = result[1].data.Info.Quantity;
}, function(error) {
alert(error.message);
});
If you need to execute promises one after other you will use chains like Maxim mentioned.
More one topic:
https://docs.angularjs.org/api/ng/service/$q
http://solutionoptimist.com/2013/12/27/javascript-promise-chains-2/
If you need to execute more promises and then resolve it you will use "$q.all".
See documenttion:
https://docs.angularjs.org/api/ng/service/$q
UPDATE
Not sure if I understand well but you should use combination of q.all and chaining.
Again, sorry if I didn't understand you well.
var p1 = ProductsService.getPrice(product.Code)
.then(function (response) {
return response.data.Info.Price;
});
var p2 = ProductsService.getQuantity(product.Code)
.then(function (response) {
return response.data.Info.Quantity;
});
return $q.all([p1,p2]); // next "then" will get array with price and quantity respectively.

Categories

Resources