Switching context to new document in Office.js Word Add-in - ms-word

I have a function that needs to copy the data from an existing document into a new document based on a new template. The problem is that when I want to load the document of the newly created object and insert the data, it instead inserts it into the existing document. It looks like it will not switch context to the new document to work with but I can't find why. Everything works fine without errors, just the data ends up in the wrong document.
This is the code that I'm using:
public convertdoc() {
this.getText('htmlsource', 'template.txt').subscribe((template) => {
Word.run(async (context) => {
const data = context.document.body;
const xml = data.getOoxml();
await context.sync();
const val = xml.value;
const newDoc = context.application.createDocument(template);
context.load(newDoc);
await context.sync();
newDoc.open();
await newDoc.context.sync();
const body = newDoc.context.document.body;
await newDoc.context.sync();
body.insertOoxml(val, 'End');
}).catch((err) => { this.log('Error: ' + err.message); });
});
}
Am I overlooking something or does the newDoc.context not work this way? How can I get to the body of the new document (with the new context)?
Btw, the getText function is a simple ajax function that returns a base64 encoded .dotx file. And the log function just adds the text to an element's innerHtml on the Taskpane.

I ended up writing the ooXML from the current document to localstorage, then opening the new document from the template which has the taskpane set to autoopen. The taskpane then checks if there's something in the localstorage and pastes that into the new document. Works like a charm :)

This is not supported, and it's by design behavior. You can only manipulate the content of the current document where the web add-in is inserted. It cannot do cross document operation.

Related

Is this the right approach to read a DOCX file from a database and open it into the current document?

I'm developing a Word Add-in (Taskpane) which the main goal is replacing some predefined content controls of documents persisted in a database. This add-in is a web app using Office.JS. Based on some user input the Add-in should get the file and open it.
My first approach based in the Office documentation was the following:
fetch(`/arquivo?id=${idModeloDocumento}`)
.then(response => response.text())
.then(base64String => {
Word.run(async context => {
const app = context.application;
const doc = app.createDocument(base64String); // base64string comes from my backend app
doc.open();
await context.sync();
});
})
However, it opens the document on a new instance of Word App, different from the one where the add-in is already running. So I created the document and inserted each part of the document (just body and header at this context) into the current document like this:
fetch(`/arquivo?id=${idModeloDocumento}`)
.then(response => response.text())
.then(base64String => {
Word.run(async context => {
var base64doc = base64String
const app = context.application
const doc = app.createDocument(base64doc)
await context.sync()
console.log("Novo documento criado")
let bodyOoxml = doc.body.getOoxml()
let headerOoxml = doc.sections.getFirst().getHeader(Word.HeaderFooterType.primary).getOoxml()
await context.sync()
console.log("Preenchendo o body do documento corrente")
let curHeader = context.document.sections.getFirst().getHeader(Word.HeaderFooterType.primary)
curHeader.insertOoxml(headerOoxml.value, Word.InsertLocation.replace)
let curBody = context.document.body
curBody.insertOoxml(bodyOoxml.value, Word.InsertLocation.replace)
await context.sync()
console.log("Pronto")
});
})
It works, however, I'm not 100% sure if it is the right way to do that.
What's the recommended approach to open a Docx file in the current document using OfficeJS?

What is the equivalent method of Word.Body.getOoxml() in c# open xml sdk?

I need to read the contents of the header in a word document using c# and save it somewhere and later insert it to another word document using javascript wordapi.
I know that in "officejs wordapi" we can use insertOoxml method to insert into header for example: getHeader("Primary").insertOoxml(ooxmlStringFetchedFromServer, Word.InsertLocation.replace);
But how can i get this ooxml using c#?
Also i already tried OuterXml property of Header object but apparently its not an ooxml.
Update:
I figure out with this code we can get the right format to be used in javascript wordapi:
using (WordprocessingDocument word = WordprocessingDocument.Open(#"C:\sample.docx", false))
{
var ooxmlStringFetchedFromServer = word.MainDocumentPart.HeaderParts.FirstOrDefault().OpenXmlPackage.ToFlatOpcString();
}
So the correct format is "FlatOpc".
Now i can use the following javascript code to add this FlatOpc to the Word document:
async function run() {
await Word.run(async (context) => {
context.document.sections.getFirst().getHeader("Primary").insertOoxml(ooxmlStringFetchedFromServer, Word.InsertLocation.replace);
await context.sync();
});
}
But now i have another problem, which is the following code does not returning the FlatOpc of the Header section, instead it returns only the FlatOpc of the body section!
word.MainDocumentPart.HeaderParts.FirstOrDefault().OpenXmlPackage.ToFlatOpcString();

insert doc file into the current oppened document at cursor point (using insertFileFromBase64 )

Here I am trying to insert a doc file at cursor point from my Office Add-in to Word , but I could not find the proper solution.
The API has only three options :
bodyObject.insertFileFromBase64(base64File, insertLocation);
where insertLocation can be Start, End or Replace.
The options for Word.InsertLocation are:
Start: Prepend the inserted content before the existing content.
End: Append the inserted content after the existing contents.
Replace: Replace the existing content with the inserted content.
When you using bodyObject.insertFileFromBase64, you're scoping your call to the entire Body of the document. Calling this method will therefore not care about your cursor location.
I suspect that you really want here is rangeObject.insertFileFromBase64. This is scoped to a Range rather than the entire Body. You can fetch a range from the current selection (or if nothing is selected, your cursor location):
Word.run(function (context) {
// Queue a command to get the current selection and then
// create a proxy range object with the results.
var range = context.document.getSelection();
// Queue a commmand to insert base64 encoded .docx at the beginning of the range.
// You'll need to implement getBase64() to make this work.
range.insertFileFromBase64(getBase64(), Word.InsertLocation.start);
// Synchronize the document state by executing the queued commands,
// and return a promise to indicate task completion.
return context.sync().then(function () {
console.log('Added base64 encoded text to the beginning of the range.');
});
})
.catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});

Insert headers and footers office.js api

I am trying to insert header and footers into a document using the office js API in a task pane app. I cant seem to find a way to add headers and footers to the docs.
Headers and Footers inherit the Body class and can be accessed by initializing a Section. Here is sample from the Office-JS Snippet Explorer :
// Run a batch operation against the Word object model.
Word.run(function (context) {
// Create a proxy sectionsCollection object.
var mySections = context.document.sections;
// Queue a commmand to load the sections.
context.load(mySections, 'body/style');
// Synchronize the document state by executing the queued-up commands,
// and return a promise to indicate task completion.
return context.sync().then(function () {
// Create a proxy object the primary header of the first section.
// Note that the header is a body object.
var myHeader = mySections.items[0].getHeader("primary");
// Queue a command to insert text at the end of the header.
myHeader.insertText("This is a header.", Word.InsertLocation.end);
// Queue a command to wrap the header in a content control.
myHeader.insertContentControl();
// Synchronize the document state by executing the queued-up commands,
// and return a promise to indicate task completion.
return context.sync().then(function () {
console.log("Added a header to the first section.");
});
});
})
.catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
A question: how can you enable the word document to use the different header types? (First and Primary)? It seems I can set the contents of the First and Primary headers and footers, but, without manually changing the document it does not seem to use them.
I am not sure how to reword this (seems clear to me) :(, trying.
A word document uses the same header type for each page unless the document is changed to use different header types. ie, how do I replicate the attached image controls?
screen shot

Upload Data to Meteor / Mongo DB

I have a Meteor app and would like to upload data (from csv) to a meteor collection.
I have found:
solutions (e.g. Collectionfs) which deal with file uploads
methods for uploading directly to the underlying mongo db from the shell
references to meteor router - but I am using the excellent iron-router, which does not appear to provide this functionality
My requirement is that the app user be able to upload csv data to the app from within the app. I do not need to store the csv file anywhere within the app file structure, I just need to read the csv data to the collection.
It is possible that I cannot figure out how to do this because my terms of reference ('upload data to meteor') are ambiguous or incorrect. Or that I am an idiot.
ChristianF's answer is spot on and I have accepted it as the correct answer. However, it provides even more than I need at this stage, so I am including here the code I have actually used - which is largely taken from Christian's answer and other elements I have found as a result:
HTML UPLOAD BUTTON (I am not including drag and drop at this stage)
<template name="upload">
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>
</template>
JAVASCRIPT
Template.upload.events({
"change #files": function (e) {
var files = e.target.files || e.dataTransfer.files;
for (var i = 0, file; file = files[i]; i++) {
if (file.type.indexOf("text") == 0) {
var reader = new FileReader();
reader.onloadend = function (e) {
var text = e.target.result;
console.log(text)
var all = $.csv.toObjects(text);
console.log(all)
_.each(all, function (entry) {
Members.insert(entry);
});
}
reader.readAsText(file);
}
}
}
})
NB there is a jquery-csv library for Meteor here: https://github.com/donskifarrell/meteor-jquery-csv
I've solved this problem in the past using this gist of mine, together with this code (using the jquery-csv plugin to parse the csv data). This is done on the client side and is independent of using iron-router or not. It would be fairly straightforward to move the insertion code into a Meteor method, uploading the csv file first and then parsing and inserting the data on the server. I've tried that, too, but didn't see any performance improvement.
$(document).ready(function() {
var dd = new dragAndDrop({
onComplete: function(files) {
for (var i = 0; i < files.length; i++) {
// Only process csv files.
if (!f.type.match('text/csv')) {
continue;
}
var reader = new FileReader();
reader.onloadend = function(event) {
var all = $.csv.toObjects(event.target.result);
// do something with file content
_.each(all, function(entry) {
Items.insert(entry);
});
}
}
}
});
dd.add('upload-div'); // add to an existing div, turning it into a drop container
});
Beware though that if you are inserting a lot of entries, then you are better off turning all reactive rerendering off for a while, until all of them are inserted. Otherwise, both node on the server and the browser tab will get really slow. See my suggested solution here: Meteor's subscription and sync are slow