OfficeJS.dialogs version 1.0.8

I have recently updated the OfficeJS.dialogs to version 1.0.8. I have also updated the Node Package Manager version as well. You can get it by running the following command in the VSCode Terminal Window:

npm install officejs.dialogs

With this latest version comes a few bug fixes, some cleanup of the code, cleaner JSDoc documentation and a PrintPreview dialog. The PrintPreview started out as a proof of concept but it actually works for me – in the browser anyway. I am not sure how well it will work in any of the full clients as I have not tested it there yet. If anyone has a chance to test it, please let me know if you encounter any issues.

Here is a sample of the PrintPreview dialog:

print

Here is some code to implement it:

 

Start Developing in OfficeJS Today with Script Lab

Recently, I have been working with a tool from Microsoft Garage which allows you to quickly develop OfficeJS projects in Word, Excel and PowerPoint. It is called Script Lab. You can find more information about it here:

Script Lab

It is also in GitHub project. You can read about it here:

https://github.com/OfficeDev/script-lab

To install Script Lab, open Word, Excel or PowerPoint and on the Insert tab, click Store.

insert

Type “script lab in” the search box and hit enter. Then select Script Lab from the list and click Add.

store

Once added, you will see a new Script Lab tab. From there, you click the Code button to open the task pane.

scriptlab

This tool comes with a lot of options:

  • If you click the hamburger menu you will see an option to Sign Into GitHub. This will allow you to create Gist’s and access and save code snippets to GitHub.
  • You can import Samples from GitHub (with a URL) and access Snippets.
  • There are options to change the color in the editor at the bottom from Dark to Light.
  • And once in your solution, you can change the template (the html page), the CSS Style and any references libraries.

NOTE:  You are dealing with a subset of the whole solution here. You cannot create Ribbon buttons or dialogs. You can only develop task pane add-ins. You can work with Script Labs in Word, Excel and PowerPoint, but not Outlook (yet).

Script Lab allows you to test things in a quick fashion. Here is what the code pane looks like:

code.PNG

NOTE: Script Lab defaults to TypeScript. It emits code in JavaScript on Export. There is no option to switch to only JavaScript. If you want to program in JavaScript that does not matter because JavaScript will work just fine as well. But in a shout out to Michael Zlatkovsky – who says Office of TypeScript are a match made in heaven – I look at this as an opportunity to get better at TypeScript.

One of the more difficult part of working in OfficeJS is to be able to quickly test how some feature of function or a block of code will work. In the VSTO and VBA days, I would pop open the Visual Basic Editor in Word, Excel or PowerPoint (ALT+F11), then open the Immediate Window (CTRL+G) and then start typing away to test specific object model items. There was nothing similar to this with OfficeJS, until now.

Script Lab is just that, a laboratory for you to test your ideas, your snippets, your work in progress. It is a way to test something before you put it into your full blown solution. You can then save code snippets as Gist (on GitHub) and export the solution.

And once you have written your code and are ready to test it, you very easily run it in the Taskpane or (my favorite) you can run side-by-side. From the Ribbon click the Run button and then you can still edit your code/html and then refresh and reload it in the side-by-side Run window. This makes developing a solution or just testing ideas VERY easy:

side-by-side.PNG

Once you are running you (kind of) have debugging tools, can see the DOM and see console messages. And my favorite part, is that once you have hacked out a basic add-in concept with buttons and text in the HTML and code in the script file, you can export your solution (Export for publishing). This will create a ZIP which will contain the XML manifest and HTML (with script inline), which is very similar to the minimalist add-in I outlined in a previous blog post:

OfficeJS: Create a (VERY) Basic Add-in for Excel

NOTE: If you do write async/await code the exported result will be very ugly and hard to follow. This is why you might want to export to GitHub if you use async/awat code (TypeScript) so you can keep the original source and then transpile to ES5 later.

However, I really like VS Code and the power of NPM and Browser-Sync, so I worked on converting a Script Lab project into an NPM project. This took quite a lot of work and that will be a topic for a future blog post, but the gist of it is to create a new Office Yeoman (yo office) project for the same application and then gut and Frankenstein it with the Script Lab code.

Bottom line, I love Script LabwlEmoticon-openmouthedsmile.png If you are working on an OfficeJS project and/or planning on creating an OfficeJS project and/or want to learn how to develop in OfficeJS, this is definitely THE tool to have.

 

easyEws v1.0.7 is now released

This has been slow coming, but I have just released v1.0.7 of easyEws. As per usual, it is on NPM and on GitHub. You can get the latest with the command:

npm update easyews

Or, you can install it into your solution with the command:

npm install easyews

In this version the JSDOC information is now full incorporated so you get great inline help with each command. I enhanced the library so that it works better with inline help and so I changed how the class is setup and exposed. I have updated the README.md on GitHub, but I still am working on examples and such, that will be done later. And I added new functionality, a moveItem command which can be used to move any item from one folder to another. This was a added due to feedback I received on LinkedIn.

Please feel free to reach out to me with any questions or suggestions.

 

Deploying OnSend Outlook Add-ins?

If you have been developing OfficeJS add-ins for Outlook Online (or Exchange 2016 Outlook Web Access) you might not know that you now have the ability to create OnSend add-ins. That is add-ins that can trap the send of an email, test for conditions (such as getBodyAsync() or getSubjectAsync() to look for content) and if they fail, prevent the message from being sent. I recently blogged about it here:

The Most Anticipated OfficeJS Feature is Here

First thing to note about these add-ins is that they cannot be created for or uploaded into the store. The second thing to note is that they MUST be configured first in order to run. The third thing to note is that if you are building an on-prem Exchange based solution, you have to have Exchange 2016 CU6 installed and a bit more. The full details of how to configure the environments (either Office Online or On-prem) are detailed here:

On send feature for Outlook add-ins

If you look this over, there is quite a lot to do. How would you like one single PowerShell command to type? Maybe something like this:


Set-OwaOnSendPolicy

wlEmoticon-hotsmile.png Well, I just published the first version of this script on GitHub:

https://github.com/davecra/Set-OwaOnSendPolicy

All the details you need to know about it are there in the README.MD. However, if you have any questions or issues, please let me know.

 

OfficeJS: Second Dialog Does not Display

I have been spending a lot of time in the Officeui.dialog lately. One of my customers has been too and it has been an adventure working out exactly the best way to get messages displayed while running code in the background asynchronously. wlEmoticon-disappointedsmile.png

I am not sure if this problem is limited to the online version of Outlook, but this is where I have been seeing the problem (and where I have spent virtually all of my time). If my code tried to open two dialogs (using Office.context.ui.displayDialogAsync()) one right after the other, the second dialog would not ever be displayed. If I waited a period and then tried again, it would. But we don’t want that. We want boom-boom, dialogs. When I looked at the console, I would see an error like the following:

Uncaught TypeError: Cannot read property ‘addEventHandler’ of undefined

Or, if I read the error object from the displayDialogAsync() function/asyncResult, I would see this:

{name: “Display Dialog Error”, message: “The operation failed because this add-in already has an active dialog.“, code: 12007}

Here is an example of some code that will reproduce the issue:

var i = 0;
function displayDialog() {
  var url = "https://localhost:3000/test.html";
  Office.context.ui.displayDialogAsync(url,{height:20, width:30, displayInIframe:true},
      function (asyncResult) {
          var dialog = asyncResult.value; // get the dialog
          var error = asyncResult.error;
          if(dialog == undefined && error.code > 0) {
            // log the error
            console.log(error.message);
          } else {
            // attache the events
            dialog.addEventHandler(Office.EventType.DialogEventReceived, function (arg) {
              // close this dialog, open the next
              dialog.close();
              i++;
              if(i<4) {
                displayDialog();
              }
            });
            dialog.addEventHandler(Office.EventType.DialogMessageReceived, function (arg) {
              // close this dialog, open the next
              dialog.close();
              i++;
              if(i<4) {
                displayDialog();
              }
            });
          }
      });
}

Notice I had used dialog.close(), but it did not work as designed. What I believe is happening is that the previous dialog is still in memory and has not been cleaned up. What needs to likely happen is a closeAsync().

In order to resolve this, I created the following function: dialogCloseAsync(). This works by issuing the close() and then attempting to add an event handler to the dialog in ansyc (setTimeout) loop. When it errors, we trap the error and issue the async callback. It is a bit ugly as we are trapping an error to get around the problem, but this was the only way I could find a way around the problem. Here is what the function looks like:

/**
 * Closes the currently open dialog asynchronously.
 * This has an ugly workaround which is to try to set a new
 * event handler on the dialog until it fails. When it failed
 * we know the original dialog object was destroyed and we
 * can then proceed. The issue we are working around is that
 * if you call two dialogs back to back, the second one will
 * likely not open at all.
 * @param {Office.context.ui.dialog} dialog The dialog to be closed
 * @param {function()} asyncResult The callback when close is complete
 */
 function dialogCloseAsync(dialog, asyncResult){
    // issue the close
    dialog.close();
    // and then try to add a handler
    // when that fails it is closed
    setTimeout(function() {
        try{
            dialog.addEventHandler(Office.EventType.DialogMessageReceived, function() {});
            dialogCloseAsync(dialog, asyncResult);
        } catch(e) {
            asyncResult(); // done - closed
        }
    }, 0);
}

I had been encountering this issue with different systems when developing the OfficeJS.dialogs library and had tried to set a timeout before I showed each dialog. That worked on some systems, but on others the timeout needed to be longer. So, setting a default timeout did not work. Using this in the original sample, provided above, the code would look like this:

var i = 0;
function displayDialog() {
  var url = "https://localhost:3000/test.html";
  Office.context.ui.displayDialogAsync(url,{height:20, width:30, displayInIframe:true},
      function (asyncResult) {
          var dialog = asyncResult.value; // get the dialog
          var error = asyncResult.error;
          if(dialog == undefined && error.code > 0) {
            // log the error
            console.log(error.message);
          } else {
            // attache the events
            dialog.addEventHandler(Office.EventType.DialogEventReceived, function (arg) {
              // close this dialog, open the next
              dialogCloseAsync(dialog, function() {
                i++;
                if(i<4) {
                  displayDialog();
                }
              });
            });
            dialog.addEventHandler(Office.EventType.DialogMessageReceived, function (arg) {
              // close this dialog, open the next
              dialogCloseAsync(dialog, function() {
                i++;
                if(i<4) {
                  displayDialog();
                }
              });
            });
          }
      });
}

As I found this workaround, I have updated OfficeJS.dialogs to use dialogCloseAsync(). Now, the MessageBox, Wait and Form objects will use closeDialogAsync() commands to replace the original closeDialog() commands I provided previously. I will be blogging about the updates to v1.0.6, shortly. wlEmoticon-hotsmile.png

Using NPM to Publish and Update Packages

This blog post is more for me than you. I come from an era when command line was the only way to get things done. The folks that were able to do that in the 1980’s and early 1990’s, were called…

nerd

Well, for some reason, after advancements in GUI (Graphical User Interfaces) we are back to command-line interfaces. I have heard an argument that this makes it easier to build in voice command interfaces, but I do not see myself saying: “npm install”. I believe it came about because it is easier to create a command line interface quickly for free than it is to create a graphical one. Just sayin’. wlEmoticon-winkingsmile.png

With that said, lets get down the business. I published OfficeJs.dialogs to NPM and found a few last minutes bugs and needed to update it. Well, this seemed easy enough, but I was getting some error about my git working directory:

git working directory dialogs not clean

This turns out to happen because I needed to do both a push and a pull. Not sure why, but I did a push and sync and that did not resolve the issue. I had to do a pull from git too:

menu

Next, I had to then do a version update. To do this you type this in the Terminal window in VS Code once it is pointed to the repository folder:

npm version patch

This will add a single point to the version, so for me that was from 1.0.1 to 1.0.2. Next, you issue a single command:

npm publish

This will publish version 1.0.2 (in my case) to the node module in the sky (npmjs.com). Next, I then went to my project where I am using it and then issue this command:

npm update officejs.dialogs

And just like that, I had the latest version. Seems simple enough, but it took a while for me to research this and get it working. And since these are all command lines without a graphical ability to “hunt and peck” for the right menu item, I am writing it down for posterity. I will likely be referring to this post for myself again in the future.

Did you miss the Message… box?

Several customers have asked me if OfficeJS has something similar to a Visual Basic or C# MessageBox.Show() function. The answer is no. And for a long time there was not even a dialog option. With the latest releases of the OfficeJS libraries comes a new dialog feature. Yet to get a standard MessageBox, you will still need to create it from scratch. Or, at least until this blog post you did. I have created a helper library the consists of two files:

  • dialogs.js
  • dialogs.html

To reference this library you can do any of the following:

If you used NPM, you can reference then with a script tag like this:

<script type="language/javascript" src="./node_modules/officejs.dialogs/dialogs.js">

NOTE: This assumes your page is in the root of your project. The key point is that it is added to your node_modules when you use NPM and this is how you will reference it.

Once referenced you can then call a MessageBox like this:

MessageBox.Reset();
MessageBox.Show("This is a test with a lot of text that simply has not point but to show you what " +
                "happens when you place a lot of text in a MessageBox for any point other than to " +
                "place a whole lot of text in the MessageBox.",
                "This is the Caption",
                MessageBoxButtons.AbortRetryCancel,
                MessageBoxIcons.Stop,
                true, "Check this if you want me to stop nagging you.",
                function(buttonPressed, checked) {
                  console.log("The button pressed was: " + buttonPressed);
                  console.log("The checkbox was checked? " + checked);
                });

Here is what this will look like:

msg

You can call up an InputBox like this:

InputBox.Reset();
InputBox.Show("What value do you want to enter?",
              "InputBox caption",
              "Default value", function(result) {
                var msg = "";
                if(result.length == 0) {
                  msg = "The user pressed cancel.";
                } else {
                  msg = "The user entered: " + result;
                }
                console.log(msg);
              });

Here is what the above code looks like:

input

You can show a custom form of your own design like this:

  Form.Reset();
  Form.Show("/test.html",10,20,false,function(result){
    var yourJSON = JSON.parse(result).Result;
    // if you placed false in the 4th param you must
    // handle the close of the form
    Form.DialogClose();
  });

Here is an example of what the above code looks like:

form-ex

It is important to note that like everything else in the OfficeJS world this is an async dialog. This also means it is non-blocking. This means any code you do not have in your callback method will continue to run. And if you are wanting to display multiple message boxes at the same time – you cannot. The last one you try to display wins, the others will be gone. Most everything in this dialog is just like you will remember from the good ol’ Visual Basic/C# MessageBox and InputBox. Even the constants for MessageBoxButtons and MessageBoxIcons are the same. But, I added a little flare and it probably helps with the best practices in OfficeJS to not nag the user with dialogs, and that is the ability to add a check box to the form so you can ask the user if they do not want to be bothered by your code anymore.

For the MessageBox, the withcheckbox and checkboxtext are there to give you that ability. Additionally, you see the callback method (asyncResult) that will return once the use dismissed the dialog. It will return with two pieces of information:

  • The button the user clicked in string format. So “Yes” or “Cancel” will be what you see here.
  • A Boolean representing whether the check box was checked or not.

For the InputBox, the callback method (asyncResult), will return one piece of information. If will return the text the user entered, or it will return nothing (an empty string), if the user pressed cancel.

The Form method will return a JSON object:

Error: { },                  // Error object
Result: { },                 // JSON from form
Cancelled: false       // boolean if formed cancelled with X

The Result object will be the JSON from your own form. In your code you will need to call back to the parent like this:

Office.initialize = function(reason) {
    $(document).ready(function () {
        $("#okButton").click(function() {
            // notify the parent
            Office.context.ui.messageParent("My custom message.");
        });
    });
};

You will also see in the examples above, I call .Reset() before I issue a new dialog request. This is because the objects are global and this is a way to be certain to clean up anything in memory that might be associated with a previous dialog. In my testing, I never really had problems like this, but I added it as an extra precaution.

Also, note, I have only tested this in Outlook OWA, I have not had a chance to test it in Excel, Word, PowerPoint or even in the full Outlook client. So, if you encounter issues in those other clients, please let me know.

Finally, I want to call out the OfficeJS Helpers. This library provides a lot of help with authorization dialogs, but also has a simple method for displaying messages using OfficeHelpers.ui.notify(). You can install it into your project using NPM:

npm install –save @microsoft/office-js-helpers