I am trying to load multiple files using jQuery's get function. A minimal example looks like this:
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<select name="file-select" id="file-select" multiple>
<option>http://url.to.file/Test1.txt</option>
<option>http://url.to.file/Test2.txt</option>
</select>
<script>
$(document).ready(function() {
$('#file-select').change(function () {
let loaded_files_list = new Array();
let promise = new Promise(function(myResolve,myReject) {
$("#file-select option:selected").each(function () {
file_name = $(this).val();
$.get($(this).val(), function(response) {
data = response;
// Do something with data
console.log("file select length: "+$("#file-select option:selected").length);
loaded_files_list.push(file_name);
console.log('loaded files length: '+loaded_files_list.length)
if(loaded_files_list.length == $("#file-select option:selected").length) {
console.log('Entered if clause');
myResolve(loaded_files_list);
}
else {
myReject('List is not complete yet');
}
});
});
});
promise.then(
function(value) {
console.log('List is complete!');
console.log(value);
},
function(error) {
console.log(error);
}
);
});
});
</script>
</body>
</html>
Now if I select a single file this works just like expected and I can see the list with a single item in the console. However if I I select both files, the if clause is entered after both files are loaded, but it seems myResolve is not called as neither List is complete! is displayed nor the list with two elements. What am I doing incorrect here?
$(document).ready(() => {
// make this a function that "waits" for the await command when
// used on promises
$('#file-select').change(async () => {
// declare an array that we will use to store our HTTP requests
const filesList = [];
// extract the clicked options
const selectedOpts = $('#file-select').children('option:selected')
// loop over the clicked options
selectedOpts.each((index, opt) => {
// extract the text value from the option
const link = opt.text
// push the HTTP request to the filesList array without
// waiting for the response
filesList.push($.get(link))
})
// wait for the HTTP requests to to complete before
// continuing
const responses = await Promise.all(filesList)
// loaded all files
responses.forEach(data => {
console.log(data) // => file contents
})
});
});
Related
I'm messing around w/ Office Dialog for Add-Ins in JS. I've got it so far where I can open a dialog, capture input to console, run a function from a button and close the dialog box, but I can't seem to get my function to interact with Excel. It's lost context I beleive, I tried using this and I get no errors, but it doesn't work --> var context = new Excel.RequestContext().
Here is my open function and my main function and the end function.
main.js
export async function helloworld(event) {
try {
await Excel.run(async (context) => {
//Start Func
console.log("BEFORE OPEN UI");
openDialog("/yo/dist/dialog.html", 30, 20);
console.log("AFTER OPEN UI");
await context.sync()
.then(function () {
console.log("AFTER SYNC UI");
var ws = context.workbook.worksheets.getActiveWorksheet();
var range = ws.getRange("A1:D5");
range.select();
})
//End Func
await context.sync();
});
} catch (error) {
console.error(error);
}
console.log("EVENT COMPLETEED HELLOW");
//event.completed();
}
open.js
function openDialog(RelURLStr, H, W) {
Office.context.ui.displayDialogAsync(window.location.origin + RelURLStr,
{ height: H, width: W }, dialogCallback);
}
run func //this gets ran, but nothing output to worksheet and no errors.
function dobuttonrun(event) {
console.log("ENDING");
var context = new Excel.RequestContext()
var ws = context.workbook.worksheets.getActiveWorksheet();
var fakedatarng = ws.getRange("A1");
fakedatarng.values = "TEST";
return context.sync();
event.completed();
}
function getGlobal() {
return typeof self !== "undefined"
? self
: typeof window !== "undefined"
? window
: typeof global !== "undefined"
? global
: undefined;
}
const g = getGlobal();
// The add-in command functions need to be available in global scope
g.dobuttonrun = dobuttonrun;
Opening a popup has UX issues on web clients. If you need to support excel web, it will ask the user a confirmation for the dialog each time and cannot by bypassed; Excel is asking not the browser!
I strongly suggest you, to use a Popup from your UX framework. If you are using Fluent UI, the microsoft suggested framework that keeps look and feel of office please refer to this page.
In case that you need a dialog solution, keep in mind that you are running a new page /yo/dist/dialog.html you'll loose all the variables and context from your parent webpage:
If you are using any SPA javascript framework like react.js or VUE.js, another clean app will be rendered.
BTW You can achieve your need implementing a simple communication as follows:
// Office.Dialog type
let dialog;
export async function helloworld(event) {
Office.context.ui.displayDialogAsync(
'/yo/dist/dialog',
{ promptBeforeOpen: true, height: 70, width: 50, displayInIframe: false },
(result) => {
if (result.status === Office.AsyncResultStatus.Failed) {
// unable to manage status
throw new Error(result.error.message)
}
else {
dialog = result.value;
dialog.addEventHandler(Office.EventType.DialogMessageReceived, messageHandler);
}
}
)
}
/**
* Assign the project to an employee.
* #param {Object} arg - Event handler args
* #param {string} arg.message - Message as string (NOT js OBJECT)
* #param {string} arg.origin - Message Origin
*/
const messageHandler = (arg) => {
// messaging can send only strings.
// If you need to pass complex JSON you need to stringify and parse objects
const message = JSON.parse(arg.message)
if (message.type === "execution-canceled") {
// you have all props of message like message.reason
dialog.close();
return
}
if (message.type === "execution-confirmed") {
// execute than close popup
// if you need to close popup than execute, just run dialog.close()
// before doStuff() and remove finally method call
doStuff()
.then(() => console.log("EVENT COMPLETEED HELLOW"))
.catch(error => console.error(error))
.finally(() => dialog.close())
}
}
const doStuff = async () => Excel.run(async (context) => {
// you don't need context.sync() before interacting excel
var ws = context.workbook.worksheets.getActiveWorksheet();
var range = ws.getRange("A1:D5");
range.select();
await context.sync();
})
On your dialog page you need to wait for Office.initialize in order to use Office.context.ui.messaging apis, than you can simply send your object.
const send = (message) => {
Office.context.ui.messageParent(JSON.stringify(message));
}
Office.initialize = () => {
// render page or activate button
}
const onExecutionConfirmed = () => send({ type: 'execution-confirmed' })
const onExcecutionCanceled = () => send({
type: 'execution-canceled',
reason: "additional infos can be placed in any props of this serialized JSON object"
})
Here is what I finished with after #CLAudio detailed answer.
commands.js
Office.onReady(() => {
// If needed, Office.js is ready to be called
});
export async function helloworld(event) {
try {
await Excel.run(async (context) => {
//Start Func
openDialog("/yo/dist/dialog.html", 30, 20);
//End Func
await context.sync();
});
} catch (error) {
console.error(error);
}
event.completed();
}
const doStuff = async () => Excel.run(async (context) => {
// you don't need context.sync() before interacting excel
var ws = context.workbook.worksheets.getActiveWorksheet();
var range = ws.getRange("A1:D5");
range.select();
await context.sync();
})
//////////////////////////////////////////
let dialog;
function openDialog(HTMLUrl, H, W) {
Office.context.ui.displayDialogAsync(window.location.origin + HTMLUrl, { promptBeforeOpen: true, height: H, width: W, displayInIframe: false },
(asyncResult) => {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
// In addition to general system errors, there are 3 specific errors for
// displayDialogAsync that you can handle individually.
switch (asyncResult.error.code) {
case 12004:
console.log("Domain is not trusted");
break;
case 12005:
console.log("HTTPS is required");
break;
case 12007:
console.log("A dialog is already opened.");
break;
default:
console.log(asyncResult.error.message);
break;
}
} else {
dialog = asyncResult.value;
/*Messages are sent by developers programatically from the dialog using office.context.ui.messageParent(...)*/
dialog.addEventHandler(Office.EventType.DialogMessageReceived, messageHandler);
/*Events are sent by the platform in response to user actions or errors. For example, the dialog is closed via the 'x' button*/
dialog.addEventHandler(Office.EventType.DialogEventReceived, eventHandler);
}
}
)
}
function eventHandler(arg) {
// In addition to general system errors, there are 2 specific errors
// and one event that you can handle individually.
switch (arg.error) {
case 12002:
console.log("Cannot load URL, no such page or bad URL syntax.");
break;
case 12003:
console.log("HTTPS is required.");
break;
case 12006:
// The dialog was closed, typically because the user the pressed X button.
console.log("Dialog closed by user");
break;
default:
console.log("Undefined error in dialog window");
break;
}
}
/**
* Assign the project to an employee.
* #param {Object} arg - Event handler args
* #param {string} arg.message - Message as string (NOT js OBJECT)
* #param {string} arg.origin - Message Origin
*/
const messageHandler = (arg) => {
// messaging can send only strings.
// If you need to pass complex JSON you need to stringify and parse objects
const message = JSON.parse(arg.message)
console.log("message:")
console.log(message)
if (message.type === "execution-canceled") {
// you have all props of message like message.reason
dialog.close();
return
}
if (message.type === "execution-confirmed") {
// execute than close popup
// if you need to close popup than execute, just run dialog.close()
// before doStuff() and remove finally method call
doStuff()
.then(() => console.log("doStuff .then"))
.catch(error => console.error(error))
.finally(() => dialog.close())
}
}
//////////////////////////////////////////
dialog.html
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Contoso Task Pane Add-in</title>
<script src="https://appsforoffice.microsoft.com/lib/1.1/hosted/office.js"></script>
<!-- For the Office UI Fabric, go to http://aka.ms/office-ui-fabric to learn more. -->
<link rel="stylesheet" href="https://appsforoffice.microsoft.com/fabric/2.1.0/fabric.min.css">
<link rel="stylesheet" href="https://appsforoffice.microsoft.com/fabric/2.1.0/fabric.components.min.css">
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.2.1.min.js"></script>
<script defer="defer" src="dialog.js"></script>
</head>
<body>
<p class="ms-font-xxl ms-fontColor-neutralSecondary ms-fontWeight-semilight">Pick a number</p>
<button class="ms-Button ms-Button--primary" id="run">
<span class="ms-Button-icon"><i class="ms-Icon ms-Icon--plus"></i></span>
<span class="ms-Button-label" id="runbutton-text">RUN</span>
<span class="ms-Button-description" id="runbutton-desc">Run Func</span>
</button>
<button class="ms-Button ms-Button--primary" id="close">
<span class="ms-Button-icon"><i class="ms-Icon ms-Icon--plus"></i></span>
<span class="ms-Button-label" id="closebutton-text">EXIT</span>
<span class="ms-Button-description" id="closebutton-desc">Exit Func</span>
</button>
<form>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname"><br>
<label for="lname">Last name:</label><br>
<input type="text" id="lname" name="lname">
</form>
</body>
</html>
dialog.js
Office.initialize = () => {
document.getElementById("run").onclick = onExecutionConfirmed;
document.getElementById("close").onclick = onExcecutionCanceled;
}
const send = (message) => {
Office.context.ui.messageParent(JSON.stringify(message));
}
const onExecutionConfirmed = () => send({ type: 'execution-confirmed' })
const onExcecutionCanceled = () => send({
type: 'execution-canceled',
reason: "additional infos can be placed in any props of this serialized JSON object"
})
I am using an api which has names of exercises in it. I want to make a search box with an autocomplete drop down (like suggetions google gives you before finish up what you type) But i want to use the api as results.
I managed to get the readings out of the api.
let data;
async function getExercises () {
let url = 'https://wger.de/api/v2/exercise/?format=json'
while (url) {
const res = await fetch(url)
data = await res.json()
for (const item of data.results) {
console.log(item.name)
}
url = data.next
}
$(document).ready(function() {
BindControls();
});
function BindControls() {
$('#exercise-search').autocomplete({
source: data,
minLength: 0,
scroll: true
}).focus(function() {
$(this).autocomplete("search", "");
});
}
}
I am trying to make the drop down using the api results but cant get it to work.
<input id="exercise-search" class="form-control" type="text" name="data">
p, div, input {
font: 16px Calibri;
}
.ui-autocomplete {
cursor:pointer;
height:120px;
overflow-y:scroll;
}
these are the libraries i imported:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css" rel="stylesheet" type="text/css"/>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
the console log in my browser doesnt seem to have any errors.
Any insight would be appreciated
Try something like this https://jsfiddle.net/voaf1sLg/.
What it boils down to is that you do not store those search results anywhere. I've modified the code for your async function to return the full array with all results (after all those 33 API calls, eh!), then return a fulfilled promise with said auto-complete entries. Modify your code accordingly.
async function getEx() {
let url = 'https://wger.de/api/v2/exercise/?format=json'
const array = [];
while (url) {
const res = await fetch(url)
data = await res.json()
for (const item of data.results) {
console.log(item.name)
array.push(item.name);
}
url = data.next
}
return array;
}
$(function() {
let tags = [];
getEx().then(res => {
$( "#tags" ).autocomplete({
source: res
});
});
} );
I can see your total result count is 685,its better if we can full those records in one shot. but if its not possible then i just modify your code accordingly using recurssion.
var sourcearray = []
var getData = function(url) {
$.getJSON(url, function(d) {
Array.prototype.push.apply(sourcearray, d.results);
if (d.next != null) {
getData(d.next);
} else {
console.log(sourcearray)
var config={
minLength: 1,
source: sourcearray,
focus: function(event, ui) {
$("#suggest").val(ui.item.license_author);
return false;
},
select: function(event, ui) {
$("#suggest").val(ui.item.license_author);
return false;
}
};
$("#suggest").autocomplete(config).autocomplete("instance")._renderItem = function(ul, item) {
return $("<li>").append("<div>" + item.license_author + "<br>" + item.description + "</div>").appendTo(ul);
};
}
})
}
$(function() {
getData("https://wger.de/api/v2/exercise/?format=json")
});
here is working fiddle
With a QR code vcard, the user scans the code with their phone and then the dialog with the "add to contacts" pops up on their phone, such as the code below:
How can I do the same but instead of a QR code scan, I want it to do the same with a button click.
I have tried the following:
var btn = document.getElementById(“clickMe”);
btn.addEventListener(“click”, loadvcard);
function loadvcard(){
url = "BEGIN%3AVCARD%0AVERSION%3A3.0%0AN%3ADoe%3BJohn%0AFN%3AJohn%20Doe%0ATITLE%3A08002221111%0AORG%3AStackflowover%0AEMAIL%3BTYPE%3DINTERNET%3Ajohndoe%40gmail.com%0AEND%3AVCARD";
window.open(url);
}
You can open your vcard in the browser as a data url if you want.
Your code would be:
var btn = document.getElementById(“clickMe”);
btn.addEventListener(“click”, loadvcard);
function loadvcard(){
var data = "BEGIN%3AVCARD%0AVERSION%3A3.0%0AN%3ADoe%3BJohn%0AFN%3AJohn%20Doe%0ATITLE%3A08002221111%0AORG%3AStackflowover%0AEMAIL%3BTYPE%3DINTERNET%3Ajohndoe%40gmail.com%0AEND%3AVCARD";
window.open("data:text/x-vcard;urlencoded," + data);
}
Try to use the web share api, it works.
<html>
<title>
Web Share API
</title>
<body>
<div>
<div>
<button onclick="shareVcard" id="shareFilesButton">Share Files</button>
</div>
</div>
</body>
<script>
document.getElementById('shareFilesButton').addEventListener("click", () => shareVcard())
function shareVcard() {
fetch("sample.vcf")
.then(function(response) {
return response.text()
})
.then(function(text) {
var file = new File([text], "sample.vcf", {type: 'text/vcard'});
var filesArray = [file];
var shareData = { files: filesArray };
if (navigator.canShare && navigator.canShare(shareData)) {
// Adding title afterwards as navigator.canShare just
// takes files as input
shareData.title = "vcard";
navigator.share(shareData)
.then(() => console.log('Share was successful.'))
.catch((error) => console.log('Sharing failed', error));
} else {
console.log("Your system doesn't support sharing files.");
}
});
}
</script>
</html>
I'm currently working on a Chrome extension and using the Chrome storage API (chrome.storage.sync.set) for saving my data but I'm having an issue to make it work.
The issue is that once I save an entry and wanted to save another the previous will be deleted.
Popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div id="data"></div>
<input type="text" id="text"></input>
<button id="set">Set</button>
<script src="popup.js"></script>
</body>
</html>`
popup.js
document.body.onload = function() {
chrome.storage.sync.get("data", function(items) {
if (!chrome.runtime.error) {
console.log(items);
document.getElementById("data").innerText = items.data;
}
});
}
document.getElementById("set").onclick = function() {
var d = document.getElementById("text").value;
chrome.storage.sync.set({ "data" : d }, function() {
if (chrome.runtime.error) {
console.log("Runtime error.");
}
});
window.close();
}
Is there a way to store tons of data without the previous ones deleted and is there a possibility to perform CRUD operation on a data I saved using the chrome storage API?
This is how you can append the value of a key in chrome.storage:
function storage_sync_append(val){
// In get, {'data': []} sets the key value pair if it didn't exist;
// in this case the value is an empty Array
chrome.storage.sync.get({'data': []}, result => {
var temp = result.data;
temp.push(val);
chrome.storage.sync.set({ 'data': temp }, function() {
if (chrome.runtime.error) {
console.log("Runtime error.");
}
});
});
}
In your code:
document.getElementById("set").onclick = function() {
var d = document.getElementById("text").value;
storage_sync_append(d);
window.close();
}
As an aside, if you have more than one array to store with chrome.storage, the append function can be rewritten to work with any key:
function storage_sync_append(key, data){
//
chrome.storage.sync.get({ [key] : []}, result => {
var temp
for(property in result)
if(property == key)
temp = result[property];
temp.push(data);
chrome.storage.sync.set({ [key] : temp }, function() {
if (chrome.runtime.error) {
console.log("Runtime error.");
}
});
});
}
My ASPX code generated some html files where I just put link for paging like
First |
Next |
Previous |
Last
say if user currently on second page when it press Next moves to 3rd page ...
now issue is when user clicking Next button several times and system is in progress to generate let say 5th page it will show error page.
Is there any way to check from html via javascript to check whether file is present or not?
Kindly help me to pull out from this show stopper issue
You can use ajax for check file exists or not
Using Jquery
$.ajax({
url:'http://www.example.com/3.html',
error: function()
{
alert('file does not exists');
},
success: function()
{
alert('file exists');
}
});
Using Javascript
function checkIfRemoteFileExists(fileToCheck)
{
var tmp=new Image;
tmp.src=fileToCheck;
if(tmp.complete)
alert(fileToCheck+" is available");
else
alert(fileToCheck+" is not available");
}
Now to check if file exists or not call js function like this
checkIfRemoteFileExists('http://www.yoursite.com/abc.html');
i like to use this type of script
function CheckFileExist(fileToCheck: string) {
return new Promise((resolve, reject) => {
fetch(fileToCheck).then(res => {
if (res.status == 404) resolve(false);
if (res.status == 200) resolve(true);
return res.text()
})
})
}
and use it
var exists = await CheckFileExist(link);
There is an issue with #Sibu's solution: it actually downloads the file (it can be potentionally big, wasting traffic)
In the 2021, one should not use jQuery in new projects
native Promises and Fetch are the way to go today
<output id="output"></output>
<script>
// create a non-cached HTTP HEAD request
const fileExists = file =>
fetch(file, {method: 'HEAD', cache: 'no-store'})
.then(r => r.status==200);
// check the file existence on the server
// and place the link asynchronously after the response is given
const placeNext = file => fileExists(file).then(yes => output.innerHTML =
(yes ? `Next` : '')
);
// place the "next" link in the output if "3.html" exists on the server
placeNext('3.html');
</script>