using json objects in URLSearchParams - javascript

Is it possible to somehow append json objects onto a URLSearchParams object?
So instead of:
urlSearchParams.append('search', 'person');
it's:
urlSearchParams.append({search: "person"});
My answer courtesy of Darshak Gajjar's answer
Can use json objects via this way:
let test_this = [{"search": "person"}, { search: "another person"}];
var json = JSON.stringify(test_this);
urlSearchParams.append("myobj", json);
return this.http.post(this.post_url, urlSearchParams, options) //options being your own RequestOptions

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Something like this might work urlSearchParams = Object.assign(urlSearchParams, {search: "person"});
EDIT: Alternate solution using vanilla javascript. Also, I thought URLSearchParams was just a normal js object, but in fact you have to use get, set and append to access properties.
var params = new URLSearchParams("a=apple&b=balloon");
var parametersToAdd = {c: "car", d: "duck"};
for(key in parametersToAdd)
params.append(key, parametersToAdd[key]);
console.log(params.get('c'));
console.log(params.get('d'));
EDIT bis:
.append() supports to re-use the same key/parameter name, while .set() would have overwritten a previous value.

May be using below code you can pass entire json object in URL Search param
var json = JSON.stringify(myObj);
this.http.get('url'+'?myobj='+encodeURIComponent(json))

There's no API for that. You just need to enumerate over the properties and append them manually, for example using the following function:
function appendParams(params: URLSearchParams, obj: any) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
params.append(key, obj[key])
}
}
}
appendParams(urlSearchParams, { search: 'person' });

Want to share my answer for Angular2 with the option of sending an Array
This is how I use this get function:
this.get('/api/database', {
'age': [1,2,3,4]
})
And the service is something like:
get(url, _params = {}) {
let params = this._processParams(_params);
return this.http.get(url, params).toPromise();
}
_processParams(obj: any) {
/* Convert this
{ age: [1,2,3] }
To:
param.append('age', 1);
param.append('age', 2);
param.append('age', 3);
*/
let params = new URLSearchParams();
for (let key in obj) {
for (let index in obj[key] ) {
params.append(key, obj[key][index]);
}
}
return {
search: params
};
}

Super simple answer/example:
// Create:
const params = new URLSearchParams({
a: 1,
b: 2
})
// OR
// const params = new URLSearchParams("a=1&b=2")
// Append
params.append('c', 'woohoo') // Note: if c param already exists in params, this will replace it (won't be adding new param if already exists, hence no duplications)
console.log(params.toString())
// Prints: 'a=1&b=2&c=woohoo'

Here is my approach. We have a simple requirement where the object is only a key value pair where the value might be a string or an array. We haven't found a use case for nested objects.
So let's say we want to convert this object into a query string or vice versa:
const input = {
ini: 'ini',
itu: 'itu',
ayo: ['desc', 'asc'],
}
Then we have two functions to parse & stringify:
function stringify(input) {
const params = new URLSearchParams();
for (const key in input) {
if (Array.isArray(input[key])) {
input[key].forEach(val => {
params.append(key + '[]', val)
})
} else {
params.append(key, input[key]);
}
}
return '?' + params.toString();
}
function parse(input) {
const payload = {};
const params = new URLSearchParams(input);
for(let [key, val] of params.entries()) {
if (key.endsWith('[]')) {
key = key.replace(/\[\]$/, '');
if (payload[key]) {
payload[key].push(val);
} else {
payload[key] = [val]
}
} else {
payload[key] = val;
}
}
return payload;
}
So the result should be "?ini=ini&itu=itu&ayo%5B%5D=desc&ayo%5B%5D=asc". This is similar to the array format that is found in this example.
Please note that this might not be battle tested, but for us we don't really have complicated object structure.

const url = new URL('/', location.origin);
console.log(url.href); // https://stackoverflow.com/
Object.entries({this:4,that:1}).forEach((item)=>{
// note .set replaces while .append will duplicate params
url.searchParams.append(...item);
});
console.log(url.href); // https://stackoverflow.com/?this=4&that=1

Related

Validating FormData objects with Joi

Just wondering if there's a way to pass a FormData object to Joi, or whether I have to convert it to an object myself?
I couldn't see anything in the docs, but thought I'd ask as it seems strange the option isn't there
const formData = new FormData(e.target);
// data is an object of inputNames/values
validate = (data, schema) => {
const options = {
abortEarly: false
};
const result = Joi.validate(data, schema, options);
};
Obviously I could do somthing like this:
for(let pair of formData.entries()) {
formObject[pair[0]] = pair[1];
}
but it was just a QoL thing I thought I might have missed

How can I extract path parameters from url?

Let's say I have an object that contains the values:
const pathParams = { schoolId :'12', classroomId: 'j3'}
and I have a path: school/:schoolId/classroom/:classroomId
I would like to extract: 'schoolId' and 'classroomId' from the path so that I can later replace them with their corresponding values from the pathParams object. Rather than iterating from the pathParam object keys, I want to do the other way around, check what keys the path needs and then check their values in the object.
I currently have this:
function addPathParams(path, paramsMap) {
const pathParamsRegex = /(:[a-zA-Z]+)/g;
const params = path.match(pathParamsRegex); // eg. school/:schoolId/classroom/:classroomId -> [':schoolId', ':classroomId']
console.log('--params--', params)
let url = path;
params.forEach((param) => {
const paramKey = param.substring(1); // remove ':'
url = addPathParam(url, param, paramsMap[paramKey]);
});
return url;
}
function addPathParam(path, param, value) {
return path.replace(`${param}`, value);
}
Does this look robust enough to you? Is there any other case I should be considering?
Here's a couple of tests I did:
Result:

Laravel: Invalid argument supplied for foreach() when sending values with multipart-form-data

I'm trying to access an array on a multipart-form-data request.
On my js code I add to the formData the object that I need this way:
for (const key in property) {
if (typeof property[key] !== 'undefined') {
console.log(key, property[key]);
formData.append(`property[${key}]`, property[key]);
}
}
The property has the array images and the value is an array like this:
Also in the same forData I send all the data from the images that I want to handle, and in this case the property contains an array of objects that are images previously stored in the database.
So, in my controller I have to following
// Images that are already in database
$propertyData = $request->input('property');
$images = isset($propertyData['images'])? $propertyData['images'] : []
foreach($images as $i) {
$image = Image::find((int)$i['id']);
if ( !is_null($image) ) {
$image->update($i);
}
}
But I got the error: Invalid argument supplied for foreach()
I've tried to even convert each image on the javascript code to an array like the property, but i got the same error.
You cannot directly append objects or arrays to FormData. You must add the keys and values one by one.
A way to do that is like so:
var formData = new FormData;
var arr = ['this', 'is', 'an', 'array'];
for (var i = 0; i < arr.length; i++) {
formData.append('arr[]', arr[i]);
}
I use a helper function (note that this is in ES6 syntax) to do this:
export function appendFormdata(formData, data, previousKey) {
if (data instanceof Object) {
Object.keys(data).forEach(key => {
const value = data[key];
if (previousKey) {
key = `${previousKey}[${key}]`;
}
if (value instanceof Object && !Array.isArray(value) && !(value instanceof File)) {
return appendFormdata(formData, value, key);
}
if (Array.isArray(value)) {
value.forEach((val, i) => {
return appendFormdata(formData, val, `${key}[${i}]`);
});
} else {
formData.append(key, value);
}
});
}
}
For more details refer: appending array to FormData and send via AJAX and Convert JS Object to form data

Ignore undefined values that are passed in the query object parameter for Mongoose's Find function?

So:
Sound.find({
what: req.query.what,
where: req.query.where,
date: req.query.date,
hour: req.query.hour}, function(err, varToStoreResult, count) {
//stuff
});
Say that only req.query.what has an actual value, "yes" for example, while the rest (req.query.where, req.query.date, etc.) are all undefined/empty. If I leave this code as is, it would look for docs which have what = req.query.what AND where = req.query.where, etc. but I would only want it to find docs which have their what value = to req.query.what, since the other fields are undefined/empty.
You have to filter your res.query object from undefined/empty values first, and then pass it to find function. If you have just a couple of properties, you can use if statement:
const query = req.query;
const conditions = {};
if (query.what) {
conditions.what = query.what;
}
if (query.where) {
conditions.where = query.where;
}
....
Sound.find(conditions, function () {});
Or if there is a lot of properties you can iterate over them:
const query = req.query;
const conditions = Object.keys(query)
.reduce((result, key) => {
if (query[key]) {
result[key] = query[key];
}
return result;
}, {});
Sound.find(conditions, function () {});
Also, I would not advise to remove properties from the actual res.query object - delete res.query.what - because you won't be able to use it in another middleware if you would like to.
May be a bit ugly, but works for me for filtering undefines. Just perform JSON.parse(JSON.stringify()) on your query like this:
JSON.parse(JSON.stringify({
what: req.query.what,
where: req.query.where,
date: req.query.date,
hour: req.query.hour
}))
You can use a simple reduce on Object.entries
const req = {query: {a: 1,b: undefined,c: 2}}
const query = Object.entries(req.query).reduce((a, [k, v]) => {
if (v !== undefined) {
a[k] = v;
}
return a
}, {});
console.log(query)
i hope i understand you well
here,this is my personally method i use for these kind of query or somesort of filtering:
var query = {}; /*this is our object*/
if(req.query.sell) { /* if request is valid and not null/undefined/empty*/
var sell = req.query.sell; /*we past the query with in a variable as a value*/
var Som = "firstname"; /*after that we define our mongodb parametre as a name */
query[Somesort] = Som; /*and here we past our name and value of parameter to object*/
}
/*and we do this*/
collection-name-here.find(query, function(err, model-name-here){
if(err){
console.log(err);
} else {
/*your code
});
hope this would help you guys

How to build query string with Javascript

Just wondering if there is anything built-in to Javascript that can take a Form and return the query parameters, eg: "var1=value&var2=value2&arr[]=foo&arr[]=bar..."
I've been wondering this for years.
The URLSearchParams API is available in all modern browsers. For example:
const params = new URLSearchParams({
var1: "value",
var2: "value2",
arr: "foo",
});
console.log(params.toString());
//Prints "var1=value&var2=value2&arr=foo"
2k20 update: use Josh's solution with URLSearchParams.toString().
Old answer:
Without jQuery
var params = {
parameter1: 'value_1',
parameter2: 'value 2',
parameter3: 'value&3'
};
var esc = encodeURIComponent;
var query = Object.keys(params)
.map(k => esc(k) + '=' + esc(params[k]))
.join('&');
For browsers that don't support arrow function syntax which requires ES5, change the .map... line to
.map(function(k) {return esc(k) + '=' + esc(params[k]);})
If you're using jQuery you might want to check out jQuery.param() http://api.jquery.com/jQuery.param/
Example:
var params = {
parameter1: 'value1',
parameter2: 'value2',
parameter3: 'value3'
};
var query = $.param(params);
console.log(query);
This will print out:
parameter1=value1&parameter2=value2&parameter3=value3
This doesn't directly answer your question, but here's a generic function which will create a URL that contains query string parameters. The parameters (names and values) are safely escaped for inclusion in a URL.
function buildUrl(url, parameters){
var qs = "";
for(var key in parameters) {
var value = parameters[key];
qs += encodeURIComponent(key) + "=" + encodeURIComponent(value) + "&";
}
if (qs.length > 0){
qs = qs.substring(0, qs.length-1); //chop off last "&"
url = url + "?" + qs;
}
return url;
}
// example:
var url = "http://example.com/";
var parameters = {
name: "George Washington",
dob: "17320222"
};
console.log(buildUrl(url, parameters));
// => http://www.example.com/?name=George%20Washington&dob=17320222
Create an URL object and append the values to seachParameters
let stringUrl = "http://www.google.com/search";
let url = new URL(stringUrl);
let params = url.searchParams;
params.append("q", "This is seach query");
console.log(url.toString());
The output will be
http://www.google.com/search?q=This+is+seach+query
With jQuery you can do this by $.param
$.param({ action: 'ship', order_id: 123, fees: ['f1', 'f2'], 'label': 'a demo' })
// -> "action=ship&order_id=123&fees%5B%5D=f1&fees%5B%5D=f2&label=a+demo"
ES2017 (ES8)
Making use of Object.entries(), which returns an array of object's [key, value] pairs. For example, for {a: 1, b: 2} it would return [['a', 1], ['b', 2]]. It is not supported (and won't be) only by IE.
Code:
const buildURLQuery = obj =>
Object.entries(obj)
.map(pair => pair.map(encodeURIComponent).join('='))
.join('&');
Example:
buildURLQuery({name: 'John', gender: 'male'});
Result:
"name=John&gender=male"
querystring can help.
So, you can
const querystring = require('querystring')
url += '?' + querystring.stringify(parameters)
No, I don't think standard JavaScript has that built in, but Prototype JS has that function (surely most other JS frameworks have too, but I don't know them), they call it serialize.
I can reccomend Prototype JS, it works quite okay. The only drawback I've really noticed it it's size (a few hundred kb) and scope (lots of code for ajax, dom, etc.). Thus if you only want a form serializer it's overkill, and strictly speaking if you only want it's Ajax functionality (wich is mainly what I used it for) it's overkill. Unless you're careful you may find that it does a little too much "magic" (like extending every dom element it touches with Prototype JS functions just to find elements) making it slow on extreme cases.
If you don't want to use a library, this should cover most/all of the same form element types.
function serialize(form) {
if (!form || !form.elements) return;
var serial = [], i, j, first;
var add = function (name, value) {
serial.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
}
var elems = form.elements;
for (i = 0; i < elems.length; i += 1, first = false) {
if (elems[i].name.length > 0) { /* don't include unnamed elements */
switch (elems[i].type) {
case 'select-one': first = true;
case 'select-multiple':
for (j = 0; j < elems[i].options.length; j += 1)
if (elems[i].options[j].selected) {
add(elems[i].name, elems[i].options[j].value);
if (first) break; /* stop searching for select-one */
}
break;
case 'checkbox':
case 'radio': if (!elems[i].checked) break; /* else continue */
default: add(elems[i].name, elems[i].value); break;
}
}
}
return serial.join('&');
}
You can do that nowadays with FormData and URLSearchParams without the need to loop over anything.
const formData = new FormData(form);
const searchParams = new URLSearchParams(formData);
const queryString = searchParams.toString();
Older browsers will need a polyfill, though.
Might be a bit redundant but the cleanest way i found which builds on some of the answers here:
const params: {
key1: 'value1',
key2: 'value2',
key3: 'value3',
}
const esc = encodeURIComponent;
const query = Object.keys(params)
.map(k => esc(k) + '=' + esc(params[k]))
.join('&');
return fetch('my-url', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: query,
})
Source
I'm not entirely certain myself, I recall seeing jQuery did it to an extent, but it doesn't handle hierarchical records at all, let alone in a php friendly way.
One thing I do know for certain, is when building URLs and sticking the product into the dom, don't just use string-glue to do it, or you'll be opening yourself to a handy page breaker.
For instance, certain advertising software in-lines the version string from whatever runs your flash. This is fine when its adobes generic simple string, but however, that's very naive, and blows up in an embarrasing mess for people whom have installed Gnash, as gnash'es version string happens to contain a full blown GPL copyright licences, complete with URLs and <a href> tags. Using this in your string-glue advertiser generator, results in the page blowing open and having imbalanced HTML turning up in the dom.
The moral of the story:
var foo = document.createElement("elementnamehere");
foo.attribute = allUserSpecifiedDataConsideredDangerousHere;
somenode.appendChild(foo);
Not:
document.write("<elementnamehere attribute=\""
+ ilovebrokenwebsites
+ "\">"
+ stringdata
+ "</elementnamehere>");
Google need to learn this trick. I tried to report the problem, they appear not to care.
You don't actually need a form to do this with Prototype. Just use Object.toQueryString function:
Object.toQueryString({ action: 'ship', order_id: 123, fees: ['f1', 'f2'], 'label': 'a demo' })
// -> 'action=ship&order_id=123&fees=f1&fees=f2&label=a%20demo'
I know this is very late answer but works very well...
var obj = {
a:"a",
b:"b"
}
Object.entries(obj).map(([key, val])=>`${key}=${val}`).join("&");
note: object.entries will return key,values pairs
output from above line will be a=a&b=b
Hope its helps someone.
Happy Coding...
The UrlSearchParams API is a great suggestion, but I can't believe nobody mentioned the incredibly useful .get and .set methods. They can be used to manipulate the query string and not only they're very easy to use, they also solve a number of issues you might encounter. For example, in my case I wanted to build a query string without duplicate keys. .set solves this problem for you. Quoting from the MDN docs:
URLSearchParams.set()
Sets the value associated with a given search parameter to the given value. If there are several values, the others are deleted.
Example (from MDN):
let url = new URL('https://example.com?foo=1&bar=2');
let params = new URLSearchParams(url.search);
// Add a third parameter
params.set('baz', 3);
params.toString(); // "foo=1&bar=2&baz=3"
Alternative, shorter syntax:
let url = new URL('https://example.com?foo=1&bar=2');
// Add a third parameter
url.searchParams.set('baz', 3);
url.searchParams.toString(); // "foo=1&bar=2&baz=3"
As Stein says, you can use the prototype javascript library from http://www.prototypejs.org.
Include the JS and it is very simple then, $('formName').serialize() will return what you want!
For those of us who prefer jQuery, you would use the form plugin: http://plugins.jquery.com/project/form, which contains a formSerialize method.
Is is probably too late to answer your question.
I had the same question and I didn't like to keep appending strings to create a URL. So, I started using $.param as techhouse explained.
I also found a URI.js library that creates the URLs easily for you. There are several examples that will help you: URI.js Documentation.
Here is one of them:
var uri = new URI("?hello=world");
uri.setSearch("hello", "mars"); // returns the URI instance for chaining
// uri == "?hello=mars"
uri.setSearch({ foo: "bar", goodbye : ["world", "mars"] });
// uri == "?hello=mars&foo=bar&goodbye=world&goodbye=mars"
uri.setSearch("goodbye", "sun");
// uri == "?hello=mars&foo=bar&goodbye=sun"
// CAUTION: beware of arrays, the following are not quite the same
// If you're dealing with PHP, you probably want the latter…
uri.setSearch("foo", ["bar", "baz"]);
uri.setSearch("foo[]", ["bar", "baz"]);`
These answers are very helpful, but i want to add another answer, that may help you build full URL.
This can help you concat base url, path, hash and parameters.
var url = buildUrl('http://mywebsite.com', {
path: 'about',
hash: 'contact',
queryParams: {
'var1': 'value',
'var2': 'value2',
'arr[]' : 'foo'
}
});
console.log(url);
You can download via npm https://www.npmjs.com/package/build-url
Demo:
;(function () {
'use strict';
var root = this;
var previousBuildUrl = root.buildUrl;
var buildUrl = function (url, options) {
var queryString = [];
var key;
var builtUrl;
var caseChange;
// 'lowerCase' parameter default = false,
if (options && options.lowerCase) {
caseChange = !!options.lowerCase;
} else {
caseChange = false;
}
if (url === null) {
builtUrl = '';
} else if (typeof(url) === 'object') {
builtUrl = '';
options = url;
} else {
builtUrl = url;
}
if(builtUrl && builtUrl[builtUrl.length - 1] === '/') {
builtUrl = builtUrl.slice(0, -1);
}
if (options) {
if (options.path) {
var localVar = String(options.path).trim();
if (caseChange) {
localVar = localVar.toLowerCase();
}
if (localVar.indexOf('/') === 0) {
builtUrl += localVar;
} else {
builtUrl += '/' + localVar;
}
}
if (options.queryParams) {
for (key in options.queryParams) {
if (options.queryParams.hasOwnProperty(key) && options.queryParams[key] !== void 0) {
var encodedParam;
if (options.disableCSV && Array.isArray(options.queryParams[key]) && options.queryParams[key].length) {
for(var i = 0; i < options.queryParams[key].length; i++) {
encodedParam = encodeURIComponent(String(options.queryParams[key][i]).trim());
queryString.push(key + '=' + encodedParam);
}
} else {
if (caseChange) {
encodedParam = encodeURIComponent(String(options.queryParams[key]).trim().toLowerCase());
}
else {
encodedParam = encodeURIComponent(String(options.queryParams[key]).trim());
}
queryString.push(key + '=' + encodedParam);
}
}
}
builtUrl += '?' + queryString.join('&');
}
if (options.hash) {
if(caseChange)
builtUrl += '#' + String(options.hash).trim().toLowerCase();
else
builtUrl += '#' + String(options.hash).trim();
}
}
return builtUrl;
};
buildUrl.noConflict = function () {
root.buildUrl = previousBuildUrl;
return buildUrl;
};
if (typeof(exports) !== 'undefined') {
if (typeof(module) !== 'undefined' && module.exports) {
exports = module.exports = buildUrl;
}
exports.buildUrl = buildUrl;
} else {
root.buildUrl = buildUrl;
}
}).call(this);
var url = buildUrl('http://mywebsite.com', {
path: 'about',
hash: 'contact',
queryParams: {
'var1': 'value',
'var2': 'value2',
'arr[]' : 'foo'
}
});
console.log(url);
var params = { width:1680, height:1050 };
var str = jQuery.param( params );
console.log(str)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Remove undefined params 💪😃
urlParams = obj =>{
const removeUndefined = JSON.parse(JSON.stringify(obj))
const result = new URLSearchParams(removeUndefined).toString();
return result ? `?${result}`: '';
}
console.log(urlParams({qwe: undefined, txt: 'asd'})) // '?txt=asd'
console.log(urlParams({qwe: undefined})) // ''

Categories