Tag Archives: jQuery

jQuery UI Autocomplete Widget Extension

jQuery UIMany of my personal and professional projects use the jQuery UI Autocomplete widget. Customers love being able to type a search term and have their choices limited to only those items in which they’re interested. The base widget, however, doesn’t provide all the functionality I need and ad hoc customizations have become tedious. So, I’ve decided to write an extension using the Widget Factory that brings together the features I use most often. My AutocompletePro widget is definitely a work in progress, but you can grab the latest version from GitHub.


Live Demo!

This is a jQuery UI widget and may not be suitable for some mobile devices. A jQuery mobile version will soon be posted to my development site!

Delete the current input text and start typing the title of one of your favorite jazz albums, perhaps “Jazz Samba”. You can also type in an artist’s name like Miles Davis or the year 1958. Then make a selection from the available recordings.


AutocompletePro adds several new options to the base Autocomplete:

  • autodrop: defines if the menu should open on focus
  • categoryfield: what field to use as the category
  • categorize: defines if the menu should be categorized
  • defaultvalue: sets the initial value
  • datasource: replaces the base source option
  • filter: sets a filter on the data source records
  • filtereddatasource: read only – the datasource that is being used by the widget
  • itemsshown: limits the number of hits returned
  • searchfields: array of fields names to be searched

And two new methods:

  • value: returns the selected item object
  • selectitem: selects an item given a set of elements

Demo Code GitHub

The datasource for this demo is structured as follows:

 
var data.items = [{ "category": "Traditional", "artist": "Benny Goodman", "label": "Live at Carnegie Hall 1938 [Live]", "year": "1950" },
  ...
{ "category": "Bebop", "artist": "Charlie Parker", "label": "Best of the Complete Savoy and Dial Studio Recordings [Compilation]", "year": "2002" }]

And the widget is created with these options:

$("#widgetbox").autocompletepro({
        autoFocus: true,
        itemsshown: 10,
        datasource: data.items,
        autodrop: true,
        searchfields: ["label", "artist", "year"],
        categorize: true,
        defaultvalue: [{ "artist": "Miles Davis", "label": "Kind of Blue" }],
        categoryfield: "category",
        filter: [{ "category": "Traditional" }, { "category": "Latin" }, { "category": "Cool" }, { "category": "Fusion" }, { "category": "Bebop" }]
});

WCF- Changing Data Contract Names and Namespaces

When posting XML data to a Windows Communication Foundation (WCF) web service1, you need to specify the XML namespace of the data sent in the entity-body of your HTTP POST. This lets WCF know how to deserialize the input you’ve sent. By default, WCF maps the namespace to:

http://schemas.datacontract.org/2004/07/Clr.Namespace

where Clr.Namespace is the namespace of your data contract.2 For example, if my data contract namespace is “RESTful”:

namespace RESTful
{
    [DataContract]
    public class Person
    {
        [DataMember]
        public int ArtistID { get; set; }
        //...

then the namespace to use when consuming this service is:

http://schemas.datacontract.org/2004/07/RESTful

Here’s a code snippet that uses jQuery and Ajax to post a Person to the WCF web service:

        $.ajax({
            type: 'POST',
            data: '<Person xmlns="http://schemas.datacontract.org/2004/07/RESTful">'
                  + '<ArtistID>0</ArtistID>'
                  + '<FirstName>Ella</FirstName>'
                  + '<Grammys>14</Grammys>'
                  + '<LastName>Fitzgerald</LastName>'
                  +'</Person>',
            dataType: 'xml',
            // ...

That’s not a descriptive namespace. Let’s change it to something a bit more informative by specifying a ContractNamespace for our assembly.

[assembly: ContractNamespace("http://schemas.dlwelch.com/JazzService", ClrNamespace = "RESTful")]
namespace RESTful
{
    [DataContract]
    public class Person
    {
        [DataMember]
        public int ArtistID { get; set; }
        //...

Putting this at the assembly level changes the namespace for all data contracts in the RESTful namespace. If we didn’t want to change them all, we could have changed a single contract like this:

namespace RESTful
{
    [DataContract (Namespace="http://schemas.dlwelch.com/JazzService")]
    public class Person
    {
        [DataMember]
        public int ArtistID { get; set; }
        //...

I’m going to leave all of the RESTful data contracts in the same namespace (line 1). And while I’m under the hood, I’m going to change the contract name to something more meaningful (line 4):

[assembly: ContractNamespace("http://schemas.dlwelch.com/JazzService", ClrNamespace = "RESTful")]
namespace RESTful
{
    [DataContract(Name = "Artist")]
    public class Person
    {
        [DataMember]
        public int ArtistID { get; set; }
        //..

Now, when we POST to the service, the request details are a lot nicer:

        $.ajax({
            type: 'POST',
            data: '<Artist xmlns="http://schemas.dlwelch.com/JazzService">'
                  + '<ArtistID>0</ArtistID>'
                  + '<FirstName>Ella</FirstName>'
                  + '<Grammys>14</Grammys>'
                  + '<LastName>Fitzgerald</LastName>'
                  + '</Artist>',
            dataType: 'xml',
            // ...         

My ultimate goal is to create a truly RESTful web service. That’s why I want everything to be self-describing – including the namespace for my data contracts. It also means I’m not interested in the WSDL which is auto-generated by WCF. However, changing the contract names and namespaces has the added bonus of making your WSDL nicer too!

A default WSDL would be similar to this:

<?xml version="1.0" encoding="UTF-8"?>
 <wsdl:definitions name="JazzArtists" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" 
    ... 
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" 
    xmlns:tns="http://tempuri.org/" 
    ... 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
                targetNamespace="http://tempuri.org/">
 <wsdl:types>
  <xsd:schema targetNamespace="http://tempuri.org/Imports">
    <xsd:import namespace="http://tempuri.org/"
         schemaLocation="http://development.dlwelch.com/examples/RestService/JazzArtists.svc?xsd=xsd0"/>
    <xsd:import namespace="http://schemas.microsoft.com/2003/10/Serialization/" 
       schemaLocation="http://development.dlwelch.com/examples/RestService/JazzArtists.svc?xsd=xsd1"/>
    <xsd:import namespace="http://schemas.datacontract.org/2004/07/RestService" 
       schemaLocation="http://development.dlwelch.com/examples/RestService/JazzArtists.svc?xsd=xsd2"/>
   </xsd:schema>
 </wsdl:types>
 <wsdl:message name="IJazzArtists_XMLData_InputMessage">
 <wsdl:part name="parameters" element="tns:XMLData"/>
  ... 
</wsdl:definitions>

There are multiple references to “http://tempuri.org/” in addition to the data contract namespace “http://schemas.datacontract.org/2004/07/RestService”. We’ve already cleaned up the latter (line 15), now let’s get rid of tempuri.org. First, we’ll change the namespace for the service contract:

[ServiceContract ( Namespace = "http://schemas.dlwelch.com/JazzService" )]
public interface IJazzService
{

and then the service implementation:

namespace RESTful
{
    [ServiceBehavior(Namespace = "http://schemas.dlwelch.com/JazzService")]
    public class JazzService : IJazzService
    { 
      //...

Finally, we’ll change the namespace for the binding in the web.config:

  <system.serviceModel>
    <services>
      <service behaviorConfiguration="RESTful.JazzServiceBehavior" name="RESTful.JazzService">
        <endpoint address="" binding="webHttpBinding" 
                             bindingNamespace="http://schemas.dlwelch.com/JazzService" 
                             contract="RESTful.IJazzService" behaviorConfiguration="web">
        </endpoint>
   ...

This gives us WSDL using our custom namespaces instead of the WCF defaults:

<?xml version="1.0" encoding="UTF-8"?>
 <wsdl:definitions name="JazzService" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" 
    ... 
    xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" 
    xmlns:tns="http://schemas.dlwelch.com/JazzService" 
    ...
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
                targetNamespace="http://schemas.dlwelch.com/JazzService">
  <wsdl:types>
  <xsd:schema targetNamespace="http://schemas.dlwelch.com/JazzService/Imports">
    <xsd:import namespace="http://schemas.dlwelch.com/JazzService" 
         schemaLocation="http://development.dlwelch.com/examples/restful/JazzService.svc?xsd=xsd0"/>
    <xsd:import namespace="http://schemas.microsoft.com/2003/10/Serialization/" 
         schemaLocation="http://development.dlwelch.com/examples/restful/JazzService.svc?xsd=xsd1"/>
  </xsd:schema>
  </wsdl:types>
 <wsdl:message name="IJazzService_GetArtist_InputMessage">
 <wsdl:part name="parameters" element="tns:GetArtist"/>
...
</wsdl:definitions>

I haven’t “switched off” the WSDL for my JazzService even though the service will eventually be RESTful. There’s no harm to ROA by leaving it on. So if someone wants to create a client by importing the WSDL, it’s now available with our nice namespaces.


1. Please see my blog post Creating RESTful WCF Web Services Using Visual Studio 2010 for an example of creating WCF web services.
2. Data Contract Names – http://msdn.microsoft.com/en-us/library/ms731045.aspx

Consuming a RESTful web service with jQuery and Ajax

In my last post, we created a REST WCF web service using Visual Studio 2010. In this article, we’ll write a client side web applicaiton that uses jQuery and Ajax to consume a REST web service.

First, create an HTML page that includes a reference to a jQuery library, a <div> tag where we’ll display the service output, a button that will fire the Ajax request to the web service and one that will clear the output <div>.


<html>
<head>
<title>Consume a REST Web Service</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.min.js"/>
</head>
<body>
<p><div id="myOutput">Output Goes Here</div><p>
<p><input id="myButton" value="Get Artist" type="submit" /></p>
<p><input id="myClearButton" value="Clear Artist" type="submit" /></p>


Next, add the jQuery to run when the Get Artist button is clicked.

<script type="text/javascript">
 $("#myButton").click(function() {
        var artistURL = "http://development.dlwelch.com/examples/restservice/JazzArtists.svc/json/Shirley";
        var returnData = "";
        $.ajax({
            type: "GET",
            dataType: "json",
            async: true,
            url: artistURL,
            error: function(request, status, error) { alert(request.responseText) },
            success: function(data) {
                $("div#myOutput").html(" ");
                returnData = "<table style='font-size:8pt;'><tr><th>Artist</th><th>Grammys</th></tr>";
                for (prop in data) {
                    if (!data.hasOwnProperty(prop)) { continue; }
                    if (data[prop] != null) {
                        for (var i = 0; i < data[prop].length; i++) {
                            returnData += "<tr><td>" + data[prop][i]["FirstName"] 
                                       + " " + data[prop][i]["LastName"] + "</td><td align='right'>" 
                                       + data[prop][i]["Grammy"] + "</td></tr>";
                        }
                    }
                }
                returnData = returnData + "</table>";
                $("div#myOutput").html(returnData);
            }
        });
        return (false);
    });
</script>

...

Code line 3 defines the URI of the resource, in our case, the artist Shirley. Line 6 indicates we are sending an HTTP GET request to the resource and line 7 says we’ll evaluate the response as JSON and send a JavaScript object back to our success function on line 11. Since a JavaScript object was returned, line 14 starts the loop to find the property holding the response data. Line 15 test the current property to see if it’s a built in property of a JavaScript object. If so, skip it. Once we reach the property with our data, we loop through all the artist objects and create our HTML string for the output <div>.

Finally, add the jQuery to clear the output and finish your HTML.

<script type="text/javascript">
 $("#myClearButton").click(function() {
        $("div#myOutput").html("Output Goes Here");
        return (false);
    });

</script>


</body>
</html>


Your page should look like this. Click on the Get Artist button to see it work!



You can see a working example and grab the code for Ajax calls of all four HTTP verbs on my development site in the WCF RESTful Web Service example.

JavaScript, jQuery and Ajax for reporting on SharePoint lists

In this post, I will show you how to query two SharePoint lists using jQuery and Ajax. We will then combine the results into a summary report. I originally created this example for SP 2007 with the requirement it be completed using only client side capabilities. In addition, the customer wanted a graphical representation of the data and a drill down to details for each record, without the use of custom web parts.

The first list contains master project data.

The second list is a project status log which is populated by a workflow on the Projects List. Each time the project status is changed, an item is created in this project status list.

Now we’ll create our report using client-side scripting. I used SharePoint Designer 2007 and created a new web page at the root of the site. But you can also use a Content Editor Web Part to hold your script.

First, add a <div> to your page that will display a loading image and notification while your Ajax runs. We’ll hide this <div> once it’s done.

<div id="wait">
<p align="center"><img src="GEARS_AN.GIF" alt="Please wait..." />
<br/>Generating report..
</p>
</div>

Then add another <div> where you will display your results. I wrapped mine in a <table> so I can easily add rows with more information later.

<table class="ms-vb" cellpadding="1" border="0">
<tr><td>
<div id="listLinks"> </div>
</td></tr> 
</table> 

Now for the script. Define two JavaScript objects. One for the project list items and one for the statuses. Add an .ajaxStop function which will run when all Ajax calls complete. The .unbind function prevents it from running again if a subsequent Ajax call is made after the document loads. Finally, define what should happen when the document is ready.

<script type="text/javascript">

 var myProjArray = new Object();
 var myStatusArray = new Object();

 $(document).ajaxStop(function() {
  $(this).unbind("ajaxStop");
  createprojectstable();
 });

 $(document).ready(function() {
  getProjects();
 });

When the document is ready getProjects() runs. Since CAML joins are only supported in SharePoint 2010+, I have to make an extra Ajax call for each project to get the status items. A single call could have been made to return all status items rather than making a call for each project returned by the first. However, that list may have been much larger than needed if we were to restrict the projects returned in the first CAML query by using a WHERE clause. Your particular circumstance will dictate which approach to take.

//****************************************************************************************
//get projects from the Projects List
function getProjects(){

 var thisurl = window.location.href;
 var i = thisurl.lastIndexOf("/");
 var asmxlocation = thisurl.substr(0,i)
 asmxlocation = asmxlocation + "/_vti_bin/lists.asmx";


 var soapEnv = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'> \
     <soapenv:Body> \
     <GetListItems xmlns='http://schemas.microsoft.com/sharepoint/soap/'> \
      <listName>Projects List</listName> \
      <viewFields> <ViewFields> \
        <FieldRef Name='ID' /><FieldRef Name='Title' /> <FieldRef Name='ProjStatus' /> <FieldRef Name='Created' /> <FieldRef Name='Team' /> \
      </ViewFields> </viewFields> \
      <rowLimit>2000</rowLimit> \
      <query> <Query> \
        <OrderBy><FieldRef Name='Team' Ascending='TRUE' /><FieldRef Name='Title' Ascending='TRUE' /></OrderBy> \
      </Query> </query> \
     </GetListItems> \
     </soapenv:Body> \
     </soapenv:Envelope>";

$.ajax({
url: asmxlocation,
type: "POST",
dataType: "xml",
data: soapEnv,
async: true,
complete: processResult,
contentType: "text/xml; charset=\"utf-8\""
});

}

//**************************************************************************************** 
//process results from projects query
function processResult(xData, status) {

$(xData.responseXML).find("z\\:row").each(function() { 

myProjArray[$(this).attr("ows_ID")] = [$(this).attr("ows_Title"),$(this).attr("ows_ProjStatus"),$(this).attr("ows_Created"),$(this).attr("ows_Team") ]; 

//CAML Joins are only available in SP 2010+ so we got to loop
getProjectStatus($(this).attr("ows_ID")); 

});
} 

//****************************************************************************************
//get status records for a specific project
function getProjectStatus(ProjID){

var thisurl = window.location.href;
var i = thisurl.lastIndexOf("/");
var asmxlocation = thisurl.substr(0,i)
asmxlocation = asmxlocation + "/_vti_bin/lists.asmx";


var soapEnvZ = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'> \
    <soapenv:Body> \
    <GetListItems xmlns='http://schemas.microsoft.com/sharepoint/soap/'> \
      <listName>Project Status Log</listName> \
      <viewFields> <ViewFields> \
         <FieldRef Name='ID' /> <FieldRef Name='ProjectID' /> <FieldRef Name='Status' /> <FieldRef Name='Created' /> \
      </ViewFields> </viewFields> \
      <rowLimit>1000</rowLimit> \
      <query> <Query> \
        <Where> <Eq> <FieldRef Name='ProjectID' /> <Value Type='Counter'>" + ProjID + "</Value> </Eq> </Where> \
        <OrderBy><FieldRef Name='Created' Ascending='TRUE' /></OrderBy> \
      </Query> </query> \
    </GetListItems> \
    </soapenv:Body> \
    </soapenv:Envelope>"; 

$.ajax({
url: asmxlocation,
type: "POST",
dataType: "xml",
async: true,
data: soapEnvZ,
complete: processResultZ,
contentType: "text/xml; charset=\"utf-8\""
});
}

//****************************************************************************************
//process results from the status query
function processResultZ(xData, status) {
var statusarray = new Array();
var arraycounter = 0
var projID;

$(xData.responseXML).find("z\\:row").each(function() { 
   statusarray[arraycounter++] = [$(this).attr("ows_Status"),$(this).attr("ows_Created")];
   projID = parseInt($(this).attr("ows_ProjectID"));
   myStatusArray[projID] = statusarray;
}); 

}

Now that we have the projects and statuses objects with a common property, the ID of the project, we can display the data any way we wish. createprojectstable() runs when the Ajax calls have completed and creates the HTML and JavaScript for the reports. I created a horizontal bar representing the length of time in each status for each project. There is also a drop down text area of the status details for each project. This is just another table row that is hidden and made visible by clicking the table row containing the graph. Click on the different elements below to see a live demo.

 

Here’s the interesting parts of creating the report – looping through the properties of the project and statuses objects to build the output HTML. (Note that, even though they are named “array”, they are actually objects.) And finally, we’ll hide the loading image and notification <div>.

function createprojectstable(){

var outHtml = "..."

//create report headings

for (prop in myProjArray) { 
  if (!myProjArray.hasOwnProperty(prop)) { 
    continue; //The current property is not a direct property of p, so goto the next one. i.e. it is not a poperty we created but a built-in JavaScript     property 
  } 

  //create HTML for each project

  if (myStatusArray[prop] !== undefined){
    //and now create the status details for the project
  }
  outHtml += "..."
  $("#listLinks").append(outHtml); 
  $("#wait").hide(); 

  //create report footers and initiate any other report options
}

You can download the source of a working example. This one reads XML files instead of SharePoint lists but is essentially the same. I’ve only tested the code in IE 8 and Firefox 5 but it would probably be quick work to make it cross-browser compatible.