I have a large datasets which i am retrieving via tableau API call. im using async await to call the data and storing this as txt extension.
How i am retrieving the data is by using this script below, script is working as expected and the logic i came out with is
Retrieve data records via api call
Append data into div element
Once data is fully loaded to div, use file streamer to save records as txt file
script used -
<!DOCTYPE html>
<html>
<head>
<title>getData() Basic Example</title>
<script type="text/javascript" src="https://public.tableau.com/javascripts/api/tableau-2.min.js"></script>
<script type="text/javascript">
var viz, sheet, table;
function initViz() {
var containerDiv = document.getElementById("vizContainer"),
url = "http://public.tableau.com/views/RegionalSampleWorkbook/Storms",
options = {
hideTabs: true,
hideToolbar: true,
onFirstInteractive: function () {
document.getElementById('getData').disabled = false; // Enable our button
}
};
viz = new tableau.Viz(containerDiv, url, options);
}
async function savefile(data){
const newHandle = await window.showSaveFilePicker();
const writableStream = await newHandle.createWritable();
await writableStream.write(data)
await writableStream.close();
}
function getUnderlyingData(){
sheet = viz.getWorkbook().getActiveSheet().getWorksheets().get("Storm Map Sheet");
sheet.getUnderlyingDataAsync().then(function(dataTable){
let _tmpdata = ''
for(let i = 0; i < dataTable.getData().length;i++){
for(let a = 0; a < dataTable.getColumns().length;a++){
_tmpdata = dataTable.getData()[i][a].formattedValue;
document.getElementById('storage').innerHTML += _tmpdata
}
}
let whatisthis = document.getElementById('storage').innerHTML
savefile(whatisthis)
});
}
</script>
</head>
<body onload="initViz();">
<div class="page-header">
<button id="getData" onclick="getUnderlyingData()" class="btn" disabled>Get Data</button>
<div id="storage"></div>
</div>
<div id="vizContainer" style="width:600px; height:600px;"></div>
<div id="dataTarget"></div>
</body>
</html>
This is working as expected but what worries me is when i have super large volume of data, the alternative logic i have in mind which i tried to implement is as follow
create streamer inside getunderlyingdata function
append data directly in for loop
New logic i tried, ets say saveFile() does not exist and writableStream are directy implemented in getUnderlyingData, this is script i tried
async function getUnderlyingData(){
// save file to location
const newHandle = await window.showSaveFilePicker();
const writableStream = await newHandle.createWritable();
sheet = viz.getWorkbook().getActiveSheet().getWorksheets().get("Storm Map Sheet");
sheet.getUnderlyingDataAsync().then(async function(dataTable){
let _tmpdata = ''
for(let i = 0; i < dataTable.getData().length;i++){
for(let a = 0; a < dataTable.getColumns().length;a++){
_tmpdata = dataTable.getData()[i][a].formattedValue;
// Write data to stream
await writableStream.write(_tmpdata)
}
}
});
// Close Sream
await writableStream.close();
}
It was not able to capture the data is because the page get reloaded as soon as i tried to save to a location . Is it possible to disable the page reload when a location is selected to save the file ?
Related
I'm attempting to create a simple to-do list and I've encountered two problems:
After refreshing the page, all the created elements are no longer visible on the page despite being in local storage.
After refreshing the page and submitting new values to the input, localStorage overwrites itself.
Despite that, the items displayed from the input fields are from the previous localStorage, which no longer exists (I really hope this makes sense).
const inputEl = document.getElementById("inputEl")
const submitBtn = document.getElementById("submit")
const clearBtn = document.getElementById("clearBtn")
const todoListContainer = document.getElementById("todoList")
const taskContainer = document.querySelector(".task")
const cancelBtn = document.querySelector(".cancelBtn")
const doneBtn = document.querySelector(".doneBtn")
const errorMsg = document.querySelector(".error")
let localStorageContent = localStorage.getItem("tasks")
let tasksItem = JSON.parse(localStorageContent)
let tasks = []
function createTask() {
if (inputEl.value.length != 0) {
const newDiv = document.createElement("div")
newDiv.classList.add("task")
const newParagraph = document.createElement("p")
const newCancelBtn = document.createElement("button")
newCancelBtn.classList.add("cancelBtn")
newCancelBtn.textContent = "X"
const newDoneBtn = document.createElement("button")
newDoneBtn.classList.add("doneBtn")
newDoneBtn.textContent = "Done"
todoListContainer.appendChild(newDiv)
newDiv.appendChild(newParagraph)
newDiv.appendChild(newCancelBtn)
newDiv.appendChild(newDoneBtn)
//^^ Creating a container for a new task, with all its elements and assigning the classes^^
tasks.push(inputEl.value)
inputEl.value = ""
for (let i = 0; i < tasks.length; i++) {
localStorage.setItem("tasks", JSON.stringify(tasks))
newParagraph.textContent = JSON.parse(localStorageContent)[i]
}
errorMsg.textContent = ""
} else {
errorMsg.textContent = "You have to type something in!"
errorMsg.classList.toggle("visibility")
}
}
submitBtn.addEventListener("click", () => {
createTask()
})
clearBtn.addEventListener("click", () => {
localStorage.clear()
})
HTML code below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/style.css">
<script src="/script.js" defer></script>
<title>To-do list</title>
</head>
<body>
<h2 class="error visibility"></h2>
<div id="todoList">
<h1>To-Do List</h1>
<input type="text" name="" id="inputEl" placeholder="Add an item!">
<button type="submitBtn" id="submit">Submit</button>
<button id="clearBtn">Clear list</button>
<div class="task">
</div>
</div>
</body>
</html>
After refreshing the page, all the created elements are no longer visible on the page despite being in local storage
That is because you are rendering the HTML only after the click event and not on page load. To render the HTML for existing tasks stored in the localStorage you have to write a code that loops over your existing tasks in the tasksItem and applies the rendering logic to it.
I would suggest splitting the rendering code from your createTask() function and create a new function for it (for example renderTask()), then you can use it inside a loop on page load and also call the function once a new task is created in the createTask() function.
window.addEventListener('load', (event) => {
// Your read, loop and render logic goes here
})
After refreshing the page and submitting new values to the input, localStorage overwrites itself.
That's because you are actually overriding the tasks in the localStorage. To keep existing tasks, you have to use your tasksItem variable instead of the blank tasks array to create your tasks in and save them to the localStorage.
So, instead of:
tasks.push(inputEl.value)
You would use:
tasksItem.push(inputEl.value)
The same goes for:
for (let i = 0; i < tasksItem.length; i++) {
localStorage.setItem("tasks", JSON.stringify(tasksItem))
// …
}
In JavaScript, how to export plotly plot as a self-contained html file? Just like in R, we can use saveWidget function. How to do it with JavaScript?
Edit
I tried to use
var a = document.body.appendChild(
document.createElement("a")
);
a.download = "export.html";
a.href = "data:text/html," + document.getElementById("myDiv").innerHTML;
a.click();
But it does work well because it loses all the event, etc.
https://codepen.io/anon/pen/XqZqWX
You can download plotly.js and then call Plotly.newPlot method.
Here are step details.
Get plotly script text
async function getPlotlyScript() {
// fetch
const plotlyRes = await fetch('https://cdn.plot.ly/plotly-latest.js')
// get response as text
return await plotlyRes.text()
}
Get plotly chart data
We need to pass the current chart state in Plot.newPlot method. If the chart data and layout are not controlled by your code, don't worry. The DOM element where plotly builds the chart contains data and layout.
function getChartState () {
const el = document.getElementById('your plotly container id')
return {
data: el.data, // current data
layout: el.layout // current layout
}
}
Compose HTML
Now we have everything to compose our HTML. Don't forget to:
add charset="utf-8" because plotly script has special characters.
escape closing script tag to make browser consider it as a string, not as a tag
async function getHtml() {
const plotlyModuleText = await getPlotlyScript()
const state = getChartState()
return `
<head>
<meta charset="utf-8" />
</head>
<script type="text/javascript">
${plotlyModuleText}
<\/script>
<div id="plotly-output"></div>
<script type="text/javascript">
Plotly.newPlot(
'plotly-output',
${JSON.stringify(state.data)},
${JSON.stringify(state.layout)}
)
<\/script>
`
}
Download HTML document
async function exportToHtml () {
// Create URL
const blob = new Blob([await getHtml()], { type: 'text/html' })
const url = URL.createObjectURL(blob)
// Create downloader
const downloader = document.createElement('a')
downloader.href = url
downloader.download = 'export.html'
// Trigger click
downloader.click()
// Clean up
URL.revokeObjectURL(url)
}
exportToHtml()
I would like to start by saying that (coming from c++ and python) I am totally new to JS and so I welcome any wise suggestions regarding my code.
I wish to read a number of files using the HTML5 file API, then open them with JS, perform some manipulation and download the results zipped. My problem is that reading the files seems to be an asynchronous operation, and I don't really see an elegant way to wait for them all to finish and then zip the results.
One possible solution is presented here:
https://stackoverflow.com/a/17491515
but I am wondering if one can do better than using a global flag.
I also have a problem with retrieving the result from the async function as I have no idea how to get back new_file_list in changeCharsInFiles.
Thank you!
Code example:
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body >
<div class="container">
<div class="jumbotron">
<h3>Add Files Here</h3>
<input type="file" id="the-file-field" multiple>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js"></script>
<script src="http://cdn.jsdelivr.net/g/filesaver.js"></script>
<script>
########### SEE JS BELOW #####################
</script>
</body>
JS:
if (window.File && window.FileReader && window.FileList && window.Blob) {
//functions
function zipMyFilesAndSave(file_list){
var zip = new JSZip();
for (var i = 0; i<file_list.length; i+=1)
{
zip.file(file_list[i].name, file_list[i] );
}
zip.generateAsync({type:"blob"}).then(
function (blob) {
saveAs(blob, "hello.zip");
},
function (err) {
jQuery("#blob").text(err);
});
}
function changeCharsInFiles(file_list){
var new_file_list = [];
for (var i = 0; i<file_list.length; i+=1)
{
var file = file_list[i]
var reader = new FileReader();
reader.onload = function() {
//alert(reader.result);
var txt = reader.result;
console.log("txt: ",txt)
var new_txt = ""
var allTextLines = txt.split(/\r\n|\n/);
for (var j = 0; j<allTextLines.length; j+=1)
{
var res = allTextLines[j].replace("a", "A");
res = res.replace("b", "B");
res = res.replace("c", "C");
res = res.replace("d", "D");
new_txt += res + "\n"
}
console.log("new_txt: ", new_txt)
var new_file = new Blob([new_txt], {type: "text/plain"});
new_file_list.push(new_file); //<---------------------------how do I get this back?
}
reader.readAsText(file);
}
return new_file_list;
}
//watcher
$( "#the-file-field" ).change(function() {
console.log("files have been chosen")
var file_list = this.files
file_list = changeCharsInFiles(file_list)
zipMyFilesAndSave(file_list)
});
} else {
alert('The File APIs are not fully supported in this browser.');
}
Try reading up on the Promise class, it was developed to make asynchronous operations easier:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
In your case, you could use it like this:
function changeCharsInFiles(file_list){
let promises = [];
for (let file of file_list) {
let filePromise = new Promise(resolve => {
let reader = new FileReader();
reader.readAsText(file);
reader.onload = () => resolve(reader.result);
});
promises.push(filePromise);
}
Promise.all(promises).then(fileContents => {
// fileContents will be an array containing
// the contents of the files, perform the
// character replacements and other transformations
// here as needed
});
}
This is just a rough outline of the solution. I'd suggest you experiment a bit with Promises first (it can be a fairly deep topic) to figure out the basic principles, and then apply something like the above.
I am currently researching the possibility to grabbing data from the Tableau report(s) via the JavaScript API but the closet I can get to grabbing values from a graph after filtering is selecting the value via the selectSingleValue() method.
For example: JavaScript API Tutorial
In the API tutorial tab called 'Select'. One of the examples selects the row "Marcao Sao, China". Is it possible to extract that numerical value of $52.0k ?
I have tried looking into the Objects returned (via FireBug) but I cannot seem to locate the right object. My recent location was in getActiveSheets().
Any help would be appreciated.
In the JavaScript API tutorial tab 'Events' it shows you how to add an event listener to return the selected marks. You can then loop through the marks to get the values you want.
Copy the below code block into a file, save as html and open in your favourite web browser (tested on ie11).
<html>
<head>
<meta charset="utf-8">
<title>Tableau 8 Javascrip API</title>
<script type="text/javascript" src="http://public.tableausoftware.com/javascripts/api/tableau_v8.js"></script>
<script type="text/javascript">
/////////////////////
// Global variables
var viz, workbook, activeSheet
// function called by viz on marks being selected in the workbook
function onMarksSelection(marksEvent) {
return marksEvent.getMarksAsync().then(reportSelectedMarks);
}
function reportSelectedMarks(marks) {
for (var markIndex = 0; markIndex < marks.length; markIndex++) {
var pairs = marks[markIndex].getPairs();
for (var pairIndex = 0; pairIndex < pairs.length; pairIndex++) {
var pair = pairs[pairIndex];
if (pair.fieldName == "AVG(F: GDP per capita (curr $))") {
alert("You selected a country with an avg GPD per capita of " + pair.formattedValue);
}
}
}
}
// Initialise the viz to hold the workbook
function initializeViz(){
var placeholderDiv = document.getElementById("tableauViz");
var url = "http://public.tableausoftware.com/views/WorldIndicators/GDPpercapita?Region=";
var options = {
width: "800px", //width: placeholderDiv.offsetWidth,
height: "400px", //height: placeholderDiv.offsetHeight,
hideTabs: true,
hideToolbar: true,
onFirstInteractive: function () {
workbook = viz.getWorkbook();
activeSheet = workbook.getActiveSheet();
}
};
viz = new tableauSoftware.Viz(placeholderDiv, url, options);
// Add event listener
viz.addEventListener(tableauSoftware.TableauEventName.MARKS_SELECTION, onMarksSelection);
}
</script>
</head>
<body>
<!-- Tableau view goes here -->
<div id="tableauViz" style="height:1200px; width:1200px"\></div>
<script type='text/javascript'>
//Initialize the viz after the div is created
initializeViz();
</script>
</body>
</html>
I'm trying to use Google's Images API to search an image and put it into my html document as a div. This is what I have so far, but nothing seems to be appearing. This is parts from http://code.google.com/apis/imagesearch/v1/devguide.html. This is my first time using an API, so I'm not sure what is really going on.
<html>
<head>
<title>Art Project FTW</title>
</head>
<body>
<br>
<br>
<form name="upload" method="post" action="parse_image.php" enctype="multipart/form-data">
<input type="file" name="Image"><br>
<input type="submit" value="Upload Image">
</form>
<script type="text/javascript" src="https://www.google.com/jsapi?key=xxx"></script>
<script type="text/javascript" charset="utf-8">
google.load('search', '1');
function searchComplete(searcher) {
// Check that we got results
if (searcher.results && searcher.results.length > 0) {
// Grab our content div, clear it.
var contentDiv = document.getElementById('content');
contentDiv.innerHTML = '';
// Loop through our results, printing them to the page.
var results = searcher.results;
for (var i = 0; i < results.length; i++) {
// For each result write it's title and image to the screen
var result = results[i];
var imgContainer = document.createElement('div');
var title = document.createElement('h2');
// We use titleNoFormatting so that no HTML tags are left in the title
title.innerHTML = result.titleNoFormatting;
var newImg = document.createElement('img');
// There is also a result.url property which has the escaped version
newImg.src = result.tbUrl;
imgContainer.appendChild(title);
imgContainer.appendChild(newImg);
// Put our title + image in the content
contentDiv.appendChild(imgContainer);
}
}
}
function onload() {
// Our ImageSearch instance.
var imageSearch = new google.search.ImageSearch();
// Restrict to extra large images only
imageSearch.setRestriction(google.search.ImageSearch.RESTRICT_IMAGESIZE,
google.search.ImageSearch.IMAGESIZE_MEDIUM);
// Here we set a callback so that anytime a search is executed, it will call
// the searchComplete function and pass it our ImageSearch searcher.
// When a search completes, our ImageSearch object is automatically
// populated with the results.
imageSearch.setSearchCompleteCallback(this, searchComplete, [imageSearch]);
// Find me a beautiful car.
imageSearch.execute("Subaru STI");
}
google.setonloadCallback(onload);
</script>
</body>
</html>
Thanks in advance!
It can't work because you are looking for a HTMLElement that has the ID='content', you haven´t anyone element with that ID
Try putting your js functions within <head></head>