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

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

Node Package Manager

As I delve ever deeper into the world of Node, I have found the ability to install packages with NPM quite handy. I do this from VS Code using the Terminal window. I just type:

npm install <packagename>

As I have been developing different packages for my customers, I have found the need to install code that I have been reusing over and over again. Most importantly, easyEws. So, I created a npmjs account (davecra) and I published easyEws. But what is even better and what I was after, is I can now install the latest version of easyEws by going into the Terminal window in VS Code and typing this:

npm install easyEws

 

Debugging JavaScript 101

I am “newish” to web development in the modern era. By modern era I mean, that I was writing webpages before “getting on the web” was cool. Before the movie “You Got Mail” came out, when AOL, dialup and Netscape were all still “the thing.” So, I know web development back then, probably up through 2007’ish. And I have done very little serious web development until 2 years ago when I really started delving into OfficeJS. So here are some things I have learned and would like to admit I did not know… I mean, pass along to you:

  1. Node. This heralds back to my previous post on Yo Office. In this post, I call out that NodeJS/BrowserSync is a powerful feature. I can safely say, as I have been working with it more and more, it has changed my life for the better. wlEmoticon-openmouthedsmile.png I have also been using Node and VS Code in an effort to understand this newer way of development. I have had a number of colleagues convince me to keep on keeping on with it – it will grow on me, they swear. Ok… So, what I like is when I hit save in VS Code and go back to the browser and click my add-in button (for a function-file), or open my taskpane again – it is current. I DO NOT have to refresh the browser. Browser-Sync takes care of all this for me.
  2. Chrome. If you are debugging outside of Visual Studio, I have found Chrome to be the best browser with which to debug. Here is why:
    • It’s Incognito mode is the best option for making sure you always have the most recent files and things are not cached. Edge and IE have private modes, but there is the next issue…
    • Edge and IE are tied into Windows and when you try to log into your developer tenant from them, it automatically grabs your credentials from Windows and takes you to your company email. Ugh!
    • Incognito still lets you (with warnings) load certain extensions and you also get form fill, so it makes getting into your tenant mailbox super quick and easy.
    • F12, developer tools. Edge and IE also have, but look at Chrome’s: Enough said there.
  3. console.log(). This is a bit of a no brainer, but I have found that sprinkling my code with this helps me get a good idea of my code flow, especially with Async() hell that you can get into with OfficeJS.
  4. debugger; – this has saved my life. I know, I am a “noob,” but I had no idea how to set a breakpoint in my code and get the browser to stop there, right there… no not there, RIGHT THERE!!! There is a window in my office with easy access to the pavement 4 stories below. One day, the day when I found this (after much frustration with my inability to write coherent JavaScript – a common problem I have), I was about to see if I could bounce. So, what you do is place this one word in your code, and if you have the developer tools open… viola! OMG:

    debugger;

  5. try/catch – In C#, especially with the debugger attached, I sometimes worried about error handling after the fact. Partially because I was not sure all the exception types I would want to catch or just use a general exception handler. But to say unexpected things happen in JavaScript and OfficeJS all the time is an understatement. Especially if you pretend to be a JavaScript developer like me. wlEmoticon-winkingsmile.png
    • I use the console to always output errors.
    • And, I know this is coming, TypeScript makes things so much easier in this area. As a matter of fact, I know one colleague that will be telling me “of course you do not bounce,” and my problems would be so much less had I used TypeScript in the first place. For the record, TypeScript makes a horribly disastrous JavaScript developer like me into an only moderately bad one. wlEmoticon-hotsmile.png But, unfortunately, all my clients are writing code in JavaScript, so here I must suffer.
  6. And shrimp sandwich… that’s… uh… that’s about itwlEmoticon-surprisedsmile.png

As I learn new things, I will come add them to this list. But if you have some great tips on debugging, please comment below.

Dialogs in OfficeJS

I have been working on a number of projects for my customers and recently, dialogs have taken front and center stage. The Office.context.ui Dialogs are powerful, albeit a tad confusing and the documentation suffers from a few easily missed points.  Here is the documentation:

But in this post, I hope to explain everything I have learned. To start off with here is the code to issue the dialog:

  Office.context.ui.displayDialogAsync('https://localhost:3000/function-file/dialog.html',
      { height: 20, width: 30, displayInIframe: true },
      function (asyncResult) {
          dialog = asyncResult.value;
          // callbacks from the parent
          dialog.addEventHandler(Office.EventType.DialogEventReceived, processMessage);
          dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage);
      });

What this dialog does is opens as a modal form in a frame over the application (in Office Web Apps). It looks like this:

screen.PNG

As you can see the dialog is modal. But what is really important are the two event handlers you need to register to be able to get back to your code:

          dialog.addEventHandler(Office.EventType.DialogEventReceived, processMessage);
          dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage);
The first one is an event receiver and really this is the event handler for errors, such as being unable to open the dialog or, most importantly, the user closed the dialog by clicking the (X) in the upper right of the dialog. There are a series errors you can catch, but specifically, the dialog cancel is this:
12006 The dialog box was closed, usually because the user chooses the X button.
The second one is a handler for messages coming from the dialog. These messages can be anything, but is usually a string or a JSON string. You can send a message from the dialog like this:
Office.context.ui.messageParent('{message}');
When the dialog issues a message using the code above, the function defined in the event handler is called. For example, if the user clicks an OK button or Submit button, you can pass the stringified values from the form back to the callback function. From there the dialog actually remains open until the caller issues a close, like this:
    // close the dialog
    dialog.close();
In the example above where I make the displayDialogAsync call, you will see I defined the SAME callback function for both dialog events. I did this because the results can be parsed by the same function. Here is what my function look like:
function processMessage(arg) {
    // close the dialog
    dialog.close();
    // procress the result
    if(arg.error == 12006) {
      // user clicked the (X) on the dialog
      sendEvent.completed({ allowEvent: false });
    } else {
      if(arg.message=="Yes") {
        // user clicked yes
        sendEvent.completed({ allowEvent: true });
      } else {
        // user clicked no
        sendEvent.completed({ allowEvent: false });
      }
    }
}
My previous blog post references all the code for the dialog:

Outlook OnSend and Dialog Sample

As promised, I have created a GitHub repository for the sample I blogged about earlier. Here is the repository:

https://github.com/davecra/outlook-sample-1

This includes three items of interest:

  1. The OnSend event, implemented in the simplest of ways to demonstrate bare bones how to get it to work.
  2. The displayDialogAsync with the inline frame (displayInFrame) option. I will blog about this in the future as well.
  3. A project build by Yo Office and published per my blog post here.

What this sample add-in does is pops up a dialog box anytime you press Send on an email with a question: “Are you sure?” If you click Yes, it sends, if you click “no” is blocks the send. Here is what that dialog looks like:

dialog

If you have followed the steps from my Yo Office post, you will be able to open the project in VS Code, and from the terminal, launch it with:

npm start

Next, you will need to follow the steps to enable the OnSend policy in your account and then you will need to install/sideload the manifest. From there you will not see any appearance of the add-in at all, until you press the Send button.

NOTE: At the point I first tested it on June, 13th, 2017, there is a KNOWN ISSUE with the certificates created fro a “yo office” build. If you have not resolved the certificates issue you will be able to start the project in Node.js, but the add-in will fail to run. You need to perform the steps outlined here to correct it:

https://github.com/OfficeDev/generator-office/issues/244