Live Search from multiple JSON files, Javascript, AJAX - javascript

I have to implement a search bar using AJAX and jQuery that displays results from 3 JSON files. At the moment I have it working with one but I am not sure how I might adapt this to live search 3 separate JSON files simultaneously.
const search = document.querySelector('#search');
search.addEventListener('keydown', liveSearch);
function liveSearch() {
const searchField = search.value;
const myExp = new RegExp(searchField, "i");
$.getJSON('weekday.json', function(data) {
var output = '<ul>';
$.each(data, function(key, val) {
if ((val.Title.search(myExp) !== -1) || (val.Description.search(myExp) !== -1)) {
output += '<li>';
output += '<strong>' + val.Title + '</strong>';
output += '<p>' + val.Description + ' - ' + val.Price + '</p>';
output += '</li>';
}
});
output += '</ul>';
$('#output').html(output);
});
}
Any help would be appreciated.

you can use $.when to execute multiple async promise
```
$.when(
$.getJSON('weekday1.json'),
$.getJSON('weekday2.json'),
$.getJSON('weekday3.json')
).then(function (results) {
var r1 = results[0]; // result in weekday1.json
var r2 = results[1]; // result in weekday2.json
var r3 = results[2]; // result in weekday3.json
})
Note: the promise(.then function) will only be resolved after all async task are resolved.
Ref: https://api.jquery.com/jquery.when/

'results' in the code provided by arfai1213 does not return an array that can be used as suggested.
Splitting results as per the code below returns separate arrays that can be used.
$.when(
$.getJSON('./data/file1.json'),
$.getJSON('./data/file2.json')
).then(function (r1, r2) {
$.each(r1[0], function(key, val){
//do something
})
$.each(r2[0], function(key, val){
//do something
})
});

Related

How to use higher order functions instead of FOR Loop for Line items in Suitelet printouts (Netsuite)?

i have been tasked by my senior to print values of line items using higher order functions (.filter/.map/.reject/.reduce). I m confused how to write the higher order function instead of a for loop(for printing the line values in Invoice Printout). I need to print the line only when the qty is more than 3. I m an intern and i dont know how it will work, kindly help.
Link to The code snippet: https://drive.google.com/file/d/1uVQQb0dsg_bo53fT3vk9f0G8WwZomgQg/view?usp=sharing
I always used if condition for printing the row only when the quantity field has value more than 3. I even know how to .filter but i dont know how to call it and where to call it. Please help
I don't believe Array.from works in server side code. If it does then use that. What I have been using are the following functions. They don't conform to the higher order functions specified but they work with Netsuite syntax and go a long way towards simplifying sublist handling and encapsulating code:
//SS2.x
//I have this as a snippet that can be included in server side scripts
function iter(rec, listName, cb){
var lim = rec.getLineCount({sublistId:listName});
var i = 0;
var getV = function (fld){
return rec.getSublistValue({sublistId:listName, fieldId:fld, line:i});
};
for(; i< lim; i++){
cb(i, getV);
}
}
// to use it:
iter(ctx.newRecord, 'item', function(idx, getV){
if(parseInt(getV('quantity')) >3){
...
}
});
or for SS1 scripts I have the following which allows code to be shared between UserEvent and Scheduled scripts or Suitelets
function forRecordLines(rec, machName, op, doReverse) {
var i, pred, incr;
var getVal = rec ? function(fld) {
return rec.getLineItemValue(machName, fld, i);
} : function(fld) {
return nlapiGetLineItemValue(machName, fld, i);
};
var getText = rec ? function(fld) {
return rec.getLineItemText(machName, fld, i);
} : function(fld) {
return nlapiGetLineItemText(machName, fld, i);
};
var setVal = rec ? function(fld, val) {
rec.setLineItemValue(machName, fld, i, val);
} : function(fld, val) {
nlapiSetLineItemValue(machName, fld, i, val);
};
var machCount = rec ? rec.getLineItemCount(machName) : nlapiGetLineItemCount(machName);
if(!doReverse){
i = 1;
pred = function(){ return i<= machCount;};
incr = function(){ i++;};
}else{
i = machCount;
pred = function(){ return i>0;};
incr = function(){ i--;};
}
while(pred()){
var ret = op(i, getVal, getText, setVal);
incr();
if (typeof ret != 'undefined' && !ret) break;
}
}
// User Event Script:
forRecordLines(null, 'item', function(idx, getV, getT, setV){
if(parseInt(getV('quantity')) >3){
...
}
});
// in a Scheduled Script:
forRecordLines(nlapiLoadRecord('salesorder', id), 'item', function(idx, getV, getT, setV){
if(parseInt(getV('quantity')) >3){
...
}
});
Usually its a straight forward task, but since you are getting length and based on that you are iterating, you can use Array.from. Its signature is:
Array.from(ArrayLikeObject, mapFunction);
var tableData = Array.from({ length: countItem}, function(index) {
vendorBillRec.selectLineItem('item', index);
var item = vendorBillRec.getCurrentLineItemText('item', 'item');
var description = nlapiEscapeXML(vendorBillRec.getCurrentLineItemValue('item', 'description'));
var quantity = parseFloat(nullNumber(vendorBillRec.getCurrentLineItemValue('item', 'quantity')));
return { item, description, quantity}
});
var htmlData = tableData.filter(...).map(getRowMarkup).join('');
function getRowMarkup(data) {
const { itemName, descript, quantity } = data;
return '<tr>' +
'<td colspan="6">' +
'<p>' + itemName + ' ' + descript + '</p>'+
'</td>' +
'<td colspan="2" align="right">' + quantity + '</td>' +
'</tr>';
}
Or if you like to use more functional approach:
Create a function that reads and give you all data in Array format. You can use this data for any task.
Create a function that will accept an object of specified properties and returns a markup.
Pass the data to this markup after any filter condition.
Idea is to isolate both the task:
- Getting data that needs to be processed
- Presentation logic and style related code
var htmlString = Array.from({ length: countItem}, function(index) {
vendorBillRec.selectLineItem('item', index);
var item = vendorBillRec.getCurrentLineItemText('item', 'item');
var description = nlapiEscapeXML(vendorBillRec.getCurrentLineItemValue('item', 'description'));
var qty = parseFloat(nullNumber(vendorBillRec.getCurrentLineItemValue('item', 'quantity')));
return getRowMarkup(item, description, qty)
}).join('');
function getRowMarkup(itemName, descript, quantity) {
return '<tr>' +
'<td colspan="6">' +
'<p>' + itemName + ' ' + descript + '</p>'+
'</td>' +
'<td colspan="2" align="right">' + quantity + '</td>' +
'</tr>';
}

synchronous method in javascript and $.get method in jQuery

I have a strange issue with my method :
$('#search').on('keyup', function () {
var valNow = $('#search').val();
if (last !== valNow && valNow !== '') {
console.log(valNow + ' / ' + i);
//interrogate a server from a cities
$.get(path + '/' + strategy() + '/' + valNow,
function (data, status) {
//here
console.log(status);
if (status === 'success') {
cities = [];
cities = data;
}
},
'json');
// make new last
last = valNow;
//list result
var items = [];
console.log(cities[0]);
console.log(' / ' + i);
$(cities).each(function (index, value) {
console.log(value);
var notStrong = valNow.length;
var strong = value.length;
items.push('<li><strong>'+ valNow +'</strong>'+value.substr(notStrong)+'</li>');
});
$('.result').append(items).show();
i++;
console.log('finished');
}
}
);
the problem is simply when I use (/bind) this function I get finish message before console.log(status) (commented://here), the $.get function takes a lot of times to interrogate the web service , I don't know why I have this issue with $.get function, is it a thread or something like this ??? what I want is to get in order all statements (console.log(status) then console.log('finish')).
Try appending your options inside the function block which gives you the data
$('#search').on('keyup', function () {
var valNow = $('#search').val();
if (last !== valNow && valNow !== '') {
console.log(valNow + ' / ' + i);
//interrogate a server from a cities
$.get(path + '/' + strategy() + '/' + valNow,
function (data, status) {
if (status === 'success') {
cities = data;
// append all the options here
}
},'json');
}
}
);
Using AJAX to get data from a remote location always runs asynchronous, meaning that, when calling $.get, the call to the server will be made and the js code returns immediately. Then, after the code in between, console.log('finish') will be called, and some time later, when the $.get call receives the response from the server, the code inside the $.get anonymous function will be called, which then runs console.log(status).
That is the intended design for grabbing data from remote locations. If you want to run the other code strictly after that, you have to run it inside the callback function of $.get, like that:
$('#search').on('keyup', function() {
var valNow = $('#search').val();
if (last !== valNow && valNow !== '') {
console.log(valNow + ' / ' + i);
//interrogate a server from a cities
$.get(path + '/' + strategy() + '/' + valNow,
function(data, status) {
//here
console.log(status);
if (status === 'success') {
cities = [];
cities = data;
}
// make new last
last = valNow;
//list result
var items = [];
console.log(cities[0]);
console.log(' / ' + i);
$(cities).each(function(index, value) {
console.log(value);
var notStrong = valNow.length;
var strong = value.length;
items.push('<li><strong>' + valNow + '</strong>' + value.substr(notStrong) + '</li>');
});
$('.result').append(items).show();
i++;
console.log('finished');
},
'json');
}
});
There are other ways to make the code more pretty, for example using Promises.

Populating html table with slowly loading separate jquery fuctions

I've seen many topics like this, some with pretty much the exact same title, but I'm still not finding a solution for what I'm attempting.
I basically have an HTML table that is getting data from 10 different sources. 9 of the sources I'm trying to push to an array and fund the sum, which will be one column in the table. And the other source is just a single external query result.
I'm running into problems when they take too long to load and the table has already populated.
I tried a timeout function but it doesn't seem to work even then. I'm also having issues with the table showing 10 commas separated values and not a sum, even when I add a variable =0 and then do source1+= variable for each of the sources.
Heres what I have so far, I Some of the letterers in the code are just random and a placeholder to show the structure. But this is the basic setup. I've only included 2 out of the 9 functions to save space, but its pretty much similar to the first two, 3..4..5 etc.
At a loss here, any help would be awesome and I thank you all for your valuable time.
/* HTML */
<table id="AA"> <caption>title<span id="totalValue">0</span></caption>
<tr>
<th>L</th>
<th>L Qty.</th>
<th>W</th>
<th class="value">Value</th>
<th>some text</th>
<label for="CD">
</label>
<input id="img" type="hidden" value="somedatahere" size="60" />
</tr>
</table>
//and heres the separate js
var sumqholder = 0;
$('#Form').submit(function(event) {
event.preventDefault();
$('#results').html('loading...');
var AB = $('#CD').val();
A.H.call(function(err, J) {
A.I.call(function(err, K) {
var EF = $('#EF').val();
A.G.call(EF, function(err, G) {
var results = '<b>--:</b> ';
results += G.round(5);
sumq += G;
$('#results').html(results);
});
});
$('#results2').html('loading...');
var AB = $('#CD').val();
A.H.call(function(err, J) {
A.I.call(function(err, K) {
var EF = $('#EF').val();
A.G.call(IA, function(err, G) {
var results2 = '<b>--:</b> ';
results2 += G.round(5);
sumq += G;
$('#results2').html(results2);
});
});
var sumq = sumqholder;
var L = [{
M: O,
quantity: sumq
}, {
P: " ",
quantity: x
}];
var totalValue = 0;
$(document).ready(function() {
refresh();
});
function refresh() {
$("#AA").find("tr:gt(0)").remove();
totalValue = 0;
L.forEach(function(L) {
sendRequest(L.M, L.quantity);
});
}
function sendRequest(M, L) {
var url = " " + M + "/";
$.get(url, function(data) {
addRow(data[0], quantity);
});
}
function addRow(data, quantity) {
var value = data.W * quantity;
totalValue += value;
var row = '<tr>';
row += '<td>' + data.I + '</td>';
row += '<td>' + quantity + '</td>';
row += '<td>' + data.I + '</td>';
row += '<td>' + value.toFixed(2) + '</td>';
row += '<td>' + data.O + '</td>';
row += '</tr>';
$('#AA tr:last').after(row);
updateTotalValue();
}
function updateTotalValue() {
$("#totalValue").text(totalValue.toFixed(6));
}
If i got this right, you're having problems processing data because the source is coming from 10 different async calls right? If that's the case, have each call finish off by calling a checking function.
var flag = 0;
function checkLoaded()
{
if(flag == 9){
doDataProcessing();
}else{
flag++;
}
}
This way, each call will end calling checkLoaded(); and if its the 10th one, flag will have incremented 9 times therefore we can already assume we can proceed to data processing.
This is precisely the kind of scenario that promises were designed to simplify. They will help you avoid "callback hell" and manage errors.
First, with reference to How do I Convert an Existing Callback API to Promises?, promisify all methods that accept a callback :
A.G.callAsync = function(val) {
return new Promise(function(resolve, reject) {
A.G.call(val, function(err, x) {
err ? reject(err) : resolve(x);
});
});
};
A.H.callAsync = function() {
return new Promise(function(resolve, reject) {
A.H.call(function(err, x) {
err ? reject(err) : resolve(x);
});
});
};
A.I.callAsync = function() {
return new Promise(function(resolve, reject) {
A.I.call(function(err, x) {
err ? reject(err) : resolve(x);
});
});
};
jQuery's $.get() already returns a promise therefore doesn't need to be promisified.
Then, everything else can be done inside a $(document).ready(function() {...}) structure, something like this :
$(document).ready(function() {
$('#Form').submit(function (event) {
event.preventDefault();
$('#results').html('loading...');
// promise chain for the first A.H(), A.I(), A,G() sequence.
var promise1 = A.H.callAsync().then(function(J) {
return A.I.callAsync();
}).then(function(K) {
return A.G.callAsync($('#AB').val());
}).then(function(G) {
$('#results').html('<b>--:</b> ' + G.round(5));
return G; // the chain delivers this value
});
// promise chain for the second A.H(), A.I(), A,G() sequence.
var promise2 = promise1.then(function() {
$('#results2').html('loading...');
return A.H.callAsync().then(function(J) {
return A.I.callAsync();
}).then(function(K) {
return A.G.callAsync($('#EF').val());
}).then(function(G) {
$('#results2').html('<b>--:</b> ' + G.round(5));
return G; // the chain delivers this value
});
});
Promise.all([promise1, promise2]) // aggregate promise1 and promise2
.then(function(G_values) { // G_values is an array containing the `G` values delivered by promise1 and promise2.
var sumq = G_values.reduce(function(runningTotal, val) {
return runningTotal + val;
}, 0);
var L = [
{ 'M': O, 'quantity': sumq }, // what is O?
{ 'P': ' ', 'quantity': x } // where's 'M'? what is 'x'
// ...
];
$("#AA").find("tr:gt(0)").remove();
return Promise.all(L.map(sendRequest)) // aggregate all the promises returned by sendRequest()
.then(function(values) { // `values` is an array of values delivered by the promises returned from sendRequest().
var totalValue = values.reduce(function(runningTotal, val) {
return runningTotal + val;
}, 0); // ie values[0] + values[1] + ...
$("#totalValue").text(totalValue.toFixed(6));
});
function sendRequest(item) {
var url = ' ' + item.M + '/';
var $tr = $('<tr/>').insertAfter('#AA tr:last'); // inserting here ensures the <tr>s are in the same order as the objects in `L`.
return $.get(url)
.then(function(data) {
return addRow(data[0], item.quantity, $tr); // deliver addRow's return value to Promise.all() above
});
}
function addRow(data, quantity, $tr) {
var value = data.W * quantity;
var cells = '<td>' + data.I + '</td>'
+ '<td>' + quantity + '</td>'
+ '<td>' + data.I + '</td>'
+ '<td>' + value.toFixed(2) + '</td>'
+ '<td>' + data.O + '</td>';
$tr.html(cells);
return value; // for on-screen consistency, you may choose to `return Number(value.toFixed(2))`
}
}).catch(function(err) {
console.error(err);
});
});
});
I'm not sure that's 100% what you want but it should give you a good idea how to solve an asynchronous javascript problem.

how to create an exact search using Js and Jquery

I'm trying to create an expanded search where you can find people not only using there names but some combinations... for instance i have this list of players and this peace of code work fine, but if i want to find for such features like - keeper England. this line of code doesn't work ((val.position.search(myExp) != -1) || (val.nationality.search(myExp) != -1))
$("#search").keyup(function() {
var field = $("#search").val();
var myExp = new RegExp(field, "i");
$.getJSON("players.json", function(data) {
var output = "<ul>";
$.each(data, function(key, val) {
if ((val.name.search(myExp) != -1) || (val.position.search(myExp) != -1) || ((val.position.search(myExp) != -1) || (val.nationality.search(myExp) != -1))) {
output += "<li>";
output += '<p class="name">' + val.name + '</p>';
output += '<p>' + val.position + '</p>';
output += '<p>' + val.dateOfBirth + '</p>';
output += '<p>' + val.nationality + '</p>';
output += '<p>' + val.contractUntil + '</p>';
output += '<p>' + val.marketValue + '</p>';
output += "</li>";
}
});
output += "</ul>";
$("#update").html(output);
});
});
{
"id":2138,
"name":"Thibaut Courtois",
"position":"Keeper",
"jerseyNumber":13,
"dateOfBirth":"1992-05-11",
"nationality":"Belgium",
"contractUntil":"2019-06-30",
"marketValue":"35,000,000 ˆ"
},
{
"id":2140,
"name":"Jamal Blackman",
"position":"Keeper",
"jerseyNumber":27,
"dateOfBirth":"1993-10-27",
"nationality":"England",
"contractUntil":"2019-06-30",
"marketValue":"250,000 ˆ"
},
You have to do multiple search queries because you have multiple words in your query:
"england keeper" => "england" and "keeper"
So you want to filter the items by "england" and also by "keeper"..
The best would be to create a small functions, each will do a part of it:
// Note: this function returns the filter function
var myFilter = function(regex) {
return function(item) {
return regex.test(item.name)
|| regex.test(item.position)
|| regex.test(item.nationality)
}
}
// this is a higher order function, takes the items and the full searchString as arguments
var findMatches = function(items, searchString) {
// make a copy of the items / data
var found = items.slice(0, item.length);
// split the searchString, and filter the items by it
searchString.split(' ').forEach(function(part) {
found = found.filter(myFilter(new RegEx(part, 'i'))
});
return found;
}
Now you can use it in your code:
...
var output = "<ul>";
var filteredData = findMatches(data, field);
$.each(filteredData, function(key, val) {
// filteredData should be fine, you can just render it
}
...

Assign Nested JSON to li using jquery

I am trying to parse a json file using jquery getJson. I have no problem looping through the first layer, but I need to assign a nested array to li as well.
My JSON Code
{"Controls":[
{
"Object":"Button",
"ButtonAttr": [{"x": "1","y": "2","width": "3","height": "4"}]
},
{
"Object":"Image",
"ButtonAttr": [{"x": "5","y": "6","width": "7","height": "8"}]
},
{
"Object":"TextField",
"ButtonAttr": [{"x": "9","y": "10","width": "11","height": "12"}]
}
]}
My JS/JQUERY Code where I have no problem bringing in the first layer of the JSON and appending it to a li. I need to figure out how to get the 'ButtonAttr' layer
//Get JSON File which contains all Controls
$.getJSON('controls.json', function(data) {
//Build Objects List
var objectList="<ul>";
for (var i in data.Controls) {
objectList+="<li>" + data.Controls[i].Object +"</li>";
}
objectList+="</ul>";
$('#options').append(objectList);
//Add new Code Object based on #Options LI Index
$(document).on('click','#options li', function() {
var index = $('#options li').index(this);
$('#code').append('<li>' + data.Controls[index].Object + '</li>');
//Shows Selected LI Index
$('#optionsIndex').text("That was div index #" + index);
});
});
I cannot for the life of me get it to loop through the second array and list out the x,y,width, and height fields.
Here is my desired output
<ul>
<li>Button</li>
<ul>
<li>x:1</li>
<li>y:2</li>
<li>width:3</li>
<li>height:4</li>
</ul>
<li>Image</li>
<ul>
<li>x:5</li>
<li>y:6</li>
<li>width:7</li>
<li>height:8</li>
</ul>
<li>TextField</li>
<ul>
<li>x:9</li>
<li>y:10</li>
<li>width:11</li>
<li>height:12</li>
</ul>
</ul>
Any help would be greatly appreciated
I worked through this in another question.
How to handle comma separated objects in json? ( [object Object],[object Object] )
You want a recursive function that starts a <ul> and adds <li> for each item in the list. It also tests items, and if they are themselves lists, it calls itself with that piece of data as the argument. Each time the function is called from within the function you get a <ul> within a <ul>.
function buildULfromOBJ(obj){
var fragments = [];
//declare recursion function
function recurse(item){
fragments.push('<ul>'); // start a new <ul>
$.each(item, function(key, val) { // iterate through items.
if((val != null) && (typeof val == 'object') && // catch nested objects
((val == '[object Object]') || (val[0] == '[object Object]'))){
fragments.push('<li>[' + key + '] =></li>'); // add '[key] =>'
recurse(val); // call recurse to add a nested <ul>
}else if(typeof(val)=='string'){ // catch strings, add double quotes
fragments.push('<li>[' + key + '] = \"' + val + '\"</li>');
}else if($.isArray(val)){ // catch arrays add [brackets]
fragments.push('<li>[' + key + '] = [' + val + ']</li>');
}else{ // default: just print it.
fragments.push('<li>[' + key + '] = ' + val + '</li>');
}
});
fragments.push('</ul>'); // close </ul>
}
// end recursion function
recurse(obj); // call recursion
return fragments.join(''); // return results
} // end buildULfromOBJ()
save your self the pain of trying to do with with for loops etc. and use client-side templating like json2html.com
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src='http://json2html.com/js/jquery.json2html-3.1-min.js'></script>
<ul id='out'></ul>
<script>
var data =
{"Controls":[
{
"Object":"Button",
"ButtonAttr": [{"x": "1","y": "2","width": "3","height": "4"}]
},
{
"Object":"Image",
"ButtonAttr": [{"x": "5","y": "6","width": "7","height": "8"}]
},
{
"Object":"TextField",
"ButtonAttr": [{"x": "9","y": "10","width": "11","height": "12"}]
}
]};
var template = {"tag":"li","children":[
{"tag":"span","html":"${Object}"},
{"tag":"ul","children":[
{"tag":"li","html":"x: ${ButtonAttr.0.x}"},
{"tag":"li","html":"y: ${ButtonAttr.0.y}"},
{"tag":"li","html":"width: ${ButtonAttr.0.width}"},
{"tag":"li","html":"height: ${ButtonAttr.0.height}"}
]}
]};
$('#out').json2html(data.Controls,template);
</script>
You can do it like this.. using the $.each and a for in loop
var str = '<ul>';
$.each(data.Controls, function(k, v) {
str += '<li>' + v.Object + '</li><ul>';
for(var kk in v.ButtonAttr[0]){
str += '<li>' + kk + ':' + v.ButtonAttr[0][kk] + '</li>';
}
str += '</ul>';
});
str += '</ul>';
FIDDLE
or with 2 $.each loops
var str = '<ul>';
$.each(data.Controls, function(k, v) {
str += '<li>' + v.Object + '</li><ul>';
$.each(v.ButtonAttr[0],function(kk,vv){
str += '<li>' + kk + ':' + vv + '</li>';
});
str += '</ul>';
});
str += '</ul>';
FIDDLE
You can loop through the second array just as easily as the first, like so:
$(document).on('click','#options li', function() {
var index = $('#options li').index(this);
$('#code').append('<li>' + data.Controls[index].Object + '</li>');
// Create a new sub-UL to after the LI
var $subUl = $(('<ul>')
// Iterate through each attribute in ButtonAttr
$.each(data.Controls[index].ButtonAttr, function(key, value){
// Append a new LI with that attribute's key/value
$subUl.append('<li>' + key + ':' + value + '</li>');
});
// Append that new sub-UL we made after the last LI we made
$('#code li:last').after($subUl);
//Shows Selected LI Index
$('#optionsIndex').text("That was div index #" + index);
});

Categories

Resources