Office Add-Ins: Working with Tables in Word. Part 1: Creation

In this article I will show how the Word JavaScript API can be utilized to add tables to your word document using an Office Add-In. This will be a multi part blog post as there are a lot of nuances and interesting ways in which you can play with tables in Word.

Introduction

In previous articles I have written about how to interact with a Word document to search and replace and even save the word document to Salesforce. Going back to a more basic level we are going to look at how to build tables in word.

We will be using the new Script Lab  which is a new Playground Add-In which can be used for development and general tinkering with Add-Ins. This and other articles on the topic are for demonstration purposes and are not hardened for production use.

The reference

For more information on Tables and how what methods/properties are available check out the documentation page – Table Object (JavaScript API for Word)

Creating a table 

At the most basic level a table is created by instantiating the table object and adding values to it.

insertTable(rowCount: number, columnCount: number, insertLocation: string, values: string[][])

This can be done from a number of different parents:

  • The body
  • A range
  • A contentControl
  • A paragraph

The method requires the following:

  • rowCount – a number
  • columnCount – a number
  • insertLocation – Depends on parent – either (body: Start/End/Replace) or (range: Before/After)
  • values: 2 dimentional Array – [[“This is”, “a table”], [“this is”, “a new row”]]

Using these parameters we can create a table from within the Script lab using the following code:

$("#run").click(run);

async function run() {
    try {
        await Word.run(async (context) => {

            var body = context.document.body;
            var range = context.document.getSelection();
            var myArray = [["a", "b"], ["c", "d"]];
            var table = range.insertTable(2, 2, "Before", myArray);            

            // Synchronize the document state by executing the queued commands,
            // and return a promise to indicate task completion.
                await context.sync().then(function () {
                    console.log('Table added before the start of the range.');
                });;
        });
    }
    catch (error) {
        OfficeHelpers.UI.notify(error);
        OfficeHelpers.Utilities.log(error);
    }
}

 

 

Conclusion

Using the Script Lab we have seen how we can easily insert a table into a Word document using the Office Add-In API. In future articles we will look at looking for the table we want to modify and then manipulating tables.

 

 

Advertisements

Office Add-Ins – JavaScript control over the Content Control lock in a Word document

In this article I will show how easy it is to programmatically lock and release the lock on a content control in a word document. This is very helpful when you are populating regions of a document but do not want users to mess with the format of the contents.

Introduction

In the Word 1.3 release of the office.js model, Microsoft release the new “cannotEdit” property of a content control. This is a get and settable property. More information on the properties available are found here in the documentation

https://github.com/OfficeDev/office-js-docs/blob/WordJs_1.3_Openspec/reference/word/contentcontrol.md

Unlocking a content control for editing

Here is my locked content control called “Checklist”. I am going to use the code to get it by Tagname and then unlock it.

In your JavaScript code when you are about to update the control you need to execute the following as a minimum. It seems like a lot of code but it is due to the Promised based architecture used for the Office Add-In APIs.

    Word.run(function (context) {

        var contentControlsWithTag = context.document.contentControls.getByTag('Checklist');
        // Queue a command to load the tag property for all of content controls.
        context.load(contentControlsWithTag, 'tag');

        // Synchronize the document state by executing the queued commands,
        // and return a promise to indicate task completion.
        return context.sync().then(function () {
            if (contentControlsWithTag.items.length === 0) {
                console.log('No content control found.');
            }
            else {
                return context.sync()
                    .then(function () {
                        //the contentControlsWithTag is always returned as an array of items
                        contentControlsWithTag.items[0].cannotEdit = false;
                        contentControlsWithTag.items[0].insertHtml("<b>Hello World</b>", 'Replace');
                    });
            }
        });
    })
    .catch(function (error) {
        console.log('Error: ' + JSON.stringify(error));
        if (error instanceof OfficeExtension.Error) {
            console.log('Debug info: ' + JSON.stringify(error.debugInfo));
        }
    });    

Once you have unlocked the control your code can be inserted and the control is editable. In a real application you just have to make sure you lock averything again with

   contentControlsWithTag.items[0].cannotEdit = false;
   contentControlsWithTag.items[0].insertHtml("<b>Hello World</b>", 'Replace');
   contentControlsWithTag.items[0].cannotEdit = true

Conclusion

Nice, simple to use locking control. Yes the users cant unlock this manually and mess with the document, but if they are going there, then it is their own fault. This way no changes can be made “by mistake”.

 

Using an Office Add-In to search and replace data in a Word Document

In this article I will demonstrate how we can use an Office Add-In to perform simple search and replace function within a Word document. This is particularly useful when you want to use an external cloud source to insert data into your documents.

Introduction 

This example comes directly from the Microsoft GitHub example on Word Add-In Document Assembly. (https://github.com/OfficeDev/Word-Add-in-DocumentAssembly). The reason I am blogging about it is that it did not appear in any searches for me and I stumbled across it purely by accident.

Filling a document template

Setup

I grabbed a sample word document template from Word Online (https://templates.office.com/en-sg/Formal%20business%20letter-TM00002133) and we are going to replace the items in this document programmatically.

r1

I created a blank Word Add-In locally and then inserted my local FirebugLite capability just to create a quick and easy demo without having to go through the trouble of hosting the code anywhere.

r2

Basic search and replace

The basic code for search and replace is as follows:

function handleSuccess() {
	app.showNotification("Replacement successful", "Success");
}

function handleError(result) {
	app.showNotification("Error", "ErrorCode = " + result.code + ", ErrorMessage = " + result.message);
}
	
Word.run(function (ctx) {

	// Queue a command to search the document for the string "Contoso".
	// Create a proxy search results collection object.
	var results = ctx.document.body.search("[Recipient Name]");      //Search for the text to replace

	// Queue a command to load all of the properties on the search results collection object.
	ctx.load(results);

	// Synchronize the document state by executing the queued commands,
	// and returning a promise to indicate task completion.
	return ctx.sync().then(function () {

	  // Once we have the results, we iterate through each result and set some properties on
	  // each search result proxy object. Then we queue a command to wrap each search result
	  // with a content control and set the tag and title property on the content control.
	  for (var i = 0; i < results.items.length; i++) {
		results.items[i].insertHtml("Marky The Receiver", "replace");     //Replace the text HERE
	  }
	})
	// Synchronize the document state by executing the queued commands.
	.then(ctx.sync)
	.then(function () {
	  handleSuccess();
	})
	.catch(function (error) {
	  handleError(error);
	})
});

Running this example we can see the replacement was successful in both places

r3

r4
So to complete this as an example for the whole document I use a sample data object as it would be returned from a cloud REST provider and cycle through all the elements to be replaced.

function handleSuccess() {
	app.showNotification("Replacement successful", "Success");
}

function handleError(result) {
	app.showNotification("Error", "ErrorCode = " + result.code + ", ErrorMessage = " + result.message);
}

var data = {

	date: "22 Aug 2016",
	sender: "Someone really important",
	company1: "The Boss | Company 1 | Somewhere | Here | There | 12345",
	company2: "The Bigger Boss | Company 2 | Somewhere else | Near | Canada | 98765"
}
	
Word.run(function (ctx) {

	var results = ctx.document.body.search("[Recipient Name]");      //Search for the text to replace
	ctx.load(results);

	return ctx.sync().then(function () {
	  for (var i = 0; i < results.items.length; i++) {
		results.items[i].insertHtml("Marky The Receiver", "replace");     //Replace the text HERE
	  }
	})
	.then(ctx.sync)
	.then(function () {
		var results = ctx.document.body.search("[Date]");      //Search for the text to replace
		ctx.load(results);

		return ctx.sync().then(function () {
		  for (var i = 0; i < results.items.length; i++) {
			results.items[i].insertHtml(data.date, "replace");     //Replace the text HERE
		  }
		})
		.then(ctx.sync)
		.then(function () {
			var results = ctx.document.body.search("[Title | Company | Address | City | State | Zip]");      //Search for the text to replace
			ctx.load(results);

			return ctx.sync().then(function () {
			  
				results.items[0].insertHtml(data.company1, "replace");     //Replace the text HERE
				results.items[1].insertHtml(data.company2, "replace");     //Replace the text HERE
			  
			})
			.then(ctx.sync)
			.then(function () {
				var results = ctx.document.body.search("[Sender Name]");      //Search for the text to replace
				ctx.load(results);

				return ctx.sync().then(function () {
				  for (var i = 0; i < results.items.length; i++) {
					results.items[i].insertHtml(data.sender, "replace");     //Replace the text HERE
				  }
				})
				.then(ctx.sync)
				.then(function () {
				  handleSuccess();
				})
			})
		})
	})
	.catch(function (error) {
	  handleError(error);
	})
});

And here’s the final output

r5

r6

Conclusion

Using the Word JavaScript API through an Office Add-In we are able to use a search and replace technique to take external data and complete a template. This is especially powerful when you consider it in the context of a cloud service integration like O365, or CRM clouds like MS Dynamics or Salesforce.

The two sides of the incremental operator in JavaScript ++

I learned this last week, possibly highlighting my non-classical programming training. I have never come across this in all my years of JavaScript and apparently it is pervasive in other languages such as Java as well.

Incremental Operator ++

Many times I have seen or used the following method for incrementing an integer count

var marky = 0;
console.log(++marky);
console.log(marky);

This increments the integer marky by 1. not rocket science. In the image below you can see that the two console.log entries always return the same value

j1

What I learned yesterday was using the ++ after the variable name also increases the variable – but only AFTER the value has been evaluated…

From this image you can see that the marky++ value and the marky value are not the same. The console.log(marky++); returns the existing marky value and then increments it.

j2

var marky = 0;
console.log(marky++);
console.log(marky);

I have to ask myself “why would anyone do that?”. Why would I want to return a variable value and THEN increase it’s value, seems a little odd to me. I guess someone thought it was a good idea…..

Always makes me think “I wonder what else I don’t know? Probably best not think about that too much”….

 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators

How to add icons to individual items in a Select2 multi-value field

Select2 is one of the best user interface enhancers I have come across – if you do not know what it is then you need to go play with it.

It transforms SELECT boxes into dynamic, stunning, interactive UI elements and allows for all sorts of customizations and developer fun.

What I want to be able to do is select items from different categories within the select2 box and then add an icon displaying to the user which category they came from. In this article I will show how.

Example

I want to take this

s3

turn it into this with a type aheads2

And when a user selects the items – make this based on the category selected

s2

And it is really pretty simple and straightforward.

The Code

We start with our select box

<select style="width:500px" id="vehicle" multiple="multiple">
<optgroup label="Cars">
	<option class="car">Honda</option>
	<option class="car">Toyota</option>
	<option class="car">Ford</option>
	<option class="car">GM</option>
</optgroup>
<optgroup label="Bikes">
	<option class="bike">Harley Davidson</option>
	<option class="bike">Kawasaki</option>
	<option class="bike">Aprilia</option>
	<option class="bike">Ducati</option>
</optgroup>
</select>

and then we add our select2 code

$("#vehicle").select2().on("change", function(e) {
	if (e.added) {
		var icon = ""
		$('.select2-search-choice DIV').filter(function() {
		    icon = '<img src="images/'+e.added.css+'.png"/>&nbsp;';
		    return $(this).text() == e.added.id;
		}).prepend(icon);
      }
})

So what this does is:

  • select the #vehicle DOM element
  • turn it into a Select2 plugin control
  • When the control is changed and if an element is added
  • Find the DIV which has been created to display the new item
    • We use filter based on the newly added.id to make sure we only get the one just created
  • create the HTML for the appropriate icon based on the class of the selected item
  • prepend that icon HTML inside of the DIV containing the newly selected option

Conclusion

I have barely scratched the surface of what is possible with Select2. But I hope that you can see with only 9 lines of code we have significantly improved a user experience 🙂

I love Select2 and the options are endless

Enjoy 🙂

Prototypal inheritance of SSJS across the whole server in XPages

A friend asked me the other day how would I determine where a string lay within a JavaScript Array. I of course answered – “it depends! If you are using newer IE or a non-crappy implementation of the JavaScript engine then use Array.indexOf – it has been around for years…..”

var arr = ["simon", "marky", "david"]
return arr.indexOf("marky") // 1.0

Well – turns out the friend was asking about SSJS in XPages and it turns out that apparently XPages has one of those crappy JavaScript engines of which I spoke and Array.indexOf is not supported (facepalm). (*really??*)

<xp:text escape="true" id="computedField1">
	<xp:this.value><![CDATA[#{javascript:
		var arr = ["simon", "marky", "david"]
		return arr.indexOf("marky")
	}]]></xp:this.value>
</xp:text>

arr1

Polyfill

OK so there is a simple polyfill for the indexOf, which has also been around for years as this article written in 2007 demonstrates…..

if(!Array.indexOf){
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){
            if(this[i]==obj){
                return i;
            }
        }
        return -1;
    }
}

And if we add this to our XPages sample code we get the answer we are expecting….

<xp:text escape="true" id="computedField1">
	<xp:this.value><![CDATA[#{javascript:
		if(!Array.indexOf){
		    Array.prototype.indexOf = function(obj){
		        for(var i=0; i<this.length; i++){
		            if(this[i]==obj){
		                return i;
		            }
		        }
		        return -1;
		    }
		}

		var arr = ["simon", "marky", "david"]
		return arr.indexOf("marky")
	}]]></xp:this.value>
</xp:text>

arr2

Yay !

Prototyping in JavaScript

For a better explanation that I can give, check out this StackOverflow answer on what is prototyping – suffice to say that every JavaScript object has a prototype property and this is the basis for prototypal (as opposed to class based) inheritance in the JavaScript language.

What we did in the polyfill above is: we simply said if Array.indexOf method does not exist, then we created one.

So what does this have to do with XPages?

In JavaScript when you prototype a function, the scope is on the page you are looking at. As soon as you leave the page the prototype is gone. In XPages it seems to be a little broader than that…..

I (unintentionally) discovered that once you prototype the Array.indexOf, not only does it become available on the same page, it is permanently on the same page. You run the prototype code once….then take it away, and it still works. The scope would appear to be at the application level…..

Well so then I asked some people who are way smarter than I and Nathan Freeman pointed me to his article about XPages Performance Pro Tips and told me he expected that the prototype would actually be at the server level and he was right….

I included the URL of the test database in the picture above to demonstrate that when I move the simple code to another database – it still works (after the prototype code has been run once)

rr3

And just to be sure – in another browser !!

arr4

Once the Array.prototype.indexOf was created, it became available in all applications on the server, instantly.

Killing the prototype

  • Restarting HTTP *DOES NOT* stop this.
  • Killing all browsers and starting again does not stop this

As far as I can tell you have modified the way the SSJS works on the server until you kill it. So far I have run a test over 24 hours later and it is still working……

This is COOL right?

Well for one it means that you can seriously enhance all your JavaScript on the server in ways which make custom libraries look like a waste of time. We could truly add all the corporate libraries to SSJS once when the server loads and they would always be available to all developers at all times.

arr5

And the even more awesome (depending on perspective…..)

arr7

Well yeah but…….

You can also serious screw up perfectly good code across the server

arr6

Conclusion

Prototypal inheritance is at the root of JavaScript programming language and is very powerful. What we can see here though is that we are able to affect code across the entire server with a few lines of code. This is *very* cool and *very scary* at the same time.

So with this knowledge comes great responsibility – use it at your own risk and make sure you have very good server side code review if you are going to go down this path.