Html templates loaded asynch (JQuery/JavaScript asynch) - javascript

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.

Related

How do I use the DOM in JS to insert HTML headers for returning JSON files?

Hey guys I have a script that connects to a webservice that looks up artists and returns songs using JSON data.
I know how to use appendChild etc to return static text in a vacuum but I have a foreach loop that returns all the results from a database, within which I have to use the DOM to insert things like "Artist:" before the JSON variable.
JSfiddle: https://jsfiddle.net/21txhmr6/
function init() {
document.getElementById("submit").addEventListener("click", sendAjax);
}
function sendAjax() {
var a = document.getElementById('artist').value;
var ajaxConnection = new XMLHttpRequest();
var newDiv = document.createElement("P");
var newArtist = document.createTextNode("Artist:");
var newSong = document.createTextNode("Song:");
var newDownloads = document.createTextNode("Downloads:");
newDiv.appendChild(newArtist);
document.body.appendChild(newArtist);
ajaxConnection.addEventListener ("load", e => {
var output = "";
var artistList = JSON.parse(e.target.responseText);
artistList.forEach( curArtist => {
output = output + `${newArtist} ${curArtist.artist}</td> <td>Title: ${curArtist.title}</td> <td>Downloads: ${curArtist.downloads}</td></table>`
});
document.getElementById('jsonload').innerHTML = output;
});
// Open the connection to a given remote URL.
ajaxConnection.open("GET", `https://edward2.solent.ac.uk/~aelsbury/wadwebsite/htwebservice.php?artist=${a}`);
// Send the request.
ajaxConnection.send();
}
The main problem in your code seems to be the fact, that you create an invalid HTML structure in your loop. Each entry in the list should create a complete table row, I suppose, so just do that:
output = output + `<tr><td>${newArtist} ${curArtist.artist}</td> <td>Title: ${curArtist.title}</td> <td>Downloads: ${curArtist.downloads}</td></tr>`
and place the <table> tags around the complete output, for example in the assignment to innerHTML:
document.getElementById('jsonload').innerHTML = `<table>${output}</table>`;
It would be even better to not create HTML code, but DOM elements. This results in longer code, but makes sure that the structure is correct. You already do so for your newDiv element, just do it for the table rows and columns as well and you would not get such problems.

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.

ServiceNow UI Page GlideAjax

I created a form using UI Page and am trying to have some fields autopopulated onChange. I have a client script that works for the most part, but the issue arises when certain fields need to be dot-walked in order to be autopopulated. I've read that dot-walking will not work in client scripts for scoped applications and that a GlideAjax code will need to be used instead. I'm not familiar with GlideAjax and Script Includes, can someone help me with transitioning my code?
My current client script looks like this:
function beneficiary_1(){
var usr = g_user.userID;
var related = $('family_member_1').value;
var rec = new GlideRecord('hr_beneficiary');
rec.addQuery('employee',usr);
rec.addQuery('sys_id',related);
rec.query(dataReturned);
}
function dataReturned(rec){
//autopopulate the beneficiary fields pending on the user selection
if(rec.next()) {
$('fm1_ssn').value = rec.ssn;
$('fm1_address').value = rec.beneficiary_contact.address;
$('fm1_email').value = rec.beneficiary_contact.email;
$('fm1_phone').value = rec.beneficiary_contact.mobile_phone;
var dob = rec.date_of_birth;
var arr = dob.split("-");
var date = arr[1] + "/"+ arr[2] + "/" + arr[0] ;
$('fm1_date_of_birth').value = date;
}
}
fm1_address, fm1_email, and fm1_phone do not auto populate because the value is dot walking from the HR_Beneficiary table to the HR_Emergency_Contact table.
How can I transform the above code to GlideAjax format?
I haven't tested this code so you may need to debug it, but hopefully gets you on the right track. However there are a couple of steps for this.
Create a script include that pull the data and send a response to an ajax call.
Call this script include from a client script using GlideAjax.
Handle the AJAX response and populate the form.
This is part of the client script in #2
A couple of good websites to look at for this
GlideAjax documentation for reference
Returning multiple values with GlideAjax
1. Script Include - Here you will create your method to pull the data and respond to an ajax call.
This script include object has the following details
Name: BeneficiaryContact
Parateters:
sysparm_my_userid - user ID of the employee
sysparm_my_relativeid - relative sys_id
Make certain to check "Client callable" in the script include options.
var BeneficiaryContact = Class.create();
BeneficiaryContact.prototype = Object.extendsObject(AbstractAjaxProcessor, {
getContact : function() {
// parameters
var userID = this.getParameter('sysparm_my_userid');
var relativeID = this.getParameter('sysparm_my_relativeid');
// query
var rec = new GlideRecord('hr_beneficiary');
rec.addQuery('employee', userID);
rec.addQuery('sys_id', relativeID);
rec.query();
// build object
var obj = {};
obj.has_value = rec.hasNext(); // set if a record was found
// populate object
if(rec.next()) {
obj.ssn = rec.ssn;
obj.date_of_birth = rec.date_of_birth.toString();
obj.address = rec.beneficiary_contact.address.toString();
obj.email = rec.beneficiary_contact.email.toString();
obj.mobile_phone = rec.beneficiary_contact.mobile_phone.toString();
}
// encode to json
var json = new JSON();
var data = json.encode(obj);
return data;
},
type : "BeneficiaryContact"
});
2. Client Script - Here you will call BeneficiaryContact from #1 with a client script
function onChange(control, oldValue, newValue, isLoading, isTemplate) {
if (isLoading || newValue === '') {
return;
}
var usr = g_user.userID;
var related = $('family_member_1').value;
var ga = new GlideAjax('BeneficiaryContact'); // call the object
ga.addParam('sysparm_name', 'getContact'); // call the function
ga.addParam('sysparm_my_userid', usr); // pass in userID
ga.addParam('sysparm_my_relativeid', related); // pass in relative sys_id
ga.getXML(populateBeneficiary);
}
3. Handle AJAX response - Deal with the response from #2
This is part of your client script
Here I put in the answer.has_value check as an example, but you may want to remove that until this works and you're done debugging.
function populateBeneficiary(response) {
var answer = response.responseXML.documentElement.getAttribute("answer");
answer = answer.evalJSON(); // convert json in to an object
// check if a value was found
if (answer.has_value) {
var dob = answer.date_of_birth;
var arr = dob.split("-");
var date = arr[1] + "/"+ arr[2] + "/" + arr[0];
$('fm1_ssn').value = answer.ssn;
$('fm1_address').value = answer.address;
$('fm1_email').value = answer.email;
$('fm1_phone').value = answer.mobile_phone;
$('fm1_date_of_birth').value = date;
}
else {
g_form.addErrorMessage('A beneficiary was not found.');
}
}

Cache each JSON search query with localStorage

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!

Javascript loading CSV file into an array

I am developing a web page in Wordpress. The webpage needs to have a combobox with all counties. I have a dataset in csv format which has some 10k rows for all these counties.
When the user selects a county in the dropdown, I want only the selected county data displayed in the web page. This is my requirement.
In wordpress, my web page I am adding the js file using
<script type="text/javascript" src="http://xxx/wp content/uploads/2014/05/countyList1.js"></script>
and the code for webpage dropdown is
<select name="county" id="county" onload="setCounties();" onchange="getSelectedCountyData();"></select>
In countyList1.js file I have the setCounties() and getSelectedCountyData() functions.
So far I can see the dropdown with counties list. I don't know how to read the CSV file and apply the selected county filter to this list.
I tried the FileReader object and I can load the CSV contents on the web page but I don't want the user to select the CSV. I have the dataset already.
I am trying to use this jquery.csv-0.71 library from this SO post How to read data From *.CSV file using javascript? but I need help.
Here's the code which gets called when a county is selected
function getSelectedCountyData() {
cntrySel = document.getElementById('county');
//selCty = countyList[cntrySel.value];
handleFiles();
}
function handleFiles() {
$(document).ready(function () {
$.ajax({
type: "GET",
url: "D:\Docs\Desktop\csvfile.csv",
dataType: "csv",
success: function (data) { processData(data); }
});
});
}
function processData(allText) {
var allTextLines = allText.split(/\r\n|\n/);
var headers = allTextLines[0].split(',');
var lines = [];
for (var i = 1; i < allTextLines.length; i++) {
var data = allTextLines[i].split(',');
if (data.length == headers.length) {
var tarr = [];
for (var j = 0; j < headers.length; j++) {
tarr.push(headers[j] + ":" + data[j]);
}
lines.push(tarr);
}
}
console.log(lines);
drawOutput(lines);
}
function drawOutput(lines) {
//Clear previous data
document.getElementById("output").innerHTML = "";
var table = document.createElement("table");
for (var i = 0; i < lines.length; i++) {
var row = table.insertRow(-1);
for (var j = 0; j < lines[i].length; j++) {
var firstNameCell = row.insertCell(-1);
firstNameCell.appendChild(document.createTextNode(lines[i][j]));
}
}
document.getElementById("output").appendChild(table);
}
I highly recommend looking into this plugin:
http://github.com/evanplaice/jquery-csv/
I used this for a project handling large CSV files and it handles parsing a CSV into an array quite well. You can use this to call a local file that you specify in your code, also, so you are not dependent on a file upload.
Once you include the plugin above, you can essentially parse the CSV using the following:
$.ajax({
url: "pathto/filename.csv",
async: false,
success: function (csvd) {
data = $.csv.toArrays(csvd);
},
dataType: "text",
complete: function () {
// call a function on complete
}
});
Everything will then live in the array data for you to manipulate as you need. I can provide further examples for handling the array data if you need.
There are a lot of great examples available on the plugin page to do a variety of things, too.
You can't use AJAX to fetch files from the user machine. This is absolutely the wrong way to go about it.
Use the FileReader API:
<input type="file" id="file input">
js:
console.log(document.getElementById("file input").files); // list of File objects
var file = document.getElementById("file input").files[0];
var reader = new FileReader();
content = reader.readAsText(file);
console.log(content);
Then parse content as CSV. Keep in mind that your parser currently does not deal with escaped values in CSV like: value1,value2,"value 3","value ""4"""
If your not overly worried about the size of the file then it may be easier for you to store the data as a JS object in another file and import it in your . Either synchronously or asynchronously using the syntax <script src="countries.js" async></script>. Saves on you needing to import the file and parse it.
However, i can see why you wouldnt want to rewrite 10000 entries so here's a basic object orientated csv parser i wrote.
function requestCSV(f,c){return new CSVAJAX(f,c);};
function CSVAJAX(filepath,callback)
{
this.request = new XMLHttpRequest();
this.request.timeout = 10000;
this.request.open("GET", filepath, true);
this.request.parent = this;
this.callback = callback;
this.request.onload = function()
{
var d = this.response.split('\n'); /*1st separator*/
var i = d.length;
while(i--)
{
if(d[i] !== "")
d[i] = d[i].split(','); /*2nd separator*/
else
d.splice(i,1);
}
this.parent.response = d;
if(typeof this.parent.callback !== "undefined")
this.parent.callback(d);
};
this.request.send();
};
Which can be used like this;
var foo = requestCSV("csvfile.csv",drawlines(lines));
The first parameter is the file, relative to the position of your html file in this case.
The second parameter is an optional callback function the runs when the file has been completely loaded.
If your file has non-separating commmas then it wont get on with this, as it just creates 2d arrays by chopping at returns and commas. You might want to look into regexp if you need that functionality.
//THIS works
"1234","ABCD" \n
"!#£$" \n
//Gives you
[
[
1234,
'ABCD'
],
[
'!#£$'
]
]
//This DOESN'T!
"12,34","AB,CD" \n
"!#,£$" \n
//Gives you
[
[
'"12',
'34"',
'"AB',
'CD'
]
[
'"!#',
'£$'
]
]
If your not used to the OO methods; they create a new object (like a number, string, array) with their own local functions and variables via a 'constructor' function. Very handy in certain situations. This function could be used to load 10 different files with different callbacks all at the same time(depending on your level of csv love! )
This is what I used to use a csv file into an array. Couldn't get the above answers to work, but this worked for me.
$(document).ready(function() {
"use strict";
$.ajax({
type: "GET",
url: "../files/icd10List.csv",
dataType: "text",
success: function(data) {processData(data);}
});
});
function processData(icd10Codes) {
"use strict";
var input = $.csv.toArrays(icd10Codes);
$("#test").append(input);
}
Used the jQuery-CSV Plug-in linked above.
The original code works fine for reading and separating the csv file data but you need to change the data type from csv to text.

Categories

Resources