0

I am trying to update the text in Word table cells (that are within Content Controls [CCs]). I have been doing this successfully using the Table Objects; however it is too slow for customers with large documents with many tables. So, I want to be able to use getOoxml and insertOoxml to update the table cell values (hopefully much more quickly, although I see it may be very slow in Word Online). So I would use getOoxml to get the table Ooxml within the CCs, then modify the xml programmatically, then insertOoxml back into the CCs.

I am able to get the Ooxml successfully, and programmatically modify the table Ooxml successfully using C# OpenXMLPowerTools. However, I have not been able to insertOoxml back into the CCs successfully.

If the CC was added around a selected table, then when the code runs, it always fails with "InvalidArgument". Note that when it is done this way, there is no line break above or below the table in the CC (the CC fits the table). This is how most of my customer's tables are.

If the table was added inside a CC (results in unwanted line breaks above/below the table), then the code runs, but results in adding an extra CC and a line break. So, every time the code runs the CC count increases by 1. The CCs appear to be nested inside each other. The extra CCs are not acceptable because they mess up subsequent code. It also adds an additional line break each time the code is run.

I've tried contentControls.items[0].clear(). - this removes text, but not tables or CCs, so it does not help.

Here is simplified code in Script Lab:

$("#getooxml").click(getooxml);
function getooxml() {
Word.run(function (context) {
    var contentControls = context.document.contentControls;
    context.load(contentControls);
    return context.sync().then(function () {
        var ooxml = contentControls.items[0].getOoxml(); //contains a table
        return context.sync().then(function () {
            contentControls.items[0].insertOoxml(ooxml.value, Word.InsertLocation.replace);
            return context.sync().then(function () {
                console.log('inserted OOXM.  CC count:' + contentControls.items.length);
            });
        });
    });
})
    .catch(function (error) {
        console.log('Error: ' + JSON.stringify(error));
        if (error instanceof OfficeExtension.Error) {
            console.log('Debug info: ' + JSON.stringify(error.debugInfo));
        }
    });
}

Are there any potential ways to make it work? For example, Is there a way I can modify the Ooxml to make it work? I suspect the reason it adds CCs is because the getOoxml() includes the parent CC itself and when inserted inside the parent CC, it adds the CC (and any nested CCs). If so, how can I remove the parent CC from the Ooxml?

I have Version 1704 (Build 8067.2115)

There is a post here indicating the added paragraph issue will be fixed next month: `context.document.body.insertOoxml` breaks documents, crashes word It also indicates there's an error where the "replace" option doesn't actually replace (but I don't think that's the case)

Andrew Hall
  • 549
  • 6
  • 17

3 Answers3

1

The replace option with table content controls (cc wrapping only the table) might have a bug here, thanks for reporting this. will investigate and report back.

That said, I strongly recommend you to use the Table OM for this, OOXML should really be used to interact with objects who are not yet exposed in the API. The issue with OOXML while powerful is really slow, as you mentioned, in Word Online. I really doubt you will get better performance than if you use the Table OM.

It would be awesome to see the document where you see a perf degradation as well as the code you are using to change the table cells, this should be quite fast. thanks!

Juan Balmori
  • 4,898
  • 1
  • 8
  • 17
  • Perhaps more significant than the wrapping is the adding of nested CCs everytime the code is run. See my comments to Yihua regarding my simple performance test (insertOoxml was much faster). – Andrew Hall May 23 '17 at 22:17
  • If I can get insertOoxml to work and truly is substantially faster on PCs/Macs, but slower on Word Online, I can have users use the Table OM method for Word Online and insertOoxml method otherwise. – Andrew Hall May 23 '17 at 22:17
  • I will email you additional information regarding sample documents and the code (I have your email). – Andrew Hall May 23 '17 at 22:18
  • I have not found a performant way to use the Table OM yet and plan to pursue the Ooxml approach for larger tables. Would you be able to provide a JavaScript snippet to reduce the unneeded content obtained from the getOoxml as you suggest in https://dev.office.com/docs/add-ins/word/create-better-add-ins-for-word-with-office-open-xml under "Editing the Office Open XML for use in your task pane add-in". In my case, I only need to edit the table in the CC. – Andrew Hall Jul 05 '17 at 15:17
1

This is by design, because for the content control on a table, the content control's start tag is INSIDE the first cell and the end tag is INSIDE the last cell. Therefore, the range from the start tag to the end tag is not the whole table.

Ooxml's getting and setting functions are also slower when you compare them with text getting/setting functions. Therefore, I suggest you do this way:

Since you may have many tables, you can add a content control on the table you are working on, and give a tag to this content control. This content control can be on the whole table or simply just in a cell.

Then you can use the content control to grab the table directly. After that, you can manipulate the table's text.

The code could be something like this:

Word.run(function (context) {
    var contentControl = context.document.contentControls.getByTag("forMyTable").getFirst();
    var table = contentControl.tables.getFirst();
    context.load(table);
    return context.sync().then(function () {
        var values = table.values;
        // add some text to the second cell in the second row.
        table.getCell(1, 1).value = values[1][1] + " extra";
        return context.sync().then(function () {
            console.log("inserted values.");
        });
    });
}).catch(function (error) {
    console.log('Error: ' + JSON.stringify(error));
    if (error instanceof OfficeExtension.Error) {
        console.log('Debug info: ' + JSON.stringify(error.debugInfo));
    }
});
yihua
  • 76
  • 1
  • Great explanation for why I get the "InvalidArgument" error when CC is directly around the table (CC is partially in the table). But I wish there was a workaround as this is the way most of tables are in our documents. – Andrew Hall May 23 '17 at 22:04
  • I did a comparison test on a single 21row by 10 column table (Windows) changing all the cell values: using the Table OM, it takes 15.2 seconds (high CPU utilization and some distracting screen flickering); using the getOoxml/insertOoxml method, it takes a total of 0.8seconds. That includes the 0.2sec find/replace through the 152,000 characters of the Ooxml. So, at least with this 1 test, the insertOoxml is substantially faster 30x (although in reality there would be a server round-trip to modify the Ooxml using OpenXMLPowerTools - 1 trip for the whole document). – Andrew Hall May 23 '17 at 22:04
  • The Table OM code you provide above is the way my add-in currently works. Although I update the entire table with the 2D array: table.value = 2dArray. It is in the Office Store and is called "Excel-to-Word Document Automation". It includes sample documents to try. – Andrew Hall May 23 '17 at 22:05
0

InsertOoxml is probably quicker, but it is slower in Word Online.

Since you already have a table, and your scenario is to add value to every cell, I suggest you to use table.addRows method. That is:

  1. Delete all rows except the first row using table.deleteRows(1, rowCount).
  2. Add back these rows using table.addRows("end", rowCount, values), where values is a 2-D string array.

Try it and see whether it is faster than manipulating cell by cell. Thanks!

yihua
  • 76
  • 1
  • this is a good suggestion i am curious to see the perf gains with this... – Juan Balmori May 25 '17 at 00:59
  • It is the same as "myTable.values = arrNewValues" if there is no content in the table. It is 2x faster if there was content. So, it is still too slow with largish tables due to screenUpdating. See https://stackoverflow.com/questions/44910445/office-js-word-add-in-performance-issue-with-updating-values-in-large-tables. Also, in my case, this is not a good solution because it would delete any formatting applied to the table (merged cells, borders, bolding, etc.). I need a solution that only replaces the table cell values/text. – Andrew Hall Jul 05 '17 at 14:39