Office-js Word addin, Grabbing document Text - ms-word

In my code below, I am attempting to grab the current documents text, set it to a variable and then use that variable in a call. My promise may not be formatted correctly but essentially the getScanResult() function uses the docBodyText variable that I set in handleClickRun(). Everytime I call it the variable is empty. Any idea as to why the document text is not being captured correctly?
const [docBodyText, setDocBodyText] = useState('');
const handleClickRun = async () => {
return Word.run(async (context: Word.RequestContext) => {
const docBody = context.document.body;
docBody.load("text");
await context.sync();
setDocBodyText(docBody.text);
await context.sync();
})
.catch(function (error) {
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
};
const handleScanResults = () => {
new Promise(async (resolve) => {
await handleClickRun();
await getScanResult();
resolve('Completed')
})};
I have tried using the docs and looking for other examples but have not seen any other use cases. The docs I am using is this Perhaps I can be pointed to the correct method.
I have also tried making a variable of just plain text and passing it to my api call and it works perfectly fine, so it is not a call issue.

Related

Repeatedly getting error in Word office add-in "RichApi.Error: Wait until the previous call completes"

After applying Content control on long document (around 10 pages) using office Js, when I am trying to select the content control form a document using below code snippet, I am getting an error
OfficeService.js:338 RichApi.Error: Wait until the previous call completes.
at new n (word-win32-16.01.js:25:246227)
at o.processRequestExecutorResponseMessage (word-win32-16.01.js:25:310053)
at word-win32-16.01.js:25:308456
Sometimes I am able to fetch the selected content control but it's very slow and many times I get the above error. Not sure why this issue is occurring, I have checked in Office Js documentation but couldn't find the resolution.
export const findSelectedContentControl = async () => {
return await Word.run(async (context) => {
try {
const selectedContentControl = context.document.getSelection().contentControls;
selectedContentControl.load("items");
console.log("selectedContentControl..",selectedContentControl)
return await context.sync().then(() => {
let tagArray;
if(selectedContentControl.items.length === 0)
return;
tagArray = selectedContentControl.items[0].tag.split("|");
return tagArray;
});
} catch (error) {
console.log(error);
}
});
};

How to loop through a RangeCollection in Word JS API?

I want to add a Find function in my Word Add-in. By clicking a button, the Word will scroll to the first place of the word need to be searched, and by clicking again, the Word will scroll to the next place. I figured out how to find the first one, but I can't loop through the RangeCollection of the search results of the word. I am looking for some functions similar to getNext()
Below is the code to find first "and". It works:
async function scroll() {
await Word.run(async (context) => {
context.document.body.search("and", { matchWholeWord: true, matchCase: false }).getFirst().select();
await context.sync()
}
)}
However, if I want to find the second or more, I can't do this:
async function scroll() {
await Word.run(async (context) => {
context.document.body.search("and", { matchWholeWord: true, matchCase: false }).getFirst().getNext().select();
await context.sync()
} )}
I read the document and there is no getNext() for RangeCollection. Hope somebody knows how to work around it. Thank you!
There are a few workarounds.
Solution 1
Load RangeCollection.items and record the index. Example:
var index = 0;
async function selectNext(...) {
...
var ranges = context.document.body.search(...);
ranges.load('items');
await context.sync();
if (index >= ranges.items.length) {
index = 0;
}
ranges.items[index++].select();
await context.sync();
...
}
This solution respects the index, but not the current selection position. So if you prefer to search downward instead of searching the next, here's solution 2.
Solution 2
Load RangeCollection.items and call Range.compareLocationWith. Example:
var ranges = context.document.body.search(...);
ranges.load('items');
await context.sync();
var selection = context.document.getSelection();
var compareResults = ranges.items.map(range => range.compareLocationWith(selection));
await context.sync();
for(var index in compareResults) {
if (compareResults[index].value == 'After') {
ranges.items[index].select();
await context.sync();
break;
}
}
Of course you would need additional code and polish it to make it a real product, but these are the rough ideas. Please see if they work for you.

Displaying a profile picture that is being fetched from Github

I am a newbie and am attempting to display an image that is from my Github profile vs hardcoding it into the HTML. Currently, I have coded the following and the object is being displayed in my Javascript console.
const APIURL = 'https://api.github.com/users/'
getUser('JetimLee')
async function getUser(username) {
try {
const {
data
} = await axios(APIURL + username)
console.log(data)
} catch (err) {
console.log(err)
}
}
My question is - how do I get the image from the profile to be displayed? Thank you so much in advance!
You have an avatar_url field, set it's value as source of an image tag in your html.
Example:
const data = await axios('link');
const imgTag = document.getElementById('git-user-id');
imgTag.setAttribute('src', data.avatar_url);

Component rendered in `describe` is not visible in `it` using react-testing-library

When I render a component in it block everything works as expected.
it("when user inputs more characters it displays suggestions", async () => {
const { getByLabelText, findByText } = render(<Component />);
debug();
});
It's not a good practice though. I prefer using it only for assertions. When I do:
describe("when user inputs more characters ", async () => {
const { getByLabelText, findByText } = render(<Component />);
it("displays suggestions", () => {
debug();
});
});
debug shows only <body /> and all queries fails in it block.
What am I doing wrong or why this is not supported?
They both work, the problem is that you must put your render inside the it block. That's because you can't reuse a rendered component

priority-web-sdk: startSubForm return the parent form

The following code starts a form and a subform, the issue is the I get the subForm instance of the parent:
await actions.loginTest();
const form = await actions.priority.formStart(this.formName,
onShowMessgeFunc, onUpdateFieldsFunc);
console.log("form", form);
if(form.subForms["SHIPTO2"]) {
const subForm = await form.startSubForm("SHIPTO2");
console.log("subform", subForm);
subForm.choose("CUSTDES", '').then(options => {
let custOptions = options.SearchLine.map(x => {return
{label:x.retval + " - " + x.string1, value: x.retval }});
this.setState({
customersShippingOptions: custOptions,
})
}).catch((err) => {
console.log("CHOOSE ERROR", err);
})
}
Output:
In order to start a subform there must be an active row in the parent form, so the subform will start for that row.
Before you start the subform you should use the getRows() function to retrieve rows and then call setActiveRow(rowIndex) to set an active row. Then you could start the subform.
Modify your code to the following:
await actions.loginTest();
const form = await actions.priority.formStart(this.formName,
onShowMessgeFunc, onUpdateFieldsFunc);
console.log("form", form);
const rows = await form.getRows();
await setActiveRow(5);
if(form.subForms["SHIPTO2"]) {
const subForm = await form.startSubForm("SHIPTO2");
console.log("subform", subForm);
...
}
By the way: I see you're using choose(), Here too there needs to be an active (subform) row,use getRows() with setActiveRow() or create a newRow() before calling choose()
If you want to pull the first line on the screen more than just use the autoRetrieveFirstRows parameter
In the function formStart
Link to the function on the Web SDK site
I personally define this value as default