Range across different Word.run contexts using OfficeExtension.TrackedObjects class - javascript

I am trying to use the OfficeExtension.TrackedObjects class to access a range across different contexts (documentation and similar questions set out below - although slightly outdated). The goal is to have a taskpane search list the results in the taskpane, then select the specific result in-text when clicking the listed result (using javascript).
Here is what I have:
var items = [];
function basicSearch() {
Word.run(function (context) {
const results = context.document.body.search("Online");
results.load("length, text, items");
return context.sync().then(function () {
context.trackedObjects.add(results);
for (let i = 0; i < results.items.length; i++) {
let x = results.items[i].text;
createtable("TestList", i, x, x);
items.push(results.items[i]);
}
});
return context.sync();
});
}
function createtable(id, x, y, z) {
var table = document.getElementById(id);
var row = table.insertRow(-1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
cell1.type = "button";
cell1.onclick = function () { selectrange(x) };
cell2.type = "button";
cell2.onclick = function () { selectrange(x) };
cell3.type = "button";
cell3.onclick = function () { selectrange(x) };
cell1.innerHTML = x;
cell2.innerHTML = y;
cell3.innerHTML = z;
}
function selectrange(x) {
results.load("text");
results.items[x].select();
results.context.sync();
}
Could someone show me where I have gone wrong, or provide a full working example of how to track and call an object (or collection of objects) for use?
Resources:
https://learn.microsoft.com/en-us/javascript/api/office/officeextension.trackedobjects?view=common-js-preview&viewFallbackFrom=office-js
How can a range be used across different Word.run contexts?
Word Online Add-In: Using objects across multiple contexts
Tracked Objects throwing errors in Word Online
https://leanpub.com/buildingofficeaddins
(Building Office Add-ins using Office.js has a working example, but it is in typescript and does not use trackedObjects - I have not been able to replicate it in my add-in).
When I run the above code, it says "ReferenceError: Can't find variable: results". I want it to select the specific search results displayed and pressed in the list. Any assistance would be greatly appreciated.

first, you need to define results as a global variable so that you can pass it to other functions. please reference the following code:
var results;
async function basicSearch() {
await Word.run(async (context) => {
results = context.document.body.search("Online");
results.track();
await context.sync();
});
}
async function selectrange() {
await Word.run(results, async (context) => {
results.load();
await context.sync();
results.items[0].select();
await context.sync();
});
}

Related

a way to avoid using empty array when tracking repetetive data

I have a function that creates a table with cities and weather conditions in those cities. It checks cityArr and if the city that came from request is not there, it creates a new table row and adds data to it. Otherwise it finds the row with the city and replaces it with fresh data.
I am looking for a way to keep the same functionality but somehow avoid using an array and check for existing cities some other way.
const cityArr = []; // don't like using array
export function makeTable() {
const storageCity = localStorage.getItem('city');
if (cityArr.includes(storageCity)) {
tbody.deleteRow(cityArr.indexOf(storageCity));
makeRowsandCells(cityArr.indexOf(storageCity), " UPDATE");
} else {
makeRowsandCells();
cityArr.push(storageCity);
}
if (tbody.children[0]) {
table.classList.add('table-visible');
}
};
function makeRowsandCells(index = -1, update = '') {
const row = tbody.insertRow(index);
const cityCell = row.insertCell();
cityCell.innerHTML = localStorage.getItem('city') + update;
const cityCell1 = row.insertCell();
cityCell1.innerHTML = localStorage.getItem('country');
const cityCell2 = row.insertCell();
cityCell2.innerHTML = localStorage.getItem('temp');
const cityCell3 = row.insertCell();
cityCell3.innerHTML = localStorage.getItem('feelslike');
};

Google Apps Scripts: Class notation and new Objects

I am trying to create a class to import Google Sheets tables into a more maneuverable. The code:
class SheetData{
constructor(worksheet, spreadsheet=null) {
this.spreadsheet = spreadsheet ? spreadsheet : SpreadsheetApp.getActive();
this.sheet = this.spreadsheet.getSheetByName(worksheet);
this.values = this.sheet.getDataRange().getValues();
}
get records() {
let cols = this.values[0], temp = {}, out = [];
for (let row of this.values.slice(1)){
cols.forEach( (colName,idx) => temp[colName] = row[idx] );
out.push(temp);
temp = {};
}
}
How ever when I try to run it on a sheet Logger.log(new SheetData('Sheet1').values), I get an Unexpected identifier at the new. What am I doing wrong? I also am not getting any syntax highlighting in the editor, even though I have the V8 runtime enabled.
How about this modification?
Modification points:
If you are testing your script in your question, I think that in your script, } is required to be added at the last line (
} is one shortage.).
I think that this might be the reason of your issue.
And, new SheetData('Sheet1').records is run, undefined is returned. Because no value is returned.
When above points are reflected to your script, it becomes as follows.
Modified script:
class SheetData{
constructor(worksheet, spreadsheet=null) {
this.spreadsheet = spreadsheet ? spreadsheet : SpreadsheetApp.getActive();
this.sheet = this.spreadsheet.getSheetByName(worksheet);
this.values = this.sheet.getDataRange().getValues();
}
get records() {
let cols = this.values[0], temp = {}, out = [];
for (let row of this.values.slice(1)){
cols.forEach( (colName,idx) => temp[colName] = row[idx] );
out.push(temp);
temp = {};
}
return out; // <--- Added
}
} // <--- Added
// Please run this function.
function main() {
var s = new SheetData('Sheet1');
console.log(s.values); // or Logger.log(s.values);
console.log(s.records); // or Logger.log(s.records);
}
Note:
Please confirm whether V8 runtime is enabled again.

Routing function not being called

I am working on a simple restaurant web app which uses a mongo-db database to store the menu items. My issue is that I have a client js file that will use a routing function that then accesses the database to return all the menu items of a certain restaurant. My issue is that my endpoint for the url isn't being recognized:
Client.js
function readMenu(rest){
(async () => {
// const newURL = url + "/menus/"+rest
const resp = await fetch(url+"/menus/"+rest)
const j = await resp.json();
itemlist = j["items"]
var element = document.getElementById("menu")
var i;
for (i = 0; i < itemlist.length; i++) {
var para = document.createElement("p")
item = itemList[i]
text = item["name"]+" | "+item["cost"]+" | "+item["descr"] +"<br>";
var node = document.createTextNode(text)
para.appendChild(node)
element.appendChild(para)
}
})
}
Server-routing.ts (Routings):
this.router.get("/menus", this.getResturants.bind(this))
this.router.post("/menus", this.addResturaunt.bind(this))
this.router.get("/menus/:rest", this.getResturauntItems.bind(this))
this.router.delete("/menus/:rest",this.deleteResturaunt.bind(this))
this.router.get("/menus/:rest/:item",[this.errorHandler.bind(this),this.getItem.bind(this)])
this.router.post("/menus/:rest",this.addItem.bind(this))
this.router.delete("/menus/:rest/:item",this.deleteItem.bind(this))
Server-routing.ts (function):
public async getResturauntItems(request, response) : Promise<void> {
console.log("Getting Restaurant Items")
let rest = request.params.rest
let obj = await this.theDatabase.getResturauntItems(rest)
console.log(obj)
response.status(201).send(JSON.stringify(obj))
response.end()
}
So, what should happen is a button calls readMenu(), it then makes a GET fetch request to localhost:8080/api/menus/ and then the menu items from the collection should be returned. The issue is that when I click the button, nothing happens. I know it is not being redirected to some other function as they all have "console.log()" to keep track of them and none of them where called. I used the "inspect" tool to see if the request was being sent or received anywhere and nothing. I am unsure of what the issue happens to be. If anyone can help, it would be really appreciated.
you just never called your function, you declared the async function inside your function but never called it.
function readMenu(rest){
(async () => {
// const newURL = url + "/menus/"+rest
const resp = await fetch(url+"/menus/"+rest)
const j = await resp.json();
itemlist = j["items"]
var element = document.getElementById("menu")
var i;
for (i = 0; i < itemlist.length; i++) {
var para = document.createElement("p")
item = itemList[i]
text = item["name"]+" | "+item["cost"]+" | "+item["descr"] +"<br>";
var node = document.createTextNode(text)
para.appendChild(node)
element.appendChild(para)
}
})();
}
you need to add () after creating the functions to call it.

JavaScript: Catch errors in async function and stop the rest of the function if there are any

EDIT: ANSWER BELOW
I'm making my first JavaScript project and decided to make a simple weather app. It fetches weather data of a city you put in from the openweathermap.org api and displays it in a table. I firstly made it using fetch() and .then. I then learned about async functions and the await keyword. After converting the script to an asynchronous function, I came across a problem. If the first city you enter isn't a real city (an error is catched while fetching the api), the warning message appears, BUT the table also appears because the rest of the function still executes.
So my question is: how can I stop the async function if any errors are catched?
Here's the website: https://lorenzo3117.github.io/weather-app/
Here's the code:
// Launch weather() function and catch any errors with the api request and display the warning message if there are any errors
function main() {
weather().catch(error => {
document.querySelector("#warningMessage").style.display = "block";
console.log(error);
});
}
// Main function
async function weather() {
// Take city from input and reset input field
var city = document.querySelector("#cityInput").value;
document.querySelector("#cityInput").value = "";
// Get api response and make it into a Json
const apiResponse = await fetch("https://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=<apiKey>&units=metric");
const jsonData = await apiResponse.json();
// Removes warning message
document.querySelector("#warningMessage").style.display = "none";
// Puts the Json into an array and launches createTable function
var arrayJson = [jsonData];
createTable(document.querySelector("#table"), arrayJson);
// Function to create the table
function createTable(table, data) {
// Makes the table visible
document.querySelector("#table").style.display = "block";
// Goes through the array and makes the rows for the table
for (let i = 0; i < data.length; i++) {
let rowData = data[i];
var row = table.insertRow(table.rows.length);
// This var exists to make the first letter capitalized without making a gigantic line (see insertCell(3), line 53)
// Could be made into a function if needed
var weatherDescription = rowData.weather[0].description;
// Take latitude and longitude for google maps link
var lat = rowData.coord.lat;
var long = rowData.coord.lon;
// Make an a-tag for link to google maps
var mapLink = document.createElement("a");
mapLink.innerHTML = "Link";
mapLink.target = "_blank";
mapLink.href = "https://www.google.com/maps/search/?api=1&query=" + lat + "," + long;
// Making rows in table
row.insertCell(0).innerHTML = rowData.name + ", " + rowData.sys.country;
row.insertCell(1).innerHTML = rowData.main.temp + " °C";
row.insertCell(2).innerHTML = rowData.main.humidity + "%";
row.insertCell(3).innerHTML = weatherDescription.charAt(0).toUpperCase() + weatherDescription.slice(1);
row.insertCell(4).appendChild(mapLink); // appendChild for anchor tag because innerHTML only works with text
}
}
And the repo: https://github.com/lorenzo3117/weather-app
Thank you
you can do this :
async function weather() {
try {
const apiResponse = await fetch("https://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=02587cc48685af80ea225c1601e4f792&units=metric");
} catch(err) {
alert(err); // TypeError: failed to fetch
return;
}
}
weather();
Actually, the error catched isn't an error with the api itself because the api still sends a json, but the error is catched while trying to read a certain object from the json (which doesn't exist because the json isn't a normal one with weather data). Therefore the function stops far later than expected, after the table was made visible.
I just put the line that made the table visible after the function that creates the table (after where the real error occurs). Also thanks #Dadboz for the try catch method which made the code even more compact. I also added an if else to check if the json file is the correct one so unnecessary code doesn't get executed. Thanks #James for pointing this out to me.
Here's the final code:
// Main function
async function weather() {
try {
// Take city from input and reset input field
var city = document.querySelector("#cityInput").value;
document.querySelector("#cityInput").value = "";
// Get api response and make it into a Json
const apiResponse = await fetch("https://api.openweathermap.org/data/2.5/weather?q=" + city + "&appid=<apiKey>&units=metric");
const jsonData = await apiResponse.json();
if (jsonData.message == "city not found") {
document.querySelector("#warningMessage").style.display = "block";
} else {
// Removes warning message
document.querySelector("#warningMessage").style.display = "none";
// Puts the Json into an array and launches updateTable function
var arrayJson = [jsonData];
updateTable(document.querySelector("#table"), arrayJson);
}
}
catch (error) {
console.log(error);
}
}
// Function to update the table
function updateTable(table, data) {
// Goes through the array and makes the rows for the table
for (let i = 0; i < data.length; i++) {
let rowData = data[i];
var row = table.insertRow(table.rows.length);
// This var exists to make the first letter capitalized without making a gigantic line (see insertCell(3), line 53)
// Could be made into a function if needed
var weatherDescription = rowData.weather[0].description;
// Take latitude and longitude for google maps link
var lat = rowData.coord.lat;
var long = rowData.coord.lon;
// Make an a-tag for link to google maps
var mapLink = document.createElement("a");
mapLink.innerHTML = "Link";
mapLink.target = "_blank";
mapLink.href = "https://www.google.com/maps/search/?api=1&query=" + lat + "," + long;
// Making rows in table
row.insertCell(0).innerHTML = rowData.name + ", " + rowData.sys.country;
row.insertCell(1).innerHTML = rowData.main.temp + " °C";
row.insertCell(2).innerHTML = rowData.main.humidity + "%";
row.insertCell(3).innerHTML = weatherDescription.charAt(0).toUpperCase() + weatherDescription.slice(1);
row.insertCell(4).appendChild(mapLink); // appendChild for anchor tag because innerHTML only works with text
}
// Makes the table visible
document.querySelector("#table").style.display = "block";
}
Thanks everyone for your answers, have a good day!
Lorenzo

Excel Javascript API fetch position of multiple sheets

How can I get the position of two worksheets using the Excel Javascript API?
Here is how it works just for one sheet:
Excel.run(function (ctx) {
var wSheetName = 'Sheet1';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
worksheet.load('position')
return ctx.sync().then(function () {
console.log(worksheet.position);
});
});
=> it logs 0 to the console
But it doesn't logs anything if I try to get the position for two worksheets:
Excel.run(function (ctx) {
var wSheetName = 'Sheet1';
var wSheetName2 = 'Evars';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
var worksheet2 = ctx.workbook.worksheets.getItem(wSheetName2);
worksheet.load('position')
worksheet2.load('position')
return ctx.sync().then(function () {
console.log(worksheet.position);
console.log(worksheet2.position);
});
});
I just tried your code, and it works fine. I wonder if you simply didn't have a sheet by one of those names, and so it was throwing an exception -- which was appearing to you as silent, since you didn't have a catch handler.
The code below, essentially the same as yours but with a catch statement, works correctly:
Excel.run(function(ctx) {
var wSheetName = 'Sheet1';
var wSheetName2 = 'Sheet2';
var worksheet = ctx.workbook.worksheets.getItem(wSheetName);
var worksheet2 = ctx.workbook.worksheets.getItem(wSheetName2);
worksheet.load('name, position')
worksheet2.load('name, position')
return ctx.sync().then(function () {
console.log(worksheet.name + ": " + worksheet.position);
console.log(worksheet2.name + ": " + worksheet2.position);
});
}).catch(function(error) {
OfficeHelpers.UI.notify(error);
OfficeHelpers.Utilities.log(error);
})
You can try this snippet live in literally five clicks in the new Script Lab (https://aka.ms/getscriptlab). Simply install the Script Lab add-in (free), then choose "Import" in the navigation menu, and use the following GIST URL: https://gist.github.com/Zlatkovsky/c61594f1c86970e8dba91fe94b7ca4b6. See more info about importing snippets to Script Lab.
Found the solution here ... maybe this will help someone
Excel.run(function (ctx) {
var worksheets = ctx.workbook.worksheets;
worksheets.load('items');
return ctx.sync().then(function () {
for (var i = 0; i < worksheets.items.length; i++) {
var sheet_name = worksheets.items[i].name;
var sheet_position = worksheets.items[i].position;
}
});

Categories