A better user experience with the dialog framework and notifications

19 Sep

Today I am playing around with the dialog framework and simple ribbon buttons. I want the user to select one or more items from a list and update the Status field for all the items selected. The user can edit multiple items in datasheet view of course, but I’m making it just a little friendlier to the user.

I give myself two challenges for today:       

Add a button to the ribbon, enabling it when one or more items in the list are selected and update the Status field to ‘Completed’ of the selected items. This will be described at the paragraph ‘Just the button’ below.       

For more flexibility I will extend the above solution by a modal dialog where the user can choose a value for the Status column instead of setting the value to ‘Completed’ (and is not mentioned to the user anywhere), update the selected items and give the user a notification message if the update was successful and mentioning the Status value from the control on the modal dialog. This will be described at the paragraph ‘Modal dialog’ below.

The last option will give the user a better experience and will be a lot more flexible, because I’m not setting the value of the Status field in the code, but let the user choose from a predefined set of values.

Not really challenges, but I just want to point out a few things which are maybe interested to you. I’m not going to explain the basics of the ribbon or the dialog framework, because there are a lot of excellent posts out there.

Just the button
Add a button to the ribbon: please check out the cmdui.xml file located the SharePoint root folder, template\global\xml. Here you find a lot of definitions and examples you can use yourself and really helpful by positioning the controls on the ribbon.       

To just enable a button when one or more items in a list are selected is quite easy:
when defining a CustomAction with for example a button, you also define a CommandUIHandler. One of the attributes is EnabledScript:       

EnabledScript="javascript: function enableMarkAsCompletedButton(){
                            var items = SP.ListOperation.Selection.getSelectedItems();
                            return (items.length >= 1);
                          }

                          enableMarkAsCompletedButton();”

Check the above code to enable the button (in this case) only when one or more items are selected. Here the client object model for ECMA script is used to get the selected items.

Another attribute is CommandAction, the real action to occur when the button in the ribbon is clicked. The code used here:

CommandAction="javascript:
    var context = SP.ClientContext.get_current();
    var currentList = context.get_web().get_lists().getById(SP.ListOperation.Selection.getSelectedList());
    var items = SP.ListOperation.Selection.getSelectedItems();
    var singleItem;

    for (var i in items) {
        singleItem = currentList.getItemById(items[i].id);
        singleItem.set_item('Status', 'Completed');
        singleItem.update();
    }


    //actually submit
    context.executeQueryAsync(OnSuccess, OnError);



    function OnSuccess() {
        window.location.href = window.location.href;
    }



    function OnError(sender, args) {
        alert('Error' + args.get_message());

    } "

First grab the context and the list we’re at. The getSelectedList() is the input of the getById() function. The result is the actual list where later the getItemById() function can be used on.
Also check the for-loop. You can’t use items[i].id directly in the getItemById() function on the list, but you have to grab the id of the single item and use this as the input parameter. Don’t use the context.load() function, just execute the query asynchronously. The OnSuccess() function refreshes the page so the adjusted value can be seen and an alert is displayed when an error occurs.

Modal dialog
By using a modal dialog and give the result back to the user the user experience will be much better. I don’t use a predefined value in the code but let the user choose which Status to set on all the selected items at once. Because I’m using a dialog some additional text to explain the function and result better will be convenient for the user.

For displaying a modal dialog the function SP.UI.ModalDialog.showModalDialog(options) will be used. The options I’m going to use here are the url, width, height and the most important to explain here the dialogReturnValueCallback.
The url is a custom page which will be opened. At the dialogReturnValueCallback a callback function will be specified, this means what is going to happen when the user closes the dialog. At our case the selected items in the list will be updated with the new status.
A lot of the above code can be reused at this scenario:
enabling the button when one or more list items are selected will not change at all.
updating the list items will occur at a different moment, because we’re going to display a dialog first to let the user choose the Status for the selected items.

First let’s look at the showModalDialog(options) and the page that will be opened at the url. I put the page at a subdirectory of the _layouts folder, so referencing is easy.
In the page itself I put a dropdown control with some status values and a button to actually set the status. For better user experience I will list the selected items at the model dialog, so the user is still aware of what items are going to change. To list the items selected I will add the parameters listguid and the id’s of the items selected to the url at the options for the showModalDialog function:

var options = {
        url: '/_layouts/ITIdea.DialogFramework/PageSetStatus.aspx?listguid=' + SP.ListOperation.Selection.getSelectedList() + '&items=' + selectedItems,
        height : 600,
        width : 500,
        dialogReturnValueCallback : CloseCallback};

        SP.UI.ModalDialog.showModalDialog(options);

And at the Page_Load function of the page I’m getting the listguid and selected items and display it in a simple label control on the page (please do not use this code as production code!):

        protected void Page_Load(object sender, EventArgs e)
        {
            if (Request.QueryString["listguid"] != null && Request.QueryString["items"] != null)
            {
                string list = Request.QueryString["listguid"].Substring(1, Request.QueryString["listguid"].Length - 2);
                Guid listGuid = new Guid(list);

                List<string> selectedItems = new List<string>();
                selectedItems.AddRange(Request.QueryString["items"].Split(','));


                SPList selectedList = SPContext.Current.Web.Lists[listGuid];


                string result = "Selected items: <br />";

                foreach (string item in selectedItems)
                {
                    result += selectedList.GetItemById(int.Parse(item)).Title + "<br />";
                }
 

                selectedItemsText.InnerHtml = result;
            }

        }

And this is how is looks like in its simplest form:

After the ‘Set’ button is selected the list items will be updated and for better user experience the user will be notified by a yellow notification on the right of the screen to what status the selected items are set. All this can be done at the function defined at dialogReturnValueCallback in the options of the showModalDialog, CloseCallback:

function CloseCallback(result, target) {
    if(result == SP.UI.DialogResult.OK){
      SP.UI.Notify.addNotification('Status value is set to ' + target, true);
      //set the status of all the selected items to the text
      AdjustFields(target);
    }
    if(result == SP.UI.DialogResult.Cancel){
      SP.UI.Notify.addNotification('Result is Cancel');
    }
}

function AdjustFields(newValue){
  var context = SP.ClientContext.get_current();
  var currentList = context.get_web().get_lists().getById(SP.ListOperation.Selection.getSelectedList());
  var items = SP.ListOperation.Selection.getSelectedItems();
  var singleItem;
  for (var i in items) {
      singleItem = currentList.getItemById(items[i].id);
      singleItem.set_item('Status', newValue);
      singleItem.update();
  }


  //actually submit
  context.executeQueryAsync(OnSuccess, OnError);

}


function OnSuccess() {
    setTimeout(window.location.href = window.location.href, 3000);
}


function OnError(sender, args) {
    alert('Error' + args.get_message());
}

The code for actually update the selected list items is moved to the AdjustFields function with newValue as input parameter and executed at the callback function of the dialog. This newValue is the value selected in the dropdown by the user at the modal dialog. How did we get that value over here?
Here another function of the SP.UI.ModalDialog comes in: commonModalDialogClose().
With this function you can set your own return value which will be used at the callback function of the showModalDialog(). Real nice function!!
To set a custom return value for the callback function, I use the commonModalDialogClose at the click eventhandler for the Set button at the page which is displayed as the modal dialog:

function SetNewStatus_Click() {
            var resultText = document.getElementById('StatusChoices').value;
            SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, resultText);
        }

In the above code the element ‘StatusChoices’ is the id of the dropdown control with the different status values. With the commonModalDialogClose() the dialog result is set to OK and the result value is set to the selected value in the dropdownbox.
By setting the result to OK, the code in the CloseCallback() function will execute the code in the first if-statement. Here the message for the notification is set and the return value (target parameter, which was set at SetNewStatus_Click() ) is added to the notification message.
       

After explaining this simple example you’re getting the idea of a better user experience with modal dialogs and notification messages. Inform the user as much as you can, the options are given to you by SharePoint and who are we not using them…

13 Replies to “A better user experience with the dialog framework and notifications

  1. Pingback: Custom Ribbon button doesn’t show up

  2. WOW, I happen to have just completed a project where I customized a ribbon, added a drop-down with several statuses in it… when the user checks off any number of items, they can then choose which status they want to assign via the drop-down in the ribbon! In my case, after the status is assigned, the item is then copied to an archival list that I have set up and removed from the current view.

    I’m having one issue related to this piece of code:
    SP.ListOperation.Selection.getSelectedList()

    For me, I’m finding that it returns null if the current list being viewed is a Datasheet view.
    Do you experience this?

    Thanks for your great blog!

  3. Hi,

    Glad you liked the post and nice to hear you implemented the same sort of functionality.
    I didn’t try it on a Datasheet view, so I don’t have any experience with that, but thanks for pointing this out.
    Did you write a blogpost about your experiences? I’d love to read it!

    Anita

  4. Thank you for an additional excellent post. Exactly where else could anybody get that kind of data in these kinds of a perfect way of writing? I’ve a presentation subsequent week, and I’m on the appear for such details.

  5. Pingback: Using async call to enable a custom ribbon button

Comments are closed.