Search code examples
javascriptoffice365office-jsword-addins

Office Word JS - Content Control from Table Selection


I am working on a Word add-in, using the Office JS API, trying to add a Content Control around a Table Binding in the document.

The issue I am experiencing is after the Binding around the table is selected, using goToByIdAsync(), the Content Control is created around the last row of the table only, not the selection. Returning the value of ctx.document.getSelection() I can see the selected Range is only the last row in the Selected Table. I need to use the Binding object to know the selected cell ranges of Table. Is there something I am doing wrong?

var bindingId  = '123';
var doc        = Office.context.document;

    // Create table in document.
    doc.setSelectedDataAsync(tableValue, { coercionType: Office.CoercionType.Table }, function (asyncResult) {

            // Add Binding to table
            doc.bindings.addFromSelectionAsync(Office.BindingType.Table, { id: bindingId }, function (asyncResult) {

                    // Select the table object                        
                    doc.goToByIdAsync(bindingId, Office.GoToType.Binding, { selectionMode: 'selected' }, function (asyncResult) {

                         // Create Content Control
                         createCC();
                    });

            });
    });


    function createCC(){
         Word.run(function (ctx) {
             var range = ctx.document.getSelection();
             var html  = range.getHtml();

                 return ctx.sync().then(function () {
                     console.log('Selected Range', html.value); // Only displays last row in the table.

                     var myContentControl = range.insertContentControl();
                         myContentControl.tag = bindingId;
                         myContentControl.title = 'My Content Control';

                         return ctx.sync().then(function () {
                                    //Content control for table created
                                });
                           }).catch(function (err) {
                               errorHandler(err);
                           });
                    });
    }

Solution

  • This is a great question! thanks for asking. I will suggest you to change a bit the approach on how to achieve the scenario you want. First of all goToById should not be used to get the range of any object, this is just for navigation purposes. It will be way more deterministic if you can insert a table, wrap it with a named content control (assigning a title to it), create a binding (using addFromNamedItem and not using addFromSelection as you don't have a solid selection :) ) , and then just subscribe to the bindingSelectionChanged event and do whatever you need to do with the table or selected cells.

    The following code shows just that. Hopefully it will set you up in the right direction. I added a bunch of comments of each of the steps I described above.

    thanks and happy coding!

    function insertTableCreateABindingAndSubscribeToEvents() {
        Word.run(function (context) { 
            //ok first we insert a table... 2 rows, 3 columns 
            var myTableData = [["Banana", "Mango", "Cherry"],["10","20","30"]];
            var myTable = context.document.body.insertTable(2, 3, "end", myTableData); //insert at the end of the body of the document.
            context.load(myTable);
            return context.sync()
                .then(function () { 
                //then we wrap the isnerted table with a content control
            var myCC = myTable.insertContentControl();
            myCC.title = "myTableTitle"; //important: assing a title so then i can use it to bind by namedItem!
            return context.sync()
            .then(function () { 
                    //Now we create the binding and subscribe to the events...
                    //since  we know the content control title, we can use addFromNamedItem!
        
                Office.context.document.bindings.addFromNamedItemAsync("myTableTitle", "table", {}, function (result) {
                 //boom now lets subscribe to the event...
                    if (result.status == "succeeded") {
                        //now lets subscribe to the selectionChanged event.\
                        result.value.addHandlerAsync(Office.EventType.BindingSelectionChanged, handler);
                    }
                    else { 
                        console.log("error");
                    }
    
                 } )
            })
    
                })
    
        })
            .catch(function (e) { 
                console.log(e.message);
            })
    
    
     }
    
    function handler(args) {
    //check out all the values you can get, see below how we use it to display the selected cell value...
      //  console.log("selection changed!" + args.startRow + " " + args.startColumn + " " + args.rowCount + " " + args.columnCount);
        var row;
        if (args.startRow == undefined) {
            //menas the selection is in the header!
            row = 0;
        }
        else {
             //selection not in the header...
            row = args.startRow + 1
        }
    
    // the other thing you can try here is to get the table, and print the selected cell value..
        Word.run(function (context) {
            //this instruction selected  cell of the  table within the content control named "myTableTite"
            var mySelectedCellBody = context.document.contentControls.getByTitle("myTableTitle").getFirst().tables.getFirst().getCell(row,args.startColumn).body;
            context.load(mySelectedCellBody);
            return context.sync()
                .then(function () {
                    //lets write the value of the cell (assumes single cell selected.)
                    console.log(mySelectedCellBody.text);
    
                 })
        })
            .catch(function (e) { 
                console.log("handler:" + e.message);
            })
    
    
    
     }