Cache each JSON search query with localStorage - javascript

THE PROMPT: We have a search that connects to an JSON API url. The search query is inside the url, and the API generates a new JSON file for every search term. We also cannot rely on the browser to cache for us, and we can't use PHP or server side caching; we need to use HTML5 LocalStorage (and we don't care that IE7 can't use it)
We need to cache every new JSON file for every new search. We want to cut down on requests per minute, so we want to use a cached version of the JSON file for repeated search terms.
WHERE I'M STUCK: What has made this difficult is caching a JSON file for each new/different search term. I have been able to cache the first search, but then all subsequent searches use the same cached JSON.
We need help rewriting this so each time a new search is made, it checks to see if the term was searched for previously and if so, grabs the corresponding JSON file. Then of course if the search term is new then cache a new JSON file for that specific search term.
WHAT I'VE TRIED: In my research I've seen a lot of very complicated solutions and I can't seem to get my head completely around all of it, some of these solutions almost worked, I think I just need a better explanation for this specific case.
I think this is the answer but I don't know how to apply it to my situation: jQuery deferred ajax cache
This is crazy and it almost works, it writes into the console when it recognizes that I've searched the same thing again, and it does stop a new request, but unfortunately the cached JSON isn't there, it returns no results.
Caching a jquery ajax response in javascript/browser
WHAT I HAVE SO FAR:
MY PSUEDO CODE:
var searchTerm = WHATEVER IS TYPED INTO THE SEARCHBOX
// The JSON file
var url = 'https://api.example.com/fake/json/path/{'+searchTerm+'}';
// Local Storage Caching Promise
var cachedData = localStorage.getItem("cachedData"),
def = $.Deferred();
if (!cachedData) {
def = $.getJSON(url, function(data) {
cachedData = data;
localStorage.setItem("cachedData", JSON.stringify(cachedData));
});
}
else{
cachedData = JSON.parse(cachedData);
def.resolve();
}
def.done(function() {
var resultHTML = '';
for(var i = 0; i < Object.keys(cachedData.things).length; i++){
$.each(cachedData, function(index, node){
resultHTML += '<li>'
resultHTML += '<h1>' + node[i].name + '</h1>';
resultHTML += '</li>';
});
}
$('div#results').html(resultHTML);
});
EXAMPLE JSON:
{
"things": [
{
"type": "thing",
"username": "randoguy",
"name": "name001",
},
{
"type": "thing2",
"username": "randoguy2",
"name": "name002",
},
...

Thank you #Ian for providing the hints to my answer!
var searchTerm = WHATEVER IS TYPED INTO THE SEARCHBOX;
// The JSON file
var url = 'https://api.example.com/fake/json/path/{'+searchTerm+'}';
// BABAM! Right here, SearchTerm + "-cachedData" gets unique cached data
var cachedData = localStorage.getItem(searchTerm + "-cachedData"),
def = $.Deferred();
if (!cachedData) {
def = $.getJSON(url, function(data) {
cachedData = data;
// BABAM! And here is where the unique cachedData is set! SearchTerm + "-cachedData"
localStorage.setItem(searchTerm + "-cachedData", JSON.stringify(cachedData));
});
}
else{
cachedData = JSON.parse(cachedData);
def.resolve(cachedData);
}
def.done(function(data) {
var resultHTML = '';
for(var i = 0; i < Object.keys(data.repositories).length; i++){
$.each(data, function(index, node){
resultHTML += '<li>'
resultHTML += '<h1>' + node[i].name + '</h1>';
resultHTML += '<p>' + node[i].owner + '</p>';
resultHTML += '</li>';
});
}
$('div#results').html(resultHTML);
});
Where would I be without StackOverflow. Thank you all!

Related

How to correctly populate a drop down list via ajax and MVC controller

I've searched quite a bit for this answer and can't find much that covers what I need.
I have some data stored in a db table I want to populate certain drop down lists with. On the document.ready I have an AJAX call to the controller requesting the data based on a parameter I send it. The controller returns the data as Json. I'm new to the process of Json so, figuring out what to with it once it returns is where I'm stuck.
I'm able display the data returned from the controller in an alert or console.log when it returns, so I know the right values are there, but I can't figure out how to populate the dropdown list with those values. All the data is, is about 5 to 10 ints. (not returned as ints, I know, but they're things like 65, 70, 2, 10, 11) I've tried some various options and nothing seems to work.
I can static the values in an array and that actually will populate the drop down list. I've tried populating that same array with the returned data, but no success that way. Here is the ajax call:
//Fill symbols drop down list
function returnSymbols(cc) {
var sendData = JSON.stringify({ 'ul': cc });
$.ajax({
url: '/Trucking/returnSymbols',
type: 'POST',
contentType: 'application/json',
data: sendData,
success: function (data) {
//alert('success');
console.log('success, yes');
alert(data);
var numbers = [];
var obj = jQuery.parseJSON(data);
/* If I do this and static these, it does work
var numbers = [1, 2, 3, 4, 5] */
var option = '';
for (var i = 0; i < numbers.length; i++) {
option += '<option value="' + numbers[i] + '">' + numbers[i] + '</option>';
}
$('#ddlMcalSymbols').append(option); //fill ddl with these values.
},
error: function () {
//alert('Error');
console.log('Error');
}
});
}
To reiterate I have tried things like numbers.push(obj) or even. .push(data), but those aren't working.
Since the controller returns a Json value I was under the impression I needed to parse that Json in order to do anything with it. Here is the controller if it helps at all:
[HttpPost]
public ActionResult returnSymbols(string ul)
{
List<Get_CIF_SymbolsVM> symbols;
Guid newGuid = Guid.Parse(ul); //parse param as Guid
using (TruckingDb db = new TruckingDb())
{
symbols = db.GetSymbols.ToArray().OrderBy(x => x.RID).Select(x => new Get_CIF_SymbolsVM(x)).ToList();
}
var syms = (from s in symbols
where s.UniqLineType == newGuid
select s.SymbolCode).Distinct();
return Json(syms, JsonRequestBehavior.AllowGet);
}
Any help would be greatly appreciated.
EDIT: Updating the process to explain a bit more.
Had some success, but it's still not correct.
Here is the ajax call. I changed just a few items. It brings back the correct data, but it displays all array items as one line. I need each value in the array as a single value in the drop down list.
var sendData = JSON.stringify({ 'ul': cc });
$.ajax({
url: '/Trucking/returnSymbols',
type: 'POST',
contentType: 'application/json',
data: sendData,
success: function (data) {
//alert('success');
console.log('success, yes');
alert(data);
var numbers = [];
numbers.push(data);
var option = '';
//Added two for loops to show what I've tried.
for (var i = 0; i < numbers.length; i++) {
option += '<option value="' + numbers[i] + '">' + numbers[i] + '</option><br>';
}
$('#ddlMcalSymbols').append(option);
//Tried this option to fill ddl
for (var i = 0; i < numbers.length; i++) {
option = '<option value="' + numbers[i] + '">' + numbers[i] + '</option><br>';
$('#ddlMcalSymbols').append(option);
}
//This Jquery foreach only returns one value to the ddl
$.each(numbers, function (i, value) {
console.log(value);
option += '<option value="' + value[i] + '">' + value[i] + '</option>';
});
$('#ddlMcalSymbols').append(option);
},
error: function () {
//alert('Error');
console.log('Error');
}
});
It brings back the data, but in the drop down both of the for loops above fill the ddl as one long looking string. "61,62,64,66,70,71,72" .. They don't show up as single select values.
I tried parts of the code, and it seems you are overlooking that the var numbers never acquires values.
I also usually prefer to create jquery objects rather than manually compile html; it is easier to develop this way. The code fails with more detail.
Something on the lines of:
var listEl=$('#ddlMcalSymbols');
for (var key in obj) {
jQuery('<option value="' + obj[key] + '">' + obj[key] + '</option>').appendTo(listEl);
}
but in better order
Worked out a solution that while it functions, there is some odd behavior with the CSS of it. I'm using a multiselect drop down called bootstrap-select. Has a .js and .css file. I needed to fill the multiselect drop down with values from a db instead of hard-coding them in with the method.
I use a post ajax call to send a parameter to the controller which retrieves the values I need based on it. I don't know if it's the bootstrap-select or a limitation with multiselect, but it did not like displaying the Json data. My ajax call is already parsing the Json, so that wasn't it. After multiple attempts and trials I figured out the only thing that really works is with an int array. When I had the string array it would display everything as either one long string or only one value. Additionally, even now with it working as I would like, I have to reload the page every time I make a change to the .js file i'm working on. That screws up the bootstrap-select.css file. NO IDEA AS TO WHY. What happens is every 3 to 4 page reloads the values are outside the drop down list and smooshed together like a bunch of unreadable text. (See pic above) I press ctrl + shft + R to clear the chromes cached css and it goes back to how it should look and function. Long-winded, but true. Here is my ajax call with some comments, so you can see what I did. I'm sure there may be more elegant and straightforward ways of doing this, but it was an improvement on what I already had. Maybe it will help someone else.
function returnSymbols(cc) {
var sendData = JSON.stringify({ 'ul': cc });
$.ajax({
url: '/Trucking/returnSymbols',
type: 'POST',
contentType: 'application/json',
data: sendData,
success: function (data) {
var num = [];
var num1 = [];
//Push all returned values into num array
$.each(data, function (index, value) {
num.push(value);
});
console.log(num); // console out to ensure values have been pushed
//convert the string array into an int array
for (var i in num) {
num1[i] = parseInt(num[i]);
}
console.log(num1); //conosle out to ensure values have parsed correctly
fillddl(num1); // send int array to fill drop down func
},
error: function () {
//alert('Error');
console.log('Error');
}
});
}
Then the Function to actually send the values to the drop down list. Very similar to what I've found in other methods.
function fillddl(sym)
{
var s = '';
for (var i = 0; i < sym.length; i++)
{
s += '<option value="' + sym[i] + '">' + sym[i] + '</option>';
}
$(".ddlMcalSymbols").html(s);
}
you can do something like this
In action method
[HttpPost]
public ActionResult getCicitesAction(int provinceId)
{
var cities = db.cities.Where(a => a.provinceId == provinceId).Select(a => "<option value='" + a.cityId + "'>" + a.cityName + "'</option>'";
return Content(String.Join("", cities));
}
The ajax call would be like this:
$("province_dll").change(function(){
$.ajax({
url: 'getCitiesController/getCitiesAction',
type: 'post',
data: {
provinceId: provinceIdVar
}
}).done(function(response){
$("cities_dll").html(response);
});

Dynamic web page scraping with npm cheerio and and request

I'm trying to scrape data from a site which has a base url and then dynamic routes. This particular site simply uses numbers, so I have this code to get the data:
for (var i = 1; i <= total; i++) {
var temp = base_url + i;
var result = "";
request(temp, function(error, response, body) {
var $ = cheerio.load(body);
var address_string = 'http://maps.google.com/?q=' + $('title').text();
//firebase
database.ref('events/' + i).set({
"address": address_string
});
});
}
However, the above code doesn't work, and doesn't add anything to the database. Does anyone know what's wrong?
I'm not sure about the reason, but one thing that will behave strangely in the code you wrote is that the variable i is not bound to the callback scope of the request, and the for loop will finish before any callback is called.
If this is the problem, there should only be one db entry for i === total.
This can be solved by doing an Array.forEach instead.

Html templates loaded asynch (JQuery/JavaScript asynch)

So I'm making a webpage with some code snippets loaded in from txt files. The information to paths and locations of the txt files are stored in a json file. First I'm loaing the json file looking like this
[
{"root":"name of package", "html":"htmlfile.txt", "c":"c#file.txt", "bridge":"bridgefile"},
{"root":"name of package", "html":"htmlfile.txt", "c":"c#file.txt", "bridge":"bridgefile"}
]
After loaded I'm using templates from my index.html file and then inserting the templates. My problem is that its happening asynch so that the page never looks the same because of the asynch nature of js.
Here is what my jquery code for loading and inserting looks like:
$(document).ready(function () {
var fullJson;
$.when(
$.get('/data/testHtml/data.json', function (json) {
fullJson=json;
})
).then(function(){
for(var i=0; i<fullJson.length; i++){
templefy(fullJson[i],i);
}
})
var templefy = function (data, number) {
//Fetches template from doc.
var tmpl = document.getElementById('schemeTemplate').content.cloneNode(true);
//Destination for template inserts
var place = document.getElementsByName('examples');
//Set name
tmpl.querySelector('.name').innerText = data.root;
//Next section makes sure that each tap pane has a unique name so that the system does not override eventlisteners
var htmlNav = tmpl.getElementById("html");
htmlNav.id += number;
var htmlLink = tmpl.getElementById('htmlToggle');
htmlLink.href += number;
var cNav = tmpl.getElementById('c');
cNav.id += number;
var cLink = tmpl.getElementById('cToggle');
cLink.href += number;
var bridgeNav = tmpl.getElementById('bridge');
bridgeNav.id += number;
var bridgeLink = tmpl.getElementById('bridgeToggle');
bridgeLink.href += number;
//Auto creates the sidebar with links using a link template from doc.
var elementLink = tmpl.getElementById('elementLink');
elementLink.name +=data.root;
var linkTemplate = document.getElementById('linkTemplate').content.cloneNode(true);
var linkPlacement = document.getElementById('linkListWVisuals');
var link = linkTemplate.getElementById('link');
link.href = "#"+data.root;
link.innerText = data.root;
linkPlacement.appendChild(linkTemplate);
//Fetches html, c# and bridge code. Then inserts it into template and finally inserts it to doc
$.get("/data/" + data.root + '/' + data.html, function (html) {
tmpl.querySelector('.preview').innerHTML = html;
tmpl.querySelector('.html-prev').innerHTML = html;
$.get('/data/' + data.root + '/' + data.c, function (c) {
tmpl.querySelector('.c-prev').innerHTML = c;
$.get('/data/' + data.root + '/' + data.bridge, function (bridge) {
console.log(bridge);
tmpl.querySelector('.bridge-prev').innerHTML = bridge;
place[0].appendChild(tmpl);
})
})
})
}
});
So yeah my problem is that it just fires in templates whenever they are ready and not in the order written in the json file.
I'll take whatever help I can get..Thank you :)
To my knowledge, there is no golden method and i usually apply one of the following options:
1) Preload the files separately e.g. create key "body" for each entry in your json and then set the value of it to the contents of the file.
2) Do not display items until they are not fully loaded in the DOM and before you show them, sort them in the DOM by e.g. their position in the json array.
Hope it helps.
My only way out has been to make the whole application in angular instead and using a filter to make sure that I get the right result.

Javascript - Ajax Request in SharePoint - Avoid to overwrite variable

Actually I have an issue with javascript. I find no solution for this problem yet.
Maybe someone of you could give me a hint.
I have a created a function, which is called by a button click.in SharePoint 2010.
The function should collect all selected / checked documents from a document library and write them into a separate box, I created. To get all selected documents works fine. But in SharePoint I have to load each element individually for details with an asynchronous request. Here comes my problem:
If I select more than one document, the variable "item" will be overwritten because of the "for" loop. In my asynchronous request success function, I use now the variable "item" again to get the details of it. So I always get the data of the last item of my selection.
Is there a way to avoid this?
Thanks for any help.
Here is my code:
function ApproveDocuments() {
var ClientContext = SP.ClientContext.get_current();
var LibraryID = SP.ListOperation.Selection.getSelectedList();
var Library = ClientContext.get_web().get_lists().getById(LibraryID); //Gets the current Library
var SelectedDocuments = SP.ListOperation.Selection.getSelectedItems(ClientContext);
for (var currentItem in SelectedDocuments) {
var item = Library.getItemById(SelectedDocuments[currentItem].id);
ClientContext.load(item, 'FileLeafRef');
ClientContext.executeQueryAsync(Function.createDelegate(this, function () {
var newElementHtml = '<div style="float:left;padding:3px;width:50px;"></div>';
newElementHtml += '<div style="float:left;padding:3px;">' + item.get_item('FileLeafRef') + '</div>';
newElementHtml += '<div style="clear:both;"></div>';
jQuery("#grol1855InfoDivData").append(newElementHtml);
}), Function.createDelegate(this, this.onLoadItemFailure));
}}
I would refactor this a bit so that you don't make an http request every time you iterate through the loop, which should also solve your over-write problem. I also declared the variables outside of the loops and make it point to the new version each iteration.
function ApproveDocuments() {
var ClientContext = SP.ClientContext.get_current();
var LibraryID = SP.ListOperation.Selection.getSelectedList();
var Library = ClientContext.get_web().get_lists().getByID(LibraryID); //Gets the current Library
var SelectedDocuments = SP.ListOperation.Selection.getSelectedItems(ClientContext);
var fileItems = [], item;
for (var currentItem in SelectedDocuments) {
item = Library.getItemById(SelectedDocuments[currentItem].id);
fileItems.push(item);
ClientContext.load(item, 'FileLeafRef');
}
ClientContext.executeQueryAsync(Function.createDelegate(this, function() {
var newElementHtml;
for (var i = 0; i < fileItems.length; i++) {
newElementHtml = '<div style="float:left;padding:3px;width:50px;"></div>';
newElementHtml += '<div style="float:left;padding:3px;">' + fileItems[i].get_item('FileLeafRef') + '</div>';
newElementHtml += '<div style="clear:both;"></div>';
jQuery("#grol1855InfoDivData").append(newElementHtml);
}
}), Function.createDelegate(this, this.onLoadItemFailure));
}
I would also really advise against writing new solutions that call any of the SOAP services or anything in _vti_bin for that matter; it's just a matter of time before those go away and your stuff won't work.
I have also tried ClientContext.executeQueryAsync in a loop but have never gotten it to work for similar reasons. I've worked around this before by using Ajax to call the SharePoint lists.asmx web service. For example:
var targetUrl = "/_vti_bin/lists.asmx";
var listName = "Shared Documents";
for (var currentItem in SelectedDocuments) {
var currentItemId = SelectedDocuments[currentItem].id;
var soapEnvArray = [];
soapEnvArray.push("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
soapEnvArray.push("<soap:Envelope ");
soapEnvArray.push("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");
soapEnvArray.push("<soap:Body><GetListItems xmlns=\"http://schemas.microsoft.com/sharepoint/soap/\"><listName>" + listName + "</listName>");
soapEnvArray.push("<viewName></viewName>");
soapEnvArray.push("<query>");
soapEnvArray.push("<Where>");
soapEnvArray.push("<Eq>");
soapEnvArray.push("<FieldRef Name=\"ID\"></FieldRef>");
soapEnvArray.push("<Value Type=\"Counter\">" + currentItemId + "</Value>");
soapEnvArray.push("</Eq>");
soapEnvArray.push("</Where>");
soapEnvArray.push("</query>");
soapEnvArray.push("<viewFields>");
soapEnvArray.push("</viewFields>");
soapEnvArray.push("<rowLimit>2000</rowLimit><queryOptions><QueryOptions xmlns=\"\">");
soapEnvArray.push("<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>");
soapEnvArray.push("<ViewAttributes Scope = \"RecursiveAll\"/>");
soapEnvArray.push("</QueryOptions></queryOptions>");
soapEnvArray.push("</GetListItems></soap:Body></soap:Envelope>");
var soapEnv = soapEnvArray.join("");
$.ajax({
cache: false,
url: targetUrl,
type: "POST",
dataType: "xml",
data: soapEnv,
contentType: "text/xml; charset=utf-8",
beforeSend: function (xhr) {
xhr.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/sharepoint/soap/GetListItems");
},
complete: function (msg) {
if (msg.status == 200) {
var totalTaskCount = $(msg.responseXML).find("z\\:row, row").length;
$(msg.responseXML).find("z\\:row, row").each(function () {
console.log(currentItemId + ": " + $(this).attr("ows_Title"));
});
} else {
//Failure
var errorCode = $(msg.responseXML).find("errorcode").text();
var errorString = $(msg.responseXML).find("errorstring").text();
if (errorString.length === 0) {
errorString = $(msg.responseXML).find("faultstring").text();
}
errorString = errorString.replace(/(\r\n|\n|\r)/gm, "");
}
}
});
}
To make SharePoint Ajax requests easier, I would recommend picking up a copy of SharePoint CAML Query Helper for 2007, 2010, and 2013. Also, although I have never used it, you may want to consider trying the SPServices jQuery Library for SharePoint to simplify the task.

How to parse data via url and retrive with javascript

I went through sb code and wants to implement similar code. he used:
htmlItems += '<li><a href="show-feed.html?url=' + items[i].url + '">' +
items[i].name + '</a></li>';
and used this javascript code to retrive the url and parse to a method
.on('pageinit', '#show-feed-page', function () {
var url = this.getAttribute('data-url').replace(/(.*?)url=/g, '');
Application.initShowFeedPage(url);
it works well and i want to parse three values to the method eg
<a href="showProduct.html?code='+ items[i].code +',name='+items[i].name+',price='+items[i].price+'">"and need code to retrive and parse to a method
initShowProductPage(code,name,price);
First of all, your html is wrong, you have to prepare proper query string format, the updated html mark up is:
<a href="showProduct.html?code='+ items[i].code +
'&name='+items[i].name+'&price='+items[i].price+'">"
You have to access window.location.href and parse it for the query string parameters.You can write a method which parses the url, like below:
function parseURL() {
var vars = [];
var hashes = window.location.href.slice( window.location.href.indexOf('?')+1 ).split("&");
for (var i=0;i<hashes.length;i++) {
hash = hashes[i].split("=");
vars.push( hash[0] );
vars[ hash[0] ] = hash[1];
}
return vars;
}
Then you can access them using code, name and price parameters like below:
.on('pageinit', '#show-feed-page', function () {
var hashObj = parseURL();
// To get code
var code = hashObj["code"];
// To get name
var name = hashObj["name"];
// To get price
var price = hashObj["price"];
// Now call the method
initShowProductPage(code,name,price);
});

Categories

Resources