Dynamic list form with ECMAScript Client Object Model

Client Object model is one of my favorite features in SharePoint 2010. It allows us to build rich features or interactive UI fairly quickly using Javascript and SharePoint Designer. In this post I will show how to use ECMAScript Client OM that performs custom operation in a custom Display Form. I will extend the custom form of  the Announcement List I created in my previous post to include additional operation.  In the list I added 2 more fields as shown below:

  1. Type: Yes/No, DisplayName: Read Flag, Internal Name: ReadFlag
  2. Type: User field (multi value), Display Name: Have Read, Internal Name: HaveRead

Then I added a button called ‘I have read’. The button will show whenever the ReadFlag field equal to ‘Yes’ and HaveRead doesn’t contain current user. And whenever a user click the button, I will add the current user into the HaveRead userfield collection and update the list item. There wouldn’t any managed code involve to implement this. Ok let’s get started with the detail implementation. I will reuse the same data form webpart and the its xlst from my previous post.

  1. On the dataform webpart I will add a new parameter binding to get the current user as shown below.
       <ParameterBinding Name="UserID" Location="CAMLVariable" DefaultValue="CurrentUserName"/>
    
  2. To use this variable in our xsl, we need to add UserID in the global parameter as shown below at line 3.
       <xsl:param name='ManualRefresh'></xsl:param>
      <xsl:param name='dvt_firstrow'>1</xsl:param>
      <xsl:param name="UserID"/>
    
  3. As I mentioned before that the button will be shown when ReadFlag = true and HaveRead doesn’t contain current user. The code below shows how to implement this.
    .
    <xsl:variable name="currentUserHasRead">
         <xsl:value-of select="contains(@HaveRead,$UserID)"/>
    </xsl:variable>
    ----------------------
    <xsl:if test="(@ReadFlag = '1')  and $currentUserHasRead = 'false'">
       <button class="ms-ButtonHeightWidth" id="IHaveRead" onclick="SetHaveRead({@ID});return false">
          I have read
      </button>
    </xsl:if>
    

    The first part of the xsl is creating a boolean variable which equals to true if current user is already added into HaveRead userfield collection. The 2nd part of the xsl is adding a html button inside an xsl if clause. The onclick button is calling a javascript function SetHaveRead and passing the itemId into the function. The ‘return false’ is added to avoid the button to perform postback

  4. Below is the client script that adding the current user to the HaveRead user field collection.
    var item;
    var ctx;
    var user;
    var notifyId;
    var $Id;
    
    function SetHaveRead(itemId) {
        notifyId = SP.UI.Notify.addNotification("Executing", true);
        $Id = itemId;
        ctx = SP.ClientContext.get_current();
        var announList = ctx.get_web().get_lists().getByTitle("Announcements");
        item = announList.getItemById(itemId);
        user = ctx.get_web().get_currentUser();
        ctx.load(item);
        ctx.load(user);
        ctx.executeQueryAsync(Function.createDelegate(this, this.doUpdate),
       Function.createDelegate(this, this.OnFailed));
    }
    
    function doUpdate(sender, args) {
        var users = item.get_item('HaveRead');
        if (!users)
            users = new Array();
    
        users.push(SP.FieldUserValue.fromUser(user.get_loginName()));
    
        ctx = SP.ClientContext.get_current();
        var announList = ctx.get_web().get_lists().getByTitle("Announcements");
        item = announList.getItemById($Id);
        item.set_item('HaveRead', users);
        item.update();
    
        ctx.load(item);
        ctx.executeQueryAsync(Function.createDelegate(this, this.UpdateSuccess),
        Function.createDelegate(this, this.OnFailed));
    }
    
    function UpdateSuccess(sender, args) {
        $("#IHaveRead").css("display", "none");
        SP.UI.Notify.removeNotification(notifyId);
    }
    
    function OnFailed(sender, args) {
        alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
        SP.UI.Notify.removeNotification(notifyId);
    }
    

    This is a brief explanation about the code. The first method is the one called by the button, in this one it grabs current item & current user using Client API. In doUpdate method, it retrieves the HaveRead field value, if it is empty, initialize an array finally using SP.FieldUserValue.fromUser to get retrieve the UserFieldValue from the current user and push it into the array, then performs item update operation. When updateSuccess, the button is hidden using jquery. During the operation the (Executing) notification will be shown as shown in 2nd pic on this post. This msdn article contains javascript Client OM API.

  5. Finally we need to add the javascript reference to the page.

In conclusion DataForm webpart & Javascript Client OM is a powerfull combination to perform sharepoint customization fairly quickly.

You can find the xsl here.

SharePoint 2010 – Add custom list form to existing list

In this post I will show you how to add a custom list form to existing list using SharePoint 2010 object model. Before we get into how, I would like to put some backgrounds about why I would do this:

  • I want to have a nicer display form (not in a form format) for an existing list such as anouncement list.
  • I also want to add a web part to the custom display form such as social comment web part.
  • And I want to be able to activate this display form as part of a feature.

This is my custom announcement display form looks like. As we can see that the list form web part has been replaced with a dataform webpart with custom rendering and also I added a social comment web part after the dataform web part. This can be done easily using SharePoint Designer. When creating a custom form, SharePoint Designer would add a DataForm web part instead of a ListForm web part to the aspx file. Interestingly, if we delete the dataform webpart, the aspx file would loose its function as a form. So we should modify the xlst of the dataform web part instead of replacing it.

To achieve the same result using codebehind, we need to do this followings:

1. Provision an aspx file and set it as a form page. As shown in the code below. In SharePoint 2007 we can’t achieve this as SPTemplateFileType.FormPage is new in SharePoint 2010.

  //the list's forms are located in the list's rootfolder
  SPFolder root = list.RootFolder;
  string customFormUrl = webUrl.LocalPath + "/" + root.Url + "/" + formName;
  //provision a new list form
  SPFile file = root.Files.Add(customFormUrl, SPTemplateFileType.FormPage);

2.Provision a webpart that implements IListWebPart such as ListFormWebPart, DataFormWebPart, DataViewWebPart, etc. Then we need to set the two properties of the web part: ListId, the List GUID, and PageType, this will determine what type of the form either display, new or edit form. My observation shows that without an IListWebPart and settings those 2 properties the aspx page wouldn’t be provisioned as a form. This below code excerpt shows how to provision the webpart and set those 2 properties (line 18, 19)

 //Provision IList Web Part in this case DataForm web part onto the new form
 SPLimitedWebPartManager limitedWebPartManager = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
IListWebPart part = null;

//set specific dataform webpart properties
//for query (Parameter bindings & DataSources),
DataFormWebPart webPart = new DataFormWebPart();
webPart.ParameterBindings = ;
SPDataSource ds = new SPDataSource(); //the detail spdatasource initialization not shown
webPart.DataSources.Add(ds);

//for rendering XslLink
webPart.XslLink = webUrl.LocalPath + xslUrl;

//set the IListWebPart Propertes required for the form
part = webPart;
string str2 = list.ID.ToString("B").ToUpper(CultureInfo.InvariantCulture);
part.ListId = list.ID;
part.PageType = PAGETYPE.PAGE_DISPLAYFORM;

3. In the XSL don’t forget to add FormToolbar control as this would initialize the form’s ribbon, without it the form’s ribbon wouldn’t be displayed. The easiest way to get the right xsl is modifying the one created by SharePoint Designer when we provision a custom form via SharePoint Designer.

4. Lastly, set the list Default form. SPList has these properties: DefaultDisplayFormUrl, DefaultEditFormUrl and DefaultNewFormUrl. In my case I would set the DefaultDisplayFormUrl.

  list.DefaultDisplayFormUrl = customFormUrl;

With this approach we can provision custom forms on any list using code behind. This source code contains an example of an implementation of the approach by provisioning a custom display form for announcement list through a List Event receiver.

Related Post:
Dynamic list form with ECMAScript Client Object Model