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.dialogs v1.0.7 Published

So, I kind of skipped v1.0.6, which I promised in this post yesterday. There were some unchecked changes on a different laptop that I forgot in v1.0.6, so I published v1.0.7 this morning with all the updates. Some notable update:

  • Moved from CloseDialog() to CloseDialogAsync() on all the dialogs using the original close method. This is to workaround the issue described in the post yesterday.
  • Fixed dialog flashing by hiding the body until the form is ready to be shown. Before you might get a flash of all the controls on the form as they were not hidden before.
  • Internal clean-up and message handling. In the message pump between the dialogs.html and dialogs.js, I cleaned up the pump to use internally standardized messages.
  • Fixed some JSDoc information that was wrong. And there is likely still more. wlEmoticon-disappointedsmile.png
  • Fixed some things that were not working correctly with the progress dialog.
  • Small tweeks and comments, code clean-up

The latest version is now on GitHub. I have also completed all the documentation and updated it to the latest usage information, code examples and screenshots.

I also published the latest version to NPM. Simply type this in you code terminal window:

npm -install officejs.dialogs

CDN Update

I still have a published CDN for OfficeJS.dialogs, but at this point I am going to suggest against using it. I have encountered a number of issues with my message pump when loaded cross-domain. Specifically:

  • you will be able to show a MessageBox, but the Update() method will not work
  • you will be able to show a Progress dialog, but the Update() method will not change the dialog. The progress bar will never advance.

What is happening is that I was going to need for **you** the  developer to provide me your own proxyHTML file (pointing to my CDN/proxy.js library) that I will do the following:

  • Add an iframe, load my html file in that iFrame, post the dialog settings to the iFrame where my dialog in the iFrame gets it, and writes it to localStorage on the CDN domain.
  • Window.replace the contents with my dialogs.html from the CDN. And since the localStorage now has the dialog settings, configure the dialog.
  • So far at this point everything worked. From HERE is where I am pushing my CORS and JavaScript to the edge of sanity…
  • I then create an hidden iFrame in my dialog and then load your proxy html file. I attach to the body change event inside the iFrame.
  • I then start a message pump in the proxy looking for changes to localStorage.
  • If a new message comes from your code with an Update() on MessageBox or Progress, I then get it from the localStorage in a message and write it to the body, therefore updating the iFrame in my dialog, thereby triggering the change event I latched on to, allowing me to write that value to the CDN localStorage where the message pump in dialog will get the message and update the form.

That hurt my brain writing that out. And maybe that is more for my posterity since it took me Binging the Internet to death (I don’t use to the G word folks wlEmoticon-hotsmile.png), just to get that far. My pretzel, I mean head hurts.

Bottom line, I want to get OfficeJS.dialogs to work cross-domain. I am not sure if the above method is the right approach. I have asked far brighter minds than my own and watched Autie Anne’s Syndrome set in almost immediately. So, if any of you have some ideas on how I can establish 2-way communication between domains like this (without using some crazy huge library), please let me know. wlEmoticon-smile.png

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

OfficeJS.dialogs Updated (v1.0.5)

I have been working hard on my OfficeJS.Dialogs library and just published version 1.0.5. You can get it from NPM and GitHub. See my previous post for more information on how to do this.

I have added a few new features:

  • A simple Alert.Show() method that displays a simple OK box. For those times you want to just simply pop up a quick notification to the user.
  • A Progress.Show() that displays a progress bar. This allows for you to show the progress bar and then issue Progress.Update() to move the progress bar along. When you are done you call Progress.Complete().
  • A Wait.Show() dialog that will show an indeterminate spinner. This form will remain up until you issue a Wait.CloseDialog().
  • New UpdateMessage() and Update() methods were added to the MessageBox. This was done to allow you to quickly ask a lot of questions of the user in one instance of the dialog, without giving the user back to the application for a second while the new dialog is rendered. UpdateMessage() will just update the message but keep all the buttons the same, but you will specify a new callback. Update() will allow you to fundamentally change all the settings the MessageBox (buttons, icon, caption, text and all), plus a new callback function.
  • Behind the scenes I made some improvements/bug fixes:
    • If you try to show two dialogs too quickly, nothing will happen. So I added a half-second delay between dialog displays to make sure you never get an overlap.
    • You will get an error message in your callback if more than one dialog is attempted to be opened at once.
    • “Window Messaging” has been setup with Progress and MessageBox to allow the parent and the dialog to pass messages back and forth. It involves using setTimeout().

For those interested in the last item, here is what that look like:

        /**
        * Handles messages coming from the parent
        */
        function startMessageHandler() {
            setTimeout(function() {
                var message = localStorage.getItem("dialogMessage");
                localStorage.setItem("dialogMessage", ""); // clear the message
                if(message !== undefined && message !== null && message != "")
                {
                    var msg = JSON.parse(message);
                    if(msg.message == "update") {
                        // update the form
                        updateForm(msg.settings);
                    } else if(msg.message == "close") {
                        // do nothing special here
                        return; // stops the message pump
                    } else if(msg.message == "progress") {
                        if(msg.settings.Number > 100) return;
                        $("#bar").prop("value",msg.settings.Number);
                    }
                }
                startMessageHandler(); // call again
            }, 0);
        }

Here is an example of how to use the Progress dialog. The method signature is like this:

Progress.Show( [message], [start], [max], [completeCallback], [cancelCallback] )

  • The message is the message the user see’s when the dialog is opened.
  • The start is the number you want the progress bar to start at. Usually this should just be zero (0).
  • The max is the number you want the Progress bar to end at. Usually this should be 100. But it can be any number you want. If you have 5 steps to perform in the background while this dialog is up, you can set this to 5.
  • The completeCallback is your callback function to be called when your code calls the Progress.Compelte().
  • The cancelCallback is what gets called when the user presses the Cancel button on the form.

By itself, this will do nothing. You will have to call the Progress.Update() command in order to move the progress bar, or update the message to the user. Here is the method signature for the Update method:

Progress.Update( [amount], [message] )

  • The amount is how much you want the progress bar to move. If you do not specify an amount, an amount of 1 is assumed.
  • The message is a new message to provide the progress bar. If you want to update the message and do not want to increment the progress bar, specify an amount of zero (0).

Once you are all done with the Progress dialog, you issue a Progress.Complete() call. There are no parameters to it. Once called, your completeCallback in the Progress.Show() call will then be executed.

Here is an example:

// reset first to make sure we get a fresh object
Progress.Reset();
// display a progress bar form and set it from 0 to 100
Progress.Show("Please wait while this happens...", 0, 100, function() {
    // once we are done - when your code
    // calls Progress.Complete()
    Alert.Show("All done folks!");
  }, function() {
    // this is only going to be called if the user cancels
    Alert.Show("The user cancelled");
});
doProgress();

function doProgress() {
  // increment by one, the result that comes back is
  // two pieces of information: Cancelled and Value
  var result = Progress.Update(1);
  // if we are not cancelled and the value is not 100%
  // we will keep going, but in your code you will
  // likely just be incrementing and making sure
  // at each stage that the user has not cancelled
  if(!result.Cancelled && result.Value <= 100) {     setTimeout(function() {       // this is only for our example to       // cause the progress bar to move       doProgress();     },100);   } else if(result.Value >= 100) {
    Progress.Compelte(); // done
  }
};

That example also uses the new Alert dialog. This one is very simple:

Alert.Show ( [message] )

This next example uses the new Wait dialog, which is much simpler to implement. Here is the method signature:

Wait.Show( [message], [showCancel], [cancelCallback] )

  • The message is the message you want to show the user. If you specify null, it will appear as simply “Please wait…”
  • The showCancel flag if set will allow the user to see a Cancel button.
  • The cancelCallback function is only valid if the showCancel option is true. When the user presses cancel, this function gets called.

When you are ready to close the Wait dialog, you issues a Wait.CloseDialog(). Here is an example:

  Wait.Show(null, true, function() {
    Alert.Show("The user cancelled.");
  });
  setTimeout(function(){
    Wait.CloseDialog();
    Alert.Show("Done!");
  }, 15000);

If you have some suggestions for some things you would like to see added to this library, please add a comment below or reach out to me on LinkedIn or Twitter. Some ideas I will be working on:

  • Allow you to call another dialog type without having the close the dialog.
  • A selection dialog, where you have a dropdown list of a listbox where you wan select (or multi-select) items.
  • An option to resize forms.
  • An option to use the message handler in your own custom form – minimal code