Second Life of a Hungarian SharePoint Geek

July 26, 2011

How to add a button to the ribbon to update a field for selected list items using the client object model

I received a comment for one of my former posts asking how it is possible to add a custom button to the ribbon that allows users to set field values for selected list items. In this post I show you a solution for this request.

You can download the complete solution from here.

The solution contains a content type with a numerical field called Numeric. A test list schema is created and the content type is assigned to it. There is also a list instance for the test list with a few items.

The following custom action definition assigns the new ribbon button to our content type. Important to note that the hexadecimal digits in the ID of the content type must be written with all capital letters in the RegistrationId attribute.

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3.   <CustomAction
  4.     Id="Ribbon.Library.Actions.ResetItems"
  5.     Location="CommandUI.Ribbon"
  6.     RegistrationType="ContentType"
  7.     RegistrationId="0x0100E36DB888927049468D7CBAF1C7B70582"
  8.     Title="Reset Selected Items Action">
  9.     <CommandUIExtension>
  10.       <CommandUIDefinitions>
  11.         <CommandUIDefinition
  12.           Location="Ribbon.ListItem.Manage.Controls._children">
  13.           <Button Id="Ribbon.ListItem.Manage.SetItemData"
  14.             Command="SetItemDataButtonCommand"
  15.             Image16by16="/_layouts/images/JCOMUpdateSelectedItems/exclamation.png"
  16.             LabelText="Reset Selected Items"
  17.             TemplateAlias="o2" />
  18.         </CommandUIDefinition>
  19.       </CommandUIDefinitions>
  20.       <CommandUIHandlers>
  21.         <CommandUIHandler
  22.           Command="SetItemDataButtonCommand"
  23.           CommandAction="javascript:updateSelectedItems();"
  24.           EnabledScript="javascript:isNonFolderSelected();" />
  25.       </CommandUIHandlers>
  26.     </CommandUIExtension>
  27.   </CustomAction>
  28. </Elements>

As you can see, the SetItemDataButtonCommand command has a CommandAction that is bound to the updateSelectedItems JavaScript method, and the EnabledScript attribute is set to the  isNonFolderSelected method.

This latter one ensures that the ribbon button will be disabled until at least one non-folder item is selected in the list. It can be achieved by iterating through the selected items and checking their fsObjType property, as illustrated by the following code:

  1. function isNonFolderSelected() {
  2.     var selectedItems = SP.ListOperation.Selection.getSelectedItems();
  3.     var result = false;
  4.  
  5.     for (var i = 0; i < selectedItems.length; i++) {
  6.         // it is a list item, not a folder
  7.         if (selectedItems[i].fsObjType == 0) {
  8.             result = true;
  9.             break;
  10.         }
  11.     }
  12.  
  13.     return result;
  14. }

If the user clicks on the button, the updateSelectedItems method is invoked. This method gets a reference for the current web and list objects, takes the selected items, and for each non-folder item sets the Numeric field to zero. Finally it calls executeQueryAsync to submit the changes.

  1. var inProgress = false;
  2. var selectedListId;
  3.  
  4. function updateSelectedItems() {
  5.     if (inProgress) {
  6.         alert("Another request is in progress. Try again later!");
  7.     }
  8.     else {
  9.         try {
  10.             var context = SP.ClientContext.get_current();
  11.             var web = context.get_web();
  12.             var selectedItems = SP.ListOperation.Selection.getSelectedItems();
  13.             this.selectedListId = SP.ListOperation.Selection.getSelectedList();
  14.             for (var i = 0; i < selectedItems.length; i++) {
  15.                 // it is a list item, not a folder
  16.                 if (selectedItems[i].fsObjType == 0) {
  17.                     var listItem = web.get_lists().getById(selectedListId).getItemById(selectedItems[i].id);
  18.                     context.load(listItem);
  19.                     listItem.set_item("Numeric", 0);
  20.                     listItem.update();
  21.  
  22.                 }
  23.             }
  24.  
  25.             context.executeQueryAsync(Function.createDelegate(this, itemsUpdated), Function.createDelegate(this, updateFailed));
  26.         }
  27.         catch (e) {
  28.             alert("Error: " + e);
  29.             inProgress = false;
  30.         }
  31.     }
  32. }
  33.  
  34. function updateFailed(sender, args) {
  35.     alert("Operation failed: " + args.get_message());
  36.     inProgress = false;
  37. }
  38.  
  39. function itemsUpdated() {
  40.     inProgress = false;
  41.     var convertedListId = selectedListId.toLowerCase().replace("-", "_").replace("{", "").replace("}", "");
  42.     var controlId = 'ctl00$m$g_' + convertedListId + '$ctl02';
  43.     __doPostBack(controlId, 'cancel');
  44.     //window.location.href = window.location.href;
  45. }

On successful execution of the request the itemsUpdated method is called. It updates the UI asynchronously as described in this post.

To enable asynchronous updates, theoretically you would have to ‘Enable Asynchronous Update’ at AJAX Options of the list web part, although based on my last tests the method works even if the asynchronous update is not allowed.

image

If you don’t need the asynchronously update, you can simply use this line to reload the whole page after Numeric values are set for selected items.

window.location.href = window.location.href;

image

Advertisements

3 Comments »

  1. hi excellent post! how about reading items column data one at a time and doing some processing on the items. I’m having a hard time reading the item data (from all of the selected items) one at a time, there are lots of examples of only reading only one item but i haven’t seen a solution that works for all selected items..

    Comment by kevin dube — August 8, 2012 @ 07:02

  2. i was trying to adapt your code but i can’t get it to work…

    function updateSelectedItems() {
    if (inProgress) {
    alert(“Another request is in progress. Try again later!”);
    }
    else {
    try {
    var context = SP.ClientContext.get_current();
    var web = context.get_web();
    var selectedItems = SP.ListOperation.Selection.getSelectedItems();
    this.selectedListId = SP.ListOperation.Selection.getSelectedList();
    for (var i = 0; i < selectedItems.length; i++) {
    // it is a list item, not a folder
    if (selectedItems[i].fsObjType == 0) {
    this.listItem = web.get_lists().getById(selectedListId).getItemById(selectedItems[i].id);
    context.load(this.listItem);
    context.executeQueryAsync(function () {
    var url = this.listItem.get_item("Title");
    alert(url);
    }, function () { alert("error"); });

    }
    }

    }
    catch (e) {
    alert("Error: " + e);
    inProgress = false;
    }
    }
    }

    Comment by kevin dube — August 8, 2012 @ 07:05

  3. here is a cleaner version of the code which doesn’t throw any syntax errors but always gives me the first item in the list and not subsequent items

    function processSelectedListItems() {
    var context = SP.ClientContext.get_current();
    var web = context.get_web();
    var oLists = web.get_lists();
    var currentListID = SP.ListOperation.Selection.getSelectedList();
    var oCurrentList = oLists.getById(currentListID);
    var selectedItems = SP.ListOperation.Selection.getSelectedItems();
    for (var i = 0; i < selectedItems.length; i++) {
    oTargetListItem = oCurrentList.getItemById(selectedItems[i].id);
    context.load(oTargetListItem, 'url');
    context.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
    }

    }

    function onQuerySucceeded() {

    alert('Request succeeded. \n\nRetrieved Item is: ' + oTargetListItem.get_item('url'));
    //do processing in here on each list item

    }

    function onQueryFailed(sender, args) {
    alert('Request failed. \nError: ' + args.get_message() + '\nStackTrace: ' + args.get_stackTrace());
    }

    Comment by kevin dube — August 8, 2012 @ 19:13


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: