How to – SharePoint 2010 – JS client object model and UI advancements


This blog entry shows how to utilize the new UI functionalities available in the SharePoint 2010 and how to use JavaScript object model to manipulate the site structure.

Create initial structure in VS2010

The first step is to create the Visual Studio solution. In this case we will create a new Empty SharePoint project with name ClientOMUIActions.

image

Since we will be adding Visual Web Part to the project, we will need to use the farm solution option.

 image

The following step is to add a new Visual Web Part item to the project – let’s name it UIDemo

 image 

Add dialog integration

In this case we need to add the dialog framework integration code to our visual web part, we can definitely use the same model in ribbon buttons, if required. Let’s add the following script element to the Visual Web Part canvas.

<script type="text/javascript">

   //Dialog opening
  
function OpenDialog() {
        var options = SP.UI.$create_DialogOptions();
        options.url = "/_layouts/ClientOMUIActions/ListCreationPage.aspx";
        options.width = 500;
        options.height = 400;
        options.dialogReturnValueCallback = Function.createDelegate(null, CloseCallback);
        SP.UI.ModalDialog.showModalDialog(options);
    }

    var messageId;

    // Dialog callback
  
function CloseCallback(result, target) {
        if(result === SP.UI.DialogResult.OK) {
            alert("OK was clicked!");
        }
        if(result === SP.UI.DialogResult.cancel) {
            alert("Cancel was clicked!");        
        }
    }

</script>

Here are a few things from the code worth pointing out

  • Notice the url property, which is set for the DialogOptions. This is the target url to be shown in the dialog. In this case we point to the custom application page, which we will create in upcoming steps
  • Notice also how to set the delegate for call back. In this case we define that when ever the dialog is closed, CloseCallback function will be called
  • Notice how we check the return value usign SP.UI.DialogResult and act upon the response
  • messageId variable will be used later, so let’s add it now but use it later

The following step is to hook up JavaScript to our link, so let’s add the following to the Visual Web Part canvas as well so that when ever the the link is clicked, our dialog code is invoked.

<br /> <a href="Javascript:OpenDialog();">Start UI and client om demo</a>

Let’s test the functionality by deploying the web part to the portal and clicking the link. We should see following popup opened up.

image

This error is due the fact that we haven’t implemented our dialog page yet. Let’s close the dialog by clicking the close button from the top right corner. You should see the following alert window shown.

image

Modify application page

The following step is to create and modify the application page to use little bit nicer UI. We also need to add some code to provide response value correctly back to the parent window. Let’s add a new application page to our project named ListCreationPage.aspx.

 image

Since we want to use the SharePoint style of rending of the dialog content, let’s add the following registration line on the top of the page, just above the Page statement. We will be using the controls to provide a nice SharePoint like popup “feeling”.

<%@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%
@ Register TagPrefix="wssuc" TagName="InputFormSection" src="/_controltemplates/InputFormSection.ascx" %>

<%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="/_controltemplates/InputFormControl.ascx" %>

<%@ Register TagPrefix="wssuc" TagName="ButtonSection" src="/_controltemplates/ButtonSection.ascx" %>

Following step is to add the required JavaScript to handle the call back functionality. Let’s add following script elements to PlaceHolderAdditionalPageHead content place holder.

<script type="text/javascript">

    function BtnCreateListCancel_Click() {

       SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel, 
                                                                        ‘Cancelled clicked’);

    }

    function BtnCreateListOk_Click() {

        var form = document.forms.<%SPHttpUtility.NoEncode(Form.ClientID,Response.Output);%>;

        var ListNameUrl = form.<%SPHttpUtility.NoEncode(TxtListName.ClientID,Response.Output);%>.value;

        SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, ListNameUrl);

    }

</script>

I’ll point out a few of the elements from the code

  • We create two separate functions – one for the OK click event and one for the Cancel event.
  • Notice how to pass the DialogResult enumerator value based on our response
  • In the OK function, we also take the value from existing textbox on the page, which is passed as a parameter to commonModalDialogClose function

Following step is to create the actual page layout. You’ll need to add following to the place holder called PlaceHolderMain.

<table

    
id="maintable"

    
border="0"

    
cellspacing="0"

    
cellpadding="0"

    
class="ms-propertysheet"

    
width="100%"

    >

      <wssuc:InputFormSection Title="List name" runat="server">

        <
Template_Description>

          <
SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server"

                                      
text="Define list name to be created."EncodeMethod=’HtmlEncode’/>

        </
Template_Description>

        <
Template_InputFormControls>

          <
wssuc:InputFormControl runat="server">

            <
Template_Control>

              <
table border="0" cellspacing="1">

                <
tr>

                  <
td class="ms-authoringcontrols" colspan="2" nowrap="nowrap">

                    <
SharePoint:EncodedLiteral ID="EncodedLiteral7" runat="server"

                  
text="List name will be used as url and as the title"

                  
EncodeMethod=’HtmlEncode’/>:

                    <font size="3">&#160;</font><br />

                  </
td>

                </
tr>

                <
tr>

                  <
td dir="ltr">

                  <
table>

                      <
tr>

                          <
tdnowrap="nowrap"colspan="2"class="ms-authoringcontrols">

                            <
wssawc:InputFormTextBox title="Enter list name" class="ms-input" ID="TxtListName" Columns="35" Runat="server" maxlength="255" size="60" width="100%" />

                          </
td>

                      </
tr>

                  </
table>

                  </
td>

                </
tr>

              </
table>

            </
Template_Control>

          </
wssuc:InputFormControl>

        </
Template_InputFormControls>

      </
wssuc:InputFormSection>

      <
wssuc:ButtonSection runat="server" ShowStandardCancelButton="False">

        <
Template_Buttons>

          <
asp:placeholder ID="Placeholder1" runat="server">

          <
inputclass="ms-ButtonHeightWidth" type="button " name="BtnCancel" id="Button1"

                          
value="OK" onclick="BtnCreateListOk_Click()"  />

            <
SeparatorHtml>

                <
span id="idSpace" class="ms-SpaceBetButtons" />

            </
SeparatorHtml>

          <
input class="ms-ButtonHeightWidth" type="button" name="BtnCancel" id="Button2"

                           
value="Cancel" onclick="BtnCreateListCancel_Click()" />           
          </
asp:PlaceHolder>

        </
Template_Buttons>

      </
wssuc:ButtonSection>

    </
table>

Few pointers again on the code to clarify what we just did

  • We use the InputFormControl and InputFormSection controls to provide a nice look and feel for our popup
  • In this case the buttons are html buttons, since we are not using code behind code to do anything in this case
  • Notice how we call the JavaScript functions we created in the previous step from the button clicks

Now that the dialog page has been implemented, let’s test the functionality. You should get following dialog.

image 

Now if you click OK, you should see following alert shown – this confirms that our dialog callback is working properly.

image

Add notifications

Let’s start using the notifications, to replace the “ugly” alerts, which we are currently using. SharePoint 2010 provides a really nice notification framework, which we can easily utilize. Let’s update the CloseCallback function on the visual web part as follows.

// Dialog callback

function CloseCallback(result, target) {

    if (result == SP.UI.DialogResult.OK) {

        //Get id

       
messageId = SP.UI.Notify.addNotification("<img src=’_layouts/images/loading.gif’> Creating list <b>" + target + "</b>…", true, "Dialog response", null);

        //Create list using client OM

       
//createList(target);

    }

    if (result == SP.UI.DialogResult.cancel) {

        SP.UI.Notify.addNotification("Operation was cancelled…", false, "", null);

    }

}

Again here’s a few things to point out from the code

  • We use the SP.UI.Notify.addNotification function to notify the end user based on the action in the dialog.
  • Notice how we also use the callback value in the message (target parameter) – in this case we are showing the list name to be created
  • We can use html for the notifications – in this case we added a small gif animation in the OK button click
  • Creating list – message is shown using sticky mode (second parameter on the function call) and we store the notification identifier, so that we can show the message until the the list has been actually created
  • Notice the commented CreateList function call – we’ll add this in following step

Now if you try the functionality again, you should get following messages shown on the user interface, depending on the button you clicked.

image  image

Add JavaScript client object model code

Objective was to create new list using Client object model when ever OK is clicked. Since the JavaScript OM is by default available in any SharePoint page, we can just add the following functions.

//Actual JS client side object model calls

function createList(listName) {

    //Create client context.

   
var clientContext = new SP.ClientContext();

    var oWebsite = clientContext.get_web();

    //Let’s create list creation information object

   
var listCreationInfo = new SP.ListCreationInformation();

    listCreationInfo.set_title(listName);

    listCreationInfo.set_templateType(SP.ListTemplateType.announcements);

    listCreationInfo.set_quickLaunchOption(SP.QuickLaunchOptions.on);

    this.oList = oWebsite.get_lists().add(listCreationInfo);

    //Let’s create also new item to the list to be created

   
var itemCreateInfo = new SP.ListItemCreationInformation();

    this.oListItem = oList.addItem(itemCreateInfo);

    oListItem.set_item(‘Title’, ‘Example item for ‘ + listName);

    oListItem.set_item(‘Body’, ‘Hello seminar audience. From list ‘ + listName);

    oListItem.update();

    clientContext.load(oListItem);

    clientContext.load(oList);

    //Execute the actual script

   
clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));

}

//Called if client side OM is successful

function onQuerySucceeded() {

    //Remove the ‘creating’ event notification

   
SP.UI.Notify.removeNotification(messageId);

    //Add ‘created’ notification as non sticky

   
SP.UI.Notify.addNotification("List <b>" + oList.get_title() + "</b> created…", false, "", null);

}

function onQueryFailed(sender, args) {

    //Remove the ‘creating’ event notification

   
SP.UI.Notify.removeNotification(messageId);

    //Shown in case of error on the JS OM call

   
SP.UI.Notify.addNotification("Operation was cancelled…", false, "", null);

}

Remember also to uncomment the createList call from the CloseCallback function

//Create list using client OM

createList(target);

Here’s a few things again from the code

  • Notice how we first get the client context, before we start doing anything
  • The executeQueryAsync method call actually invokes our modifications on server side from the JavaScript – we set two different function callbacks here, depending on the result. During this call, the requested actions are sent to the server side handler for manipulation
  • In both callback functions we remove the previously added sticky notification, before adding new one to indicate the result

If the changes have been applied properly and you now test the dialog functionality, after providing the list name and clicking OK, you should get following message after a while.

image

If you navigate to All Site Settings, you can see that the new list with single example item has been created.

image

Dialog framework and code behind code in dialogs

In the example we just went through, we used only client side code, but it’s quite common that you’ll need to execute also server side OM code as part of the dialog operations. In that case after you have executed any server side code, you can execute the following code to close the dialog and return results back to caller window.

//Close the dialog – we are good to go

this.Page.Response.Clear();

this.Page.Response.Write(string.Format(CultureInfo.InvariantCulture,

     "<script type=\"text/javascript\">window.frameElement.

                    commonModalDialogClose(1, ‘{0}’);</script>"
, ListItems.Count));

this.Page.Response.End();

Notice following from the code

  • Two different parameters are passed to the commonModalDialogClose JavaScript function
  • First one defines the response code – in this case it’s 1, which means success (same as SP.UI.DialogResult.OK). You can also return 0, which would mean cancel
  • Second parameter can be used to pass any relevant information for your application. In this case we return the count of items in the ListItems variable, which we utilized in our server side code.

Summary

As I wrote in the beginning of this blog entry, we can definitely use similar code and approach also from ribbon buttons, which makes extending of the ribbon extremely easy. SharePoint 2010 provides excellent web 2.0 platform for developers. For more information about the new capabilities, check MSDN.

Hopefully this was useful – btw. Due miscellaneous reasons, the full package won’t be unfortunately available at least for a while…

Comments (25)

  1. Pham Trung says:

    Oah,

    Amazing article, I have learned a lot from here

    Thank you so much

  2. Jukka Kotamäki says:

    Hi Vesku!

    Excellent article!

    In order to skip the task to navigate to All site content, just add this one line of code inside of onQuerySucceeded function after the last line ( SP.UI.Notify.addNotification("List…)

    this.window.location.href = oList.get_defaultViewUrl();

    And the client browser will jump to the default view automatically 🙂

    Keep up the good work, Vesku!

    BR

    Jukka

  3. Vesa Juvonen says:

    Hi,

    thanks for the feedback and the improvement suggestion. That really helps to make demo more straight forward.

    .vesku

  4. bmm6o says:

    Good article.  A question about the OK/Cancel buttons though: It looks like you included your own.  I would expect a dialog framework to provide those in a standard way, and just allow you to indicate the type of dialog you want (OK, OK/Cancel, etc).  I’d rather not worry about localizing those strings.

    Also, you include a reference to ButtonSection.ascx.  It doesn’t seem to work for me (I get a compiler warning, and nothing shows up at run time) – any ideas?

  5. bmm6o says:

    I was making a dumb mistake, and got the ButtonSection to work now.  It still gives compiler warnings though, so I’d be curious how to resolve those.

  6. Steven Derveaux says:

    Great article! I was looking for the "close" functionality of the Dialog box.

  7. Dave says:

    The CloseCallback function doesn’t get called when using this to delete a list item.

  8. Jeremy Thake says:

    Great article!

    The ButtonSection doensn't work because of the asp:PlaceHolder tags don't start with correct case.

  9. Elizabeth says:

    Great article.

    One query is ,how can we use localize the  title of dialog window. I know the title is pssed through the dialog options.How can we use of the satellite assemblies in javascript?

  10. Sandeep says:

    I think this code will only work for Root Site Collection

  11. Vesa Juvonen says:

    Hi Sandeep, that's correct. This is reference example and with the current code, it only works in the root site collection due the URL mapping to aspx file is using the following format – "options.url = "/_layouts/ClientOMUIActions/ListCreationPage.aspx" – so we are always linking to root site collection in the dialog opening. You could easily provide dynamically the url based on the context you are.

    Key point is to have the above link to be created dynamically using server SPContext.Site etc.

  12. rs.emenu says:

    Here a good link that provide details about the Model Dialog of Share Point 2010

    http://www.a2zmenu.com/…/SharePoint%202010%20Model%20Dialog.aspx

  13. Anton says:

    Unfortunately, I can't  use your code.

    Because after calling the dialog, I'm getting the mistake – "The server tag is not well formed".

    I have cut the your code (In placeholder – PlaceHolderMain:) for the next view:

    <table

        id="maintable"

        border="0"

        cellspacing="0"

        cellpadding="0"

        class="ms-propertysheet"

        width="100%"

       >

         <wssuc:InputFormSection Title="List name" runat="server">

             <SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server"

                                          text="Define list name to be created."EncodeMethod='HtmlEncode'/>

          </wssuc:InputFormSection>      

    </table>

    I think the truble is in calling  – wssuc:InputFormSection, because VS2010 make a next warning on it – "Element 'InputFormSection' is not a known element. This can occur if there is a compilation error in the Web site, or the web.config file is missing". All registration operations you wrote I've maid. The top of my dialog page look as follows:

    <%@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

    <%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="/_controltemplates/InputFormSection.ascx" %>

    <%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="/_controltemplates/InputFormControl.ascx" %>

    <%@ Register TagPrefix="wssuc" TagName="ButtonSection" src="/_controltemplates/ButtonSection.ascx" %>

    <%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>

    <%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>

    <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

    <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

    <%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

    <%@ Import Namespace="Microsoft.SharePoint" %>

    <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="mydialog.aspx.cs" Inherits="modaldialog2.Layouts.modaldialog2.mydialog" DynamicMasterPageFile="~masterurl/default.master" %>

    I would appreciate if you will show me my mistake.

  14. JuPi says:

    Hi Vesa.

    I have the same issues as Anton.  I am getting the error message "Element InputForm is not a known element.'  and also if I go ahead and deploy the page I get Parser Error – "Parser Error Message: System.Web.UI.WebControls.TableRowCollection must have items of type 'System.Web.UI.WebControls.TableRow'. 'wssuc:InputFormSection' is of type 'ASP._controltemplates_inputformsection_ascx'."

    What step am I missing?  Please help.

    Thanks.

  15. realtodd says:

    I couldn't get this to work as I was getting the same issue. I reworked the <table> as follows:

    <table

        id="maintable"

        border="0"

        cellspacing="0"

        cellpadding="0"

        class="ms-propertysheet"

        width="100%"

       >

         <wssuc:InputFormSection Title="List name" runat="server">

          <Template_Description>

             <SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server"

                                          text="Define list name to be created." EncodeMethod='HtmlEncode'/>

           </Template_Description>

           <Template_InputFormControls>

             <wssuc:InputFormControl runat="server">

               <Template_Control>

                 <table border="0" cellspacing="1">

                   <tr>

                     <td class="ms-authoringcontrols" colspan="2" nowrap="nowrap">

                       <SharePoint:EncodedLiteral ID="EncodedLiteral7" runat="server"

                      text="List name will be used as url and as the title"

                      EncodeMethod='HtmlEncode'/>:

                       <font size="3"> </font><br />

                     </td>

                   </tr>

                   <tr>

                     <td dir="ltr">

                     <table>

                         <tr>

                             <td nowrap="nowrap" colspan="2" class="ms-authoringcontrols">

                               <SharePoint:InputFormTextBox title="Enter list name" class="ms-input" name="txtListName" ID="TxtListName" Columns="35" Runat="server" maxlength="255" size="60" width="100%" />

                             </td>

                         </tr>

                     </table>

                     </td>

                   </tr>

                 </table>

               </Template_Control>

             </wssuc:InputFormControl>

           </Template_InputFormControls>

         </wssuc:InputFormSection>

          <wssuc:ButtonSection runat="server" ShowStandardCancelButton="False">

           <Template_Buttons>

             <asp:placeholder ID="Placeholder2" runat="server">

             <input class="ms-ButtonHeightWidth" type="button" name="BtnOk" id="Button1"

                               value="OK" onclick="BtnCreateListOk_Click()" />            

             </asp:PlaceHolder>

              <SeparatorHtml>

                   <span id="idSpace" class="ms-SpaceBetButtons" />

               </SeparatorHtml>

             <asp:placeholder ID="Placeholder1" runat="server">

             <input class="ms-ButtonHeightWidth" type="button" name="BtnCancel" id="Button2"

                               value="Cancel" onclick="BtnCreateListCancel_Click()" />            

             </asp:PlaceHolder>

           </Template_Buttons>

         </wssuc:ButtonSection>

         </table>

  16. Sekhar says:

    Nice Article

  17. tatya says:

    Hi,

    Is this possible to resize the model at runtime. i.e. attached a file after that a  modal of 800*800 is this possible????

  18. Phil says:

    Hi,

    Good article… just one issue I had and solved:

    The line

    var options = SP.UI.$create_DialogOptions();

    threw an error some times when calling it right after page load ("SP.UI.$create_DialogOptions() is not a function")

    After some debugging I saw that this function just returned an empty object, so it was easy to solve the problem by replacing the line above with

    var options = {};

    HTH

  19. Arbind says:

    Great Stuff!!!

    I was struggling to close the dialog after executing server side code.

    Thanks.

  20. Help says:

    How can i make this from af c# codebehind file ??

  21. Vesa Juvonen says:

    Hi "Help",

    Popup works onlly in JavaScript mode, if your request is to open up the popup immediately when someone arrives on the page… this means that you should be just writing proper JavaScript entries to html and make sure that it's automatically called during page load.

    There's no way directly starting client side script (JavaScript) from code, which is running in server (code behind), but this can be achived by rendering required JavaScript to html for telling client side browser to open up the popup immediately when page is loaded.

  22. avinash says:

    Nice post. Informations are helpful.

    I have around 50 popups in my application.

    To make it more generic; i have created custom framework for popups. This framework can load any .net user control & handle ok, cancel button event to call sharepoint list or sql DB.

    I have used this blog basics to create my custom framework sharepoint.infoyen.com/…/sharepoint-2010-popup-dialogs.

    Thank you very for your blog.

    Avinash