SharePoint 2013 Step by Step Implement ModalDialog With Promise Pattern

Overview

While binding data to the page there are sometimes certain logic or an operations that need to be done to fetch/update/delete records. Until then end user must wait (ideal scenario 2-3 sec, this may vary based on other parameters) . During this delay in binding the content, it would be a good user experience to show an "Appropriate Message". This can be implemented in SharePoint 2010/2013 with ShowDialog with a promise pattern deferred object.



Scenario

I am showing a modal dialog while the following operations are taking place.

  1. Get Parameter value from Query String.
  2. Get List Item from SharePoint List.
  3. Get Content Type of the List Item.
  4. Bind List Item data to label controls.

Here is the HTML source:

  1. <div id="divemployeeData">  
  2.     <table class="table">  
  3.         <tbody>  
  4.             <tr>  
  5.                 <td>  
  6.                     <b>Employee Name</b>  
  7.                 </td>  
  8.                 <td>  
  9.                     <label id="employeeName"></label>  
  10.                 </td>  
  11.             </tr>  
  12.             <tr>  
  13.                 <td>  
  14.                     <b>DOJ</b>  
  15.                 </td>  
  16.                 <td>  
  17.                     <label id="employeeDOJ"></label>  
  18.                 </td>  
  19.             </tr>  
  20.             <tr>  
  21.                 <td>  
  22.                     <b>Location</b>  
  23.                 </td>  
  24.                 <td>  
  25.                     <label id="employeeLocation"></label>  
  26.                 </td>  
  27.             </tr>  
  28.         </tbody>  
  29.     </table>  
  30. </div>   
The following JavaScript has two modules. One of the modules is of utilities (App_Core) and another one is for employee data services (employeeData).

How to call this JavaScript

The BindData method is a public method that will be called internally defined the loadItemDetails method. Call this method in a button click or page onload as "employee.Services.BindData();".

How the following logic works (Step-by-Step):
  1. Initially the BindData method is called, it internally triggers loadItemDetails.
  2. In the next line, the getEmployeeData method is used a parameter for the when method that would wait until it hears back either from resolve or reject method.
  3. When the getEmployeeData method is called, the variable deferred is defined & declared with a deferred object. (Note: the getEmployeeData method will fetch employee details based on employeeId. This employeeId is queried from URL string.)
  4. A dialog box is popped up with a custom message.
  5. Next, standard code to get SharePoint ListItem code is defined.
  6. When the executeQueryAsync is executed, a success instance will call deferred.resolve() and a failed instance will call deferred.reject().

    If it is success, deferred.resolve() is called defined at a success parameter and goes to the done() method defined at the loadItemDetails method.
    If it fails, deferred.reject() is called defined at the failed parameter and goes to the fail() method defined at the loadItemDetails method.
  7. When it reaches the done() method, It would loop through all the labels available in the div and bind the values of the respective labels. (Note: columnNames and labelNames are defined the same).
  8. For either of the scenarios, the close method is called to close the dialog window at the end of done() and fail() methods.
    1. var App_Core = App_Core || {};  
    2. //All reusable functions    
    3. App_Core.Utilities = function() {  
    4.     //Format Date field    
    5.     var getFormattedDateTime = function(d) {  
    6.         // padding function    
    7.         var s = function(p) {  
    8.             return ('' + p).length < 2 ? '0' + p : '' + p;  
    9.         };  
    10.         // default parameter    
    11.         if (typeof d === 'undefined')   
    12.         {  
    13.             var d = new Date();  
    14.         };  
    15.         var selDate = '';  
    16.         if (s(d.getHours()) > 0)  
    17.         {  
    18.             selDate = s(d.getDate()) + '/' + s(d.getMonth() + 1) + '/' + d.getFullYear() + ' ' + s(d.getHours()) + ':' + s(d.getMinutes()) + ':' + s(d.getSeconds());  
    19.         }  
    20.         else   
    21.         {  
    22.             selDate = s(d.getDate()) + '/' + s(d.getMonth() + 1) + '/' + d.getFullYear() + ' ';  
    23.         }  
    24.         // return datetime    
    25.         return selDate;  
    26.     };  
    27.     var removeSpaces = function(content) {  
    28.         if (content == null || content == '')  
    29.         {  
    30.             return '';  
    31.         }   
    32.         else   
    33.         {  
    34.             return content.replace(/\s/g, "");  
    35.         }  
    36.     }  
    37.     //function to get a parameter value by a specific key    
    38.     var getQueryStringParameter = function(param) {  
    39.         var url = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');  
    40.         for (var i = 0; i < url.length; i++)  
    41.         {  
    42.             var urlparam = url[i].split('=');  
    43.             if (urlparam[0] == param)  
    44.             {  
    45.                 return urlparam[1];  
    46.             }  
    47.         }  
    48.     }  
    49.     return  
    50.     {  
    51.         FormateDate: getFormattedDateTime,  
    52.         getQueryStringParameter: getQueryStringParameter,  
    53.         RemoveSpaces: removeSpaces  
    54.     };  
    55. }();  
    56. var employeeData = employeeData || {};  
    57. //Get employee details    
    58. employeeData.services = function() {  
    59.     //Global Variables    
    60.     var listName = 'employeeData';  
    61.     var employeeID = App_Core.Utilities.getQueryStringParameter('employeeID');  
    62.     var employeeListItem = null;  
    63.     var listContentTypes = null;  
    64.     var employeeColumns = null;  
    65.     var waitDialog = null;  
    66.     //employee required Columns    
    67.     var employeeListColumns = ['ID''employeeName''employeeDOJ''employeeLocation']  
    68.     //Get employee Item    
    69.         function getEmployeeData()   
    70.         {  
    71.             if (listName == null || listName == '')   
    72.             {  
    73.                 return false;  
    74.             }  
    75.             else   
    76.             {  
    77.                 if (waitDialog == null)  
    78.                 {  
    79.                     waitDialog = SP.UI.ModalDialog.showWaitScreenWithNoClose('Please Wait''Catalogue details loading....');  
    80.                 }  
    81.                 var deferred = $.Deferred();  
    82.                 //Load SP with Current Context    
    83.                 var ctx = new SP.ClientContext.get_current();  
    84.                 //Load List by Title    
    85.                 var employeeList = ctx.get_web().get_lists().getByTitle(listName);  
    86.                 //Load List Item by ID    
    87.                 employeeListItem = employeeList.getItemById(employeeID);  
    88.                 //Load the context with List Item of required columns    
    89.                 ctx.load(employeeListItem, employeeListColumns);  
    90.                 //get the content types and load the collection    
    91.                 listContentTypes = employeeList.get_contentTypes();  
    92.                 ctx.load(listContentTypes);  
    93.                 ctx.executeQueryAsync(  
    94.                 function()  
    95.                 {deferred.resolve(employeeListItem);},  
    96.                 function()  
    97.                 {deferred.reject(); });  
    98.                 return deferred.promise();  
    99.             }  
    100.         };  
    101.     //Binds the values to the label controls in details page.    
    102.     function loadItemDetails()  
    103.     {  
    104.         $.when(getEmployeeData()).done(function(employeeItem) {  
    105.             $('#divemployeeData label').each(function()  
    106.             {  
    107.                 var $element = $(this);  
    108.                 if ($element.val() == '')   
    109.                 {  
    110.                     var $label = $("label[id='" + this.id + "']");  
    111.                     $label.text(employeeItem.get_item(this.id));  
    112.                 }  
    113.             });  
    114.     waitDialog.close(); //Close Modal Dialog    
    115.             waitDialog = null;  
    116.         }) //End of deffered done method    
    117.         $.when(getEmployeeData()).fail(function() {  
    118.             alert('Failed to load Employee details');  
    119.             waitDialog.close(); //Close Modal Dialog    
    120.             waitDialog = null;  
    121.         }); //End of deffered fail method    
    122.     };  
    123.     return {  
    124.         BindData: loadItemDetails  
    125.     };  
    126. }();  
My Thoughts/Approaches
  1. Defining a separate module for all reusable functions.
  2. Naming labels the same as column names would help to bind data easily. (** Please comment if this is a bad practice).