Wednesday, July 7, 2010

SharePoint 2010: Use ECMAScript to manipulate (Add/Delete/Update/Get) List Items

In SharePoint 2010 there are three different types of Client Object Model extension you can use. They are Managed Client Object Model, ECMAScript and silverlight extension. In one of my post I have described how to manipulate list items using Managed Client Object Model. Today I’ll go through how to use ECMAScrip to manipulate list items. Fist of all make sure your page/webpart is ready to use ECMAScript as I have described in another post.

How to Get ECMAScript Intellisence

When you’ll use ECMAScript library in Visual Studio, it’s possible to get intellisense for ECMAScript. There are three ways you may need to enable the intellisense:

  1. Get Intellisense in Application Page: In the case you want to put  your javascript in aspx file as inline, you need to add the following lines in the markup file. However, this will not work for webpart file. For webpart file you need to put your javascript in another js file as described in option 2.

    <script type="text/ecmascript" src="/_layouts/SP.debug.js" />
    <
    script type="text/ecmascript" src="/_layouts/SP.Debug.js" />
    <
    script type="text/ecmascript" src="/_layouts/SP.Runtime.Debug.js" />

    <
    script type="text/javascript">
          //you'll get intellisense here
    </script>
  2. Get Intellisense in js file: If you want to get intellisense in js file then you need to add the following lines in the top of the js file. As shown in the snippet below, the first reference is to MicrosoftAjax.js file. This is mandatory to have this js file reference at the top. Then I have added two other references. The two files (SP.Core.Debug.js and SP.debug.js) have basic SharePoint namespaces. However, if you need more functionalities try to add more js file reference from the path “C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS”
    /// <reference name="MicrosoftAjax.js" />
    /// <reference path="file://C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS/SP.core.debug.js" />
    /// <reference path="file://C:/Program Files/Common Files/Microsoft Shared/Web Server Extensions/14/TEMPLATE/LAYOUTS/SP.debug.js" />
    
  3. Get Intellisense in webpart: To get intellisense in webpart you need to add the following two lines in the webpart ascx file:

    <script type="text/javascript" src="/_layouts/MicrosoftAjax.js" ></script>
    <script type="text/javascript" src="/_layouts/SP.debug.js" /> 

    However, I have found the MicrosoftAjax.js file is not located in “/_layouts/MicrosoftAjax.js” and for that the above intellisense will not work. So to get intellisense you need to copy the file in the Layouts folder. You can try to find the MicrosoftAjax.js file from location “C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ProjectTemplatesCache\VisualBasic\Web\1033\EmptyMvcWebApplicationProjectTemplatev2.0.vb.zip\Scripts” if exists. Or you can download the file from the location below where I have uploaded the file: http://cid-04d8f6d0dd4e7214.office.live.com/self.aspx/Public/MicrosoftAjax.js 

A Sample List I have used

For this blog I have used a sample product list with the properties as shown in the following image.

image

Figure 1: Product List

 

Add a new List Item

The code snippet below shows that  you need to get the current SharePoint Context first. Then get the current web from the context. And then you need to get the list from the web. Then I have used ListItemCreationInformation object. There are two important properties of ListItemCreationInforamtion:

  • ListItemCreationInformation.folderUrl: this property defines in which location you want to add the item. The url should start with forward slash(/). For example for the web myweb and list product and for the folder ‘myfolder’ in the product list the url will be  ‘/myweb/Lists/product/myfolder’.
  • ListItemCreationInformation.UnderlyingObjectType: this value identity the type of object to create. The possible values are File,Folder, Web and Invalide. The values can be found in ‘SP.FileSystemObjectType’.

The addProduct method below takes few arguments that represents the product list’s fields.

function addProduct(productName, productDesc, productLaunchDate, productAvailQty, productType) {
    try {
        var context = new SP.ClientContext.get_current();        
        var web = context.get_web();
        var list = web.get_lists().getByTitle('product');

        var listItemCreationInfo = new SP.ListItemCreationInformation();
        var newItem = list.addItem(listItemCreationInfo);
        newItem.set_item('Title', productName);
        newItem.set_item('ProductName', productName);
        newItem.set_item('ProductDescription', productDesc);
        newItem.set_item('LaunchDate', productLaunchDate);
        newItem.set_item('AvailableQuantity', productAvailQty);
        newItem.set_item('ProductType', productType);

        newItem.update();
        context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));
    }
    catch (e) {
        alert('error:' + e.Message);
    }
}

If you look a bit closer in the above code snippet you can find that ClientContext.executeQueryAsync takes two function delegates. The first one will be invoked when the ECMAScript get executed successfully. The second one will be invoked otherwise. The two methods are defined below:

function success() {
    alert('success');
}
function failed(sender, args) {
    alert('failed. Message:' + args.get_message());
}

 

Delete a List Item

To delete a product by product id the following code snippet can be used:

function deleteProduct(productId) {
    var context = new SP.ClientContext.get_current();
    var web = context.get_web();
    var list = web.get_lists().getByTitle('product');
    var itemToDelete = list.getItemById(productId);
    itemToDelete.deleteObject();
    context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));
}

To delete an object in Client Object Model you need to invoke the deleteObject method of that object.

 

Get Item By Id

To get an item using ECMAScript, you need to share a common variable between the method that execute the ECMAScript (getProductById method in the following code snippet) and callback method (productReceived, failed in the snippet below). Only for this reason I have defined a variable product in the first line of the code snippet below.

var product;
function getProductById(productId) {
    try {
        var context = new SP.ClientContext.get_current();
        var web = context.get_web();
        var list = web.get_lists().getByTitle('product');
        this.product = list.getItemById(productId);
        context.load(product, 'ProductName', 'ProductDescription', 'ProductType', 'LaunchDate', 'AvailableQuantity');
        context.executeQueryAsync(Function.createDelegate(this, this.productReceived), Function.createDelegate(this, this.failed));
    }
    catch (e) {
        alert(e);
    }
}
function productReceived() {
    alert('got product');
    gotProduct(this.product);
}
function failed(sender, args) {
    alert('failed. Message:' + args.get_message());
}

In the code snippet above, the Context.Load method has taken the item to load (product) as the first parameter. And a comma separated list of columns to load for this item are passed then to the load method. If you want to load all properties of the item (which is not recommended) you can just call the context.load method with only first parameter.

 

Search Items from a List

In the code snippet below Caml Query is used for searching a product by title. I have used Caml Query to search product by title. Notice here that the load takes a second parameter (wrapped with ‘include’) specifying all properties to load for items.

var productcollection;
function getProducts(title) {
    try {
        var context = new SP.ClientContext.get_current();
        var web = context.get_web();
        var list = web.get_lists().getByTitle('product');
        var query = '<View Scope=\'RecursiveAll\'>'+
                        '<Query>'+
                            '<Where>'+
                            '<Contains>'+
                                '<FieldRef Name=\'ProductName\'/>' +
                                '<Value Type=\'Text\'>' + title +'</Value>'+
                            '</Contains>'+
                            '</Where>'+
                        '</Query>'+
                             '</View>';
        var camlQuery = new SP.CamlQuery();
        camlQuery.set_viewXml(query);

        this.productcollection = list.getItems(camlQuery);
        context.load(this.productcollection, 'Include(ProductName, ProductDescription, ProductType, LaunchDate, AvailableQuantity)');
        context.executeQueryAsync(Function.createDelegate(this, this.productsReceived), Function.createDelegate(this, this.failed));
    }
    catch (e) {
        alert(e);
    }
}
function productsReceived() {
    alert('got products');
    prcessProducts(this.productcollection);
}
function failed(sender, args) {
    alert('failed. Message:' + args.get_message());
}

 

Update a list item

The code snippet below shows how to update a product item. The list item’s set_item(propertyname, propertyvalue) method is used to update the field values.

function updateProduct(productid, productName, productDesc, productLaunchDate, productAvailQty, productType) {
    var context = new SP.ClientContext.get_current();
    var web = context.get_web();
    var list = web.get_lists().getByTitle('product');
    var product = list.getItemById(productid);
    product.set_item('ProductName', productName);
    product.set_item('ProductDescription', productDesc);
    product.set_item('ProductType', productType);
    product.set_item('LaunchDate', productLaunchDate);
    product.set_item('AvailableQuantity', productAvailQty);
    product.update();
    context.executeQueryAsync(Function.createDelegate(this, this.success), Function.createDelegate(this, this.failed));

}

 

Points to Remember

1. FormDigest Control: If you write code that modifies data on server, include a FormDigest control to create a digest for security validation of the page. The formdigest control can be added in master page and then all content pages will get it automatically.

<SharePoint:FormDigest runat="server" />

2. Get and Set Property: For the scrip used, when you need to get a property, you need to add get_ as prefix. For example the web has the property title. So if you want to get the title property of web then you need to use the syntax web.get_title(). Similary, if you want to set the title value then you need to invoke web.set_title(‘title’).

2. ClietContext.Load: ClientContext’s load method is used to load object. The first parameter to the load method is the object to load. The second parameter vary depending on whether the first parameter is a single object or collection.

  • If the first parameter is a single object then the following syntax is used to load properties:

context.load(objectToLoad,’property1’,’property2’,………….,’propertyN’)

  • If the first parameter is a collection then the following syntax is used

context.load(objectCollectionToLoad,’Include(property1, property2,……….,propertyN)’)

30 comments:

  1. Nice post Sohel.

    Question : In every example you are passing the 'productid'.. how do you get the id of the item?

    I know you can get the checkbox selected items through 'SP.ListOperation.Selection.getSelectedItems();'

    But what if I am looking at the properties of the ListItem and in there I want the current item.. how can I get this ?

    Thanks
    mano

    ReplyDelete
  2. The product id is actually the listItem.ID which is list item's id. For second question (though I'm not clear about the question), I think you need to loop through the items and for each item you need to invoke item.get_item('propertyname'). However, if you get property not initialized exception then you need to load the property.

    ReplyDelete
  3. Hi, in the example you are using Simple fields, have you tried to save a MultiLookup field using ECMAScript? do you know how to do that?

    ReplyDelete
  4. @Pancho, Hope this link will help: http://social.technet.microsoft.com/Forums/en-US/sharepoint2010programming/thread/3b5a2d14-4c44-49e2-80d1-67ced9bb2a47/

    ReplyDelete
  5. May i get the source code or project folder?

    ReplyDelete
  6. Great post! That was very useful for me, thanks.

    Marcel

    ReplyDelete
  7. Thanks for the post. Great help!

    Sebastian

    ReplyDelete
  8. Sohel Thanks for the post.

    Question: How to get current list item id.

    ReplyDelete
  9. @shyam, Please follow the link: http://social.technet.microsoft.com/Forums/en-US/sharepoint2010programming/thread/80e08b24-9971-417f-8e9a-4330b12e11ff/

    ReplyDelete
  10. Nice Blog! I have a question that I have been looking everywhere for even a hint-- Could you give a example of How to use ecmascript to allow me to manipulate Taxonomy data? What I would like to do is load a list of the Stores from the Taxonomy session- populate that into a dropdown where I can pick one then have it populate a group list and term set list and so on... I can do all of this without ecmascript..But I am just lost on how to do it with ecmascript..I'm a newbie at this and I understand ecmascript with lists..Just kinda lost at how to incorporate the other API's into ecmascript..

    ReplyDelete
  11. Hi James, SharePoint 2010 Client Object Model doesn't support taxonomy manipulation till now. Since EcmaScript is one of the three types of Client Object Model, it also lacks the support for taxonomy manipulation. However there's a web service 'TaxonomyClientService.asmx' which you can use using JavaScript. There's a codeplex project for that: http://sptaxonomyclient.codeplex.com/

    ReplyDelete
  12. any way to call synchronously for get a list item?

    ReplyDelete
  13. @Gulja, no it's not possible out of the box. But u can do it with a bit customization. You can show a 'progress bar/waiting image' while waiting for completed event. on success/error, you can hide the progress bar/waiting image.

    ReplyDelete
  14. Sohel, Could you please provide more detail about how to customize using a progress bar/waiting image while waiting for a completed event? An example of how to accomplish this would help immensely. Thanks!

    ReplyDelete
  15. I should elaborate a bit more... I'm updating list items using ecmascript, but sometimes a user can access the item from the page before the item update is complete in the backend. Is there any way using ecmascript to check if the item is being edited/updated and not allowing a user to access it until the edit/update is complete? This issue is causing many "Save Conflicts" for our users. Thanks again.

    ReplyDelete
  16. Drew, the approach is common to asp.net. keep a div in the page that will show the progress. By default the div will be hidden (display:none). After executing the method 'context.executeQueryAsync();', show the div. In the success or failed method, hide the div. That's all.

    ReplyDelete
  17. Hi,
    even though I use the same code u have given, success method is not getting called on first time. WHEN WE PERFORM SAY BUTTON CLICK FOR 2ND TIME ONL SUCCESS IS GETTING CALLED. any help pls?

    ReplyDelete
  18. Hi Sohel,
    This is a great article. Since I am new in SharePoint can you tell me any way of sending email using client side script like Jquery and web service because I am not allowed to write any server side(C#) code and any kind of deployment on server. I can only use content editor webpart and scripts.

    ReplyDelete
  19. Nice Post.
    Devendra
    http://devendra-sharepoint.blogspot.in/

    ReplyDelete
  20. More Informative post. Thanks sohel.
    Venkat Ramavath
    WWW.SharePoint2010Training.blogspot.in

    ReplyDelete
  21. In every example, you are using the current context. How would you do this if your list existed in a different site collection?

    ReplyDelete
  22. I'm a little confused. If the UpdateItem is performed before the item is added, how is the ID available? So If in the new item form, you place a CEWP that contains the UpdateItem method above, since the item does not exist, how can the javascript have the id?

    ReplyDelete
  23. @Clem, I'm not quite sure about your need, but what I understand u need to call updateItem in new form page. I think, you will not get the ID unless the item in created. If you have any logic where you need to do something after the item is created, you should move it to Event Receiver or some other post-events.

    ReplyDelete
  24. Thanks for your post. I also need to remove listitems by title, could you help me?
    Thanks in advance.

    ReplyDelete
  25. Thanks for the post.

    I am trying to add a new item in list. Code is working fine in IE but i am getting error "Unexpected response from server. The status code of response is '0'. The status text of response is "" " in Firefox and Chrome.Please suggest.

    ReplyDelete
  26. Hi Sohel I want to send mail using ecma Script Any idea please help .

    ReplyDelete
  27. Thanks for your post! Please tell me, should i use "context.load(listItem)" after listItem.update() ?

    ReplyDelete

Note: Only a member of this blog may post a comment.