I am sending an AJAX request to my backend application using the following code:
function add() {
ajax('add', 'POST', {
content: ['abc', 'xyz,123']
});
}
function ajax(endpoint, method, payload, callback) {
const xhttp = new XMLHttpRequest();
const url = 'http://localhost:3000/' + endpoint;
xhttp.onreadystatechange = function() {
console.log('readyState: ', this.readyState);
if (this.readyState === 4) {
if (this.status === 200) {
console.log('success!');
if (callback) {
callback();
}
} else {
console.log('error: ', JSON.stringify(this));
}
}
};
xhttp.open(method, url, true);
xhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
if (payload) {
xhttp.send(new URLSearchParams(payload));
} else {
xhttp.send();
}
}
As you can see, I am passing an array of strings to the ajax function. The function formats the array using URLSearchParams() before sending it with the request. This results in a url formatted string of parameters that looks like this:
content=abc,xyz,123
But you'll notice that the original array consisted of only two strings, one of which was "xyz,123". Because of the comma, however, the url formatted string ends up looking like 3 strings: "abc", "xyz", "123".
The backend needs a way of distinguishing between the case where there really are n original strings and the case where there are < n original strings, one or more of which contain commas. Is there a different way of formatting the array of strings other than using URLSearchParams(), or is there something I can do with commas in the strings before calling URLSearchParams()? Or is there another way of accomplishing my goal?
Thanks.
Related
I want to make a bot command that allows the user to get a random image of their choice. (!image dog)
How do I let the user input the image that they want to be searched? This is what I have so far:
client.on("message", (message) => {
let args = message.content.substring(prefix.length).split(" ");
switch (args[0]) {
case "dog":
image(message);
break;
}
});
function image(message) {
var options = {
url: "http://results.dogpile.com/serp?qc=images&q=" + "dog",
method: "GET",
headers: {
Accept: "text/html",
"User-Agent": "Chrome",
},
};
request(options, function (error, response, responseBody) {
if (error) {
return;
}
$ = cheerio.load(responseBody);
var links = $(".image a.link");
var urls = new Array(links.length).fill(0).map((v, i) => links.eq(i).attr("href"));
console.log(urls);
if (!urls.length) {
return;
}
// Send result
message.channel.send(urls[Math.floor(Math.random() * urls.length)]);
});
}
Your current code is on the right track to doing what you want. You simply need to properly use your args variable.
The Problem(s)
First of all, you need to separate your args from your command. The code you provided in your question currently counts the command itself ("image") as the first arg and "dog" as the second arg, whereas "dog" is supposed to be the first argument of the command. So we need to separate the command from the args, and we can do so easily with Array.shift() which removes the first element of an array and returns it.
In your current code, you have a switch statement that checks the value of the first arg in the message (args[0]), and if the first arg is "dog", you are searching for dog images. But what you want to do is you want to be able to search for more than just dogs, for whatever image the user wants. That means you'll first need to get rid of the argument-checking switch, because as it is now it will only allow users to search for "dog".
Next, you need to realize that you want to search for an image of whatever the user puts in. So what's the value that the user is putting in? It's contained in args[0]. So if you want to be able to search for whatever args[0] is, instead of doing "http://results.dogpile.com/serp?qc=images&q=" + "dog" you need to instead do "http://results.dogpile.com/serp?qc=images&q=" + args[0]. In order to do that, you'll also need to give your image method access to args[0], which you can do by passing it in as an argument of the method.
The Solution
So here's how these simple fixes will look in your code:
client.on("message", (message) => {
let args = message.content.substring(prefix.length).split(" ");
let command = args.shift(); //separates and removes command from args
switch (command) {
case "image":
image(message, args[0]);
break;
}
});
function image(message, query) {
//'query' now represents 'args[0]'
var options = {
url: "http://results.dogpile.com/serp?qc=images&q=" + query,
method: "GET",
headers: {
Accept: "text/html",
"User-Agent": "Chrome",
},
};
request(options, function (error, response, responseBody) {
if (error) {
return;
}
$ = cheerio.load(responseBody);
var links = $(".image a.link");
var urls = new Array(links.length).fill(0).map((v, i) => links.eq(i).attr("href"));
console.log(urls);
if (!urls.length) {
return;
}
// Send result
message.channel.send(urls[Math.floor(Math.random() * urls.length)]);
});
}
However, note that this will only take the first argument and search for it (it will not include spaces). So if you did !image furry cats, it would only look up images of "furry" and not "furry cats". If you want to account for spaces, then use all of the args instead of just args[0]. You could do so by replacing all occurrences of args[0] in this code with args.join(" ").
Let me know if I misunderstood your question. This answer is solely based off of the somewhat limited information you provided in your question and comment.
I'm trying to split data into chunks, which are then sent to Python flask via AngularJS $http.post calls. On the JS side, it seems that the requests are being issued in the given order. But on the Python side, the requests don't seem to be arriving with the intended order.
The temporary code looks something like this:
JS:
var xdata = ["0", "1", "2", "3", "4", "***__END__***"].map(x => JSON.stringify(x));
var post_chain = [];
for (var i = 0; i < xdata.length; i++) {
post_chain.push($http.post("/path", xdata[i]));
}
post_chain[0].then(function (response0) {
console.log("response0");
return post_chain[1];
}).then(function (response1) {
console.log("response1");
return post_chain[2];
}).then(function (response2) {
console.log("response2");
return post_chain[3];
}).then(function (response3) {
console.log("response3");
return post_chain[4];
}).then(function (response4) {
console.log("response4");
return post_chain[5];
}).then(function (response) {
console.log('Done');
// Handle response
});
In Python, I use flask.request.get_json(silent=False) to get the input data. There's some code to detect a termination criterion (e.g., given by the string "***__END__***") signaling the end of posts from JS. This is why I need to ensure the order of the requests from JS to Python. I also print out the received data.
The console output in JS looks fine (it shows 0, 1, 2, etc. in the given order). But in Python, the printed data is out of order (0, 4, 1, etc.).
Later on, I intend to generalize the JS code with a recursive function, for example. But for now, I'm trying to guarantee that the Python code receives data in the correct order.
Any suggestions?
UPDATE 1
The suggestions work! However, the ideal approach is to generalize the code for any array size. I think this can be done with recursion, but it doesn't seem to be passing the data in the right order. What's missing?
var post_chain_call = function(i, post_element) {
if (i == post_chain.length - 1) {
return post_element(xdata[i]).then(function (response) {
// Handle response
});
} else {
return post_element(xdata[i]).then(post_chain_call(i + 1, post_chain[i + 1]));
}
}
post_chain_call(0, post_chain[0]);
UPDATE 2
Got it to work with another approach!
var post_chain_call = function(i, post_element) {
if (i == post_chain.length - 1) {
return post_element.then(function (response) {
// Handle response
});
} else {
return post_element.then(function (response_tmp) {
return post_chain_call(i + 1, post_chain[i + 1](xdata[i + 1]));
});
}
}
post_chain_call(0, post_chain[0](xdata[0]));
The moment your front-end starts communicating with the back-end is when you're assigning them to postChain. This means that each of the requests on their own are attempting to communicate with the server, and the order in which they connect to the server cannot be predicted. A possible workaround to make sure a request is only instantiated in the .then() of the previous request would be to not store the result of the $http.post() call in the chain, but instead a function that executed $http.post() and returns that promise.
var xdata = ["0", "1", "2", "3", "4", "***__END__***"].map(x => JSON.stringify(x));
var post_chain = [];
for (var i = 0; i < xdata.length; i++) {
// this way, the code is not yet executed, but it will when you call it.
post_chain.push((data) => $http.post("/path", data));
}
post_chain[0](xdata[0]).then(function (response0) {
console.log("response0");
return post_chain[1](xdata[1]);
}).then(function (response1) {
console.log("response1");
return post_chain[2](xdata[2]);
}).then(function (response2) {
console.log("response2");
return post_chain[3](xdata[3]);
}).then(function (response3) {
console.log("response3");
return post_chain[4](xdata[4]);
}).then(function (response4) {
console.log("response4");
return post_chain[5](xdata[5]);
}).then(function (response) {
console.log('Done');
// Handle response
});
You can also use arrow functions to make sure xdata is still in the function scope and you don't need to pass any arguments to it
var post_chain = [];
for (var i = 0; i < xdata.length; i++) {
// this way, the code is not yet executed, but it will when you call it.
post_chain.push(() => $http.post("/path", xdata[i]));
}
post_chain[0]().then((response0) => {
console.log("response0");
return post_chain[1]();
}).then((response1) => {
console.log("response1");
return post_chain[2]();
}).then((response2) => {
console.log("response2");
return post_chain[3]();
}).then((response3) => {
console.log("response3");
return post_chain[4]();
}).then((response4) => {
console.log("response4");
return post_chain[5]();
}).then((response) => {
console.log('Done');
// Handle response
});
I hope this helps.
I have this array called fieldlist which is coming from the below http.get request in angular 2 component.
I have declared it like this .
fieldlist: string[] = [];
then I assigned its value using iteration of json resposne.
this.http.get(getform_endpoint,requestOptions).map((res:
Response) => res.json()).subscribe(
res => {
this.FormData = res.schema;
res.fields.forEach(element => {
this.fieldlist.push(element);
});
});
Now in another function like this
create_hidden_qp() {
let elementsnamevalue = this.fieldlist.join();
console.log("hello", this.fieldlist.join());
}
when I convert it to string using this lines it gives me blank "" response
but when I print it like this
console.log("hello", this.fieldlist);
I am able to see the perfect array like this.
hello[] 0 :"userroleid" 1: "ruletype" 2: "employeeid"
So what I am missing?
A) wrong declaration?
b) wrong assigment?
c) wrong access to array elements?
You should call the create_hidden_qp after your request has finished:
this.http.get(getform_endpoint,requestOptions).map(r => r.json()).subscribe(res => {
this.FormData = res.schema;
res.fields.forEach(element => {
this.fieldlist.push(element);
});
this.create_hidden_qp();
});
I am making a request like this:
fetch("https://api.parse.com/1/users", {
method: "GET",
headers: headers,
body: body
})
How do I pass query string parameters? Do I simply add them to the URL? I couldn't find an example in the docs.
Your first thought was right: just add them to the URL.
Remember you can use template strings (backticks) to simplify putting variables into the query.
const data = {foo:1, bar:2};
fetch(`https://api.parse.com/1/users?foo=${encodeURIComponent(data.foo)}&bar=${encodeURIComponent(data.bar)}`, {
method: "GET",
headers: headers,
})
Short answer
Just substitute values into the URL like this:
const encodedValue = encodeURIComponent(someVariable);
fetch(`https://example.com/foo?bar=${encodedValue}`);
Longer answer
Yes, you just need to add the query string to the URL yourself. You should take care to escape your query string parameters, though - don't just construct a URL like
`https://example.com/foo?bar=${someVariable}`
unless you're confident that someVariable definitely doesn't contain any &, =, or other special characters.
If you were using fetch outside of React Native, you'd have the option of encoding query string parameters using URLSearchParams. However, React Native does not support URLSearchParams. Instead, use encodeURIComponent.
For example:
const encodedValue = encodeURIComponent(someVariable);
fetch(`https://example.com/foo?bar=${encodedValue}`);
If you want to serialise an object of keys and values into a query string, you could make a utility function to do that:
function objToQueryString(obj) {
const keyValuePairs = [];
for (const key in obj) {
keyValuePairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]));
}
return keyValuePairs.join('&');
}
... and use it like this:
const queryString = objToQueryString({
key1: 'somevalue',
key2: someVariable,
});
fetch(`https://example.com/foo?${queryString}`);
Here's an es6 approach
const getQueryString = (queries) => {
return Object.keys(queries).reduce((result, key) => {
return [...result, `${encodeURIComponent(key)}=${encodeURIComponent(queries[key])}`]
}, []).join('&');
};
Here we're taking in a queries object in the shape of key: param
We iterate and reduce through the keys of this object, building an array of encoded query strings.
Lastly we do a join and return this attachable query string.
I did a small riff on Mark Amery's answer that will pass Airbnb's eslint definitions since many teams seem to have that requirement these days.
function objToQueryString(obj) {
const keyValuePairs = [];
for (let i = 0; i < Object.keys(obj).length; i += 1) {
keyValuePairs.push(`${encodeURIComponent(Object.keys(obj)[i])}=${encodeURIComponent(Object.values(obj)[i])}`);
}
return keyValuePairs.join('&');
}
My simple function to handle this:
/**
* Get query string
*
* #param {*} query query object (any object that Object.entries() can handle)
* #returns {string} query string
*/
function querystring(query = {}) {
// get array of key value pairs ([[k1, v1], [k2, v2]])
const qs = Object.entries(query)
// filter pairs with undefined value
.filter(pair => pair[1] !== undefined)
// encode keys and values, remove the value if it is null, but leave the key
.map(pair => pair.filter(i => i !== null).map(encodeURIComponent).join('='))
.join('&');
return qs && '?' + qs;
}
querystring({one: '##$code', two: undefined, three: null, four: 100, 'fi###ve': 'text'});
// "?one=%23%40%24code&three&four=100&fi%23%23%40ve=text"
querystring({});
// ""
querystring('one')
// "?0=o&1=n&2=e"
querystring(['one', 2, null, undefined]);
// "?0=one&1=2&2" (edited)
Yes you should, there are a few classes in JS, that can help you a handy one is https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
e.g. if you had the params in a javascript object say
let params = {one: 'one', two: 'two'}
you could say this function
let queryString = new URLSearchParams()
for(let key in params){
if(!params.hasOwnkey())continue
queryString.append(key, params[key])
}
then you can get your nicely formatted query string by saying
queryString.toString()
The accepted answer works, but if you have more params than one it doesn't generalize. I suggest the following approach, which also handles array parameters:
let route = 'http://test.url.com/offices/search';
if (method == 'GET' && params) {
const query = Object.keys(params)
.map((k) => {
if (Array.isArray(params[k])) {
return params[k]
.map((val) => `${encodeURIComponent(k)}[]=${encodeURIComponent(val)}`)
.join('&');
}
return `${encodeURIComponent(k)}=${encodeURIComponent(params[k])}`;
})
.join('&');
route += `?${query}`;
}
I have a the following code that currently works
but...
I know it is not elegant and can be done much more efficiently.
What I am trying to do is take an array of emails, search if they exist in a corresponding db with a particular templateName, and for those that do not exist (i.e are 'new'), list them back on the returned page to the user. However, they end up waiting quite a while if there are a lot of emails to check.
This is the first time I'm using async and it may not actually be the best way to do this. Some of the below has been modified from what I am actually using currently to make it easier to read/follow.
Basically, from my handler, I call the following (where both emailArray and templateName are extracted from the request parameter passed in).
var newEmails = "";
async.eachSeries(emailArray, function(entry, cb) { // check each item in array (these are the potential new emails)
utils.emailAddressAndTemplateExists(entry.toString().toLowerCase(), templateName, function (err, emailExists, templateExists ) {
if (emailExists) {
if (templateExists) {
++existingCount;
} else if (emailExists && !templateExists) {
} else {
console.log('template does not exist');
}
} else {
++newCount;
newEmails = newEmails + entry + "</br>";
}
cb();
});
//cb();
}, function (err) {
if (err) { throw err; }
var content = utils.getHTMLHead() + newEmails + utils.getHTMLClose();
utils.writeHTMLPage(response, content);
});
The utils call does the following: (the writeHTMLPage simply adds the required html tags and sends back to response).
//checks for a single email address
var emailAddressExists = function(emailAddress, callback) {
if (emailAddressCollection == null) {
//console.log("it was null " + db_singleton + " " + dbName);
emailAddressCollection = db_singleton.collection(dbName);
}
emailAddressCollection.find( { "emailAddress" : emailAddress.toLowerCase() } ).toArray( function (err, docs) {
if (err) { console.err(err); }
if (docs.length == 0) {
callback(null, false, docs.EmailsSent);
} else {
doc = docs[0];
callback(null, true, doc.EmailsSent);
}
});
}
// check for email And template
var emailAddressAndTemplateExists = function (emailAddress, templateName, callback) {
emailAddressExists(emailAddress, function (err, returnVal, templates) {
if (returnVal) {
if (templates != null) {
callback (null, true, templates.hasOwnProperty(templateName)) // email exists, checking for templateName
} else {
callback (null, true, false); // email exists, no templates at all exist
}
} else {
callback (null, false, false); // email does not exist, templates must be false
}
});
}
//creates HTML formated respnse data
function writeHTMLPage(response, content) {
// if (err) { console.error(err); response.send("Error " + err); }
response.writeHead(200, {"Content-Type": "text/html"});
response.write(content);
response.end();
}
What are more elegant and efficient way to do this?
This looks like it's constructed according to how you'd normally see it. You can look into Promises with ES6 to get a better program flow:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Basically, it lets you chain together functions. It's not too bad for what you're doing now, it might help out some, but if you have 4-5 callbacks nested together, that's when promises can be very helpful.
You'll just have to work through structuring your code differently to use Promises, but it'll make "callback hell" less of an issue.