Drupal 7: Allow a Theme to use a newer version of jQuery

Recently I have started working with Drupal 7. Drupal is a CMS built on top of PHP, MySQL, and jQuery. In Drupal, a website layout is managed by a Theme. Boostrap Themes can be plugged in – or, you can roll your own. At Qualcomm, my group created a custom theme that required a newer version of JQuery (1.11) than Drupal 7 provided (1.4).

Initially, our custom theme loaded the newer version of jQuery. Drupal 7 allows this as it uses jQuery’s .noConflict() function. .noConflict() is frees up jQuery’s $ alias for other frameworks to use. In this case the “other” framework … was just a newer version of jQuery.

There were two problems with this …

  1. We were loading jQuery TWICE. When visiting the site Drupal would load jQuery 1.4 – and then our custom theme would load jQuery 1.11.
  2. We discovered that any jQuery plugins loaded by Drupal for jQuery 1.4 needed to be reloaded by our custom theme after it loaded jQuery 1.11.
  3. I found this out the hard way. I kept getting TypeError: $(…).once is not a function” errors. This was because our custom theme required the jquery.once.js plugin. This plugin was initially loaded by Drupal when it loaded JQuery 1.4 – but subsequentally wiped out by our custom theme’s newer JQuery.

To fix this we did the following …

  1. We added the desired version of JQuery to our custom theme :

    \sites\all\themes\[custom_theme]\js\jquery-x.x.x.js

  2. We added this statement to our custom themes template.php :

    So, in this file :

    \sites\all\themes\[custom_theme]\template.php

    We added this code :

    		function [custom_theme]_js_alter(&$javascript) {
    			$javascript['misc/jquery.js']['data'] = drupal_get_path('theme', '[custom_theme]') . '/js/jquery-x.x.x.js';
    		}
    		

    This will instruct Drupal to swap out the version of jQuery (1.4) it uses with the version that resides in your custom theme. :

  3. We encapsulated any javascript used by our custom theme into a Javascript module and injected jQuery into it.

    For example :

    Behavior = (function($){
    
    	$( document ).ready(function() {
    
    	});
    
    	// Public Interface.
    	return {
    
    		myFunc : function () {
    
    		}
    	}  
    }(jQuery));	
    

    Notice that jQuery is injected rather than its $ alias. As I mentioned earlier this is because Drupal invokes jQuery’s .noConflict() function. To tidy things up a bit I inject jQuery as $.

  4. We stopped our custom theme from loading its own version of jQuery. It is no longer necessary as (1) and (2) above will force Drupal to use whatever version of jQuery you desire.

    So in this file :

    sites/all/themes/[custom_theme]/[custom_theme].info

    We removed this :

    			
    			scripts[] = 'js/jquery-x.x.x.js' <=== comment this out.
    			

I Hope this helps! Let me know if you have any questions!

A Paginated JQuery UI Autocomplete Widget

The JQuery UI Autocomplete Widget allows you to return a list of suggestions based upon a query string. The widget works great unless the user does not know what to search for. For example, if the user wants to search through a large database of people using only a single letter (presumably the first letter of the last name) the Autocomplete Widget quickly becomes unwieldy. Either the suggestions need to be truncated or the list of suggestions becomes so large as to be unusable.

To resolve this problem I have extended the JQuery UI Autocomplete Widget and added pagination. Arrows allow the user to cycle through pages of suggestions. Each page of is retrieved using AJAX. Click here for a jsFiddle sample. The sample uses MockJax to simulate the AJAX calls. A complete end-to-end example using ASP.NET MVC is available here.

The Paginated Autocomplete Widget inherits the same API as the Autocomplete Widget from which it is derived. However, there are some differences to be aware of …

First, the web server endpoint that handles the AJAX GET must support three parameters – search, pageSize, and pageIndex. search is the query string provided by the user. pageSize is the number of items to return per page. pageIndex is the page to return.

	
[HttpGet]
public JsonResult SearchCars(string search, int pageSize=20, int pageIndex=0)
{
	// Your logic here.
}
ASP.NET MVC Controller ActionMethod to support the Paginated Autocomplete Widget.

Second, the Paginated Autocomplete expects a JSON response from the web server in a specific format. The Widget requires this format to facilitate pagination. data is an array of text/value objects that will get bound to to the list of suggestions. total is the total number of unpaginated suggestions that the search query returned.

		
{	
	data : [
		{ text : "Item 1", value : 0 },
		{ text : "Item 2", value : 1 },
		{ text : "Item 3", value : 2 }
	],
	total : 1000
}
JSON result expected by the Paginated Autocomplete Widget.

Third, when using the Paginated Autocomplete Widget in Javascript you need to specify sourceUrl. The Widget will automatically generate an AJAX GET to the sourceUrl and retrieve paginated suggestions as necessary. This is different than the Autocomplete Widget where you need to define the source method as well as the underlying AJAX GET. The obfuscation is necessary to facilitate pagination. In addition pageSize is an optional parameter that determines the number of suggestions per page.

<input id="myFilter" type="text" />   	      

<script type="text/javascript">

$(document).ready(function() {	

	$("#myFilter").paginatedAutocomplete({
		pageSize: 10,
		sourceUrl: '/Simpsons/SearchCharacters'
	});
}); 

</script>
Using the Paginated Autocomplete Widget in Javascript.

The Paginated Autocomplete Widget requires JQuery 1.9.1 or newer and JQuery UI 1.10.3 or newer. I have tested it against Internet Explorer 7/8/9/10, Firefox 21, and Chrome 27. It will probably run in other permutations as well. Please let me know if you have any comments or questions.

Simple AJAX Using JQuery, JSON, ASP.NET, and WCF

I know what you’re thinking. Oh great, another article on AJAX! Okay, sure. You’re right. But seriously, when I was originally attempting to learn this stuff I found myself jumping through hoops just trying to get a simple example to work…

  • How do I configure a web service to return JSON?
  • What in the heck is JSON?
  • Why not use XML?
  • What is JQuery?
  • How do I call a Web Service from JavaScript?

Yeah, there were articles. Lots of articles. But I found that no single article presented a complete example. Some would focus on the client. Some the server. Some would gloss over the “why” and emphasize the “how”. Others would pull in obscure technology that, while cool, would be completely at odds in a business environment.

So, here’s my own entry into wide assortment of AJAX articles – a simple Echo Service.

Why an Echo Service?

It’s a very simple way to demonstrate how to send/receive JSON objects between a client browser and a Web Service. This is common pattern that is used in modern websites. A simple example makes it easier to showcase a bunch of cool (yet essential) technologies such as…

ASP.NET Microsoft’s web application framework.
Javascript The defacto standard for manipulating the HTML DOM.
JQuery An open-source Javascript Library for manipulating the HTML DOM. Endorsed by Microsoft and included with Visual Studio 2010.
JSON Like XML but with a (much) smaller footprint. JSON is Javascript. It can be serialized into a human-readable string or deserialized into a Javascript object that can be used programatically in Javascript code. When serialized it looks like this …

{
"name": "Homer Simpson",
"homeAddress": {
"address": "742 Evergreen Terrace",
"city": "Springfield",
"state": "NT",
"zip": "49007"	
}
}              
Windows Communication Foundation (WCF) Microsoft’s implementation of a SOA framework. We’ll use it to create a web service.

The Echo Service

Overview

First, we’re going to create an EchoService Web Service using Microsoft’s Windows Communication Foundation (WCF). A Message class will be used to facilitate communication with the EchoService. The Message will be automatically converted to/from JSON by WCF.

Next, we’re going to create an ASP.NET EchoPage Web Form. A blank JSON representation of the Message class will be exposed to the EchoPage’s client-side. This JSON Message will be populated with some text and submitted to the EchoService using JQuery’s AJAX implementation.

Finally, we’re going to apply some enhancements to the EchoService and EchoPage to make them a little more robust – while eliminating unecessary code.

EchoService WCF Web Service

The EchoService exposes a single Echo() method that receives and returns a JSON object. Why a JSON object and not just a bunch of params? Because parameters are sloppy and difficult to manage. Also, it’s easier to deal with the same data object on both the server and the client.

The EchoService’s handling of JSON is declared in the attributes for the Echo() method. Notice the ResponseFormat and RequestFormat indicators. This tells WCF to implicitly convert between the Message class and JSON.

[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
public Message Echo (Message message)
{
message.Text = string.Format("Echo {0}", message.Text);

return message;
}
EchoService.svc.cs

EchoPage ASP.NET Web Form

Client-Side (.aspx)

The client-side portion of the EchoPage uses JQuery’s AJAX implementation to submit (and receive) Messages to the EchoService. The “data” parameter specifies the data that you want to submit to the EchoService. The “dataType : json” instructs JQuery to expect a JSON result from the EchoService. JQuery will implicitly convert the EchoService’s response. The “success” function is called upon a successful transaction.

Be very careful when specifying your “data” parameter. It should be in the form of a JSON (name/value pair) object where the name corresponds to the parameter name on the EchoService’s Echo() method. The value is your actual data – serialized using the JSON.Stringify() function. Also, take special notice of the quotation marks. I’ve had problems before with single versus double-quotes.

$.ajax({
type: "POST",
url: "EchoService.svc/Echo",
data: '{ "message" : ' + JSON.stringify(jsonMessage) + '}',
dataType: "json",											
contentType: "application/json; charset=utf-8",             
success: function(data, textStatus, httpRequest) {
	
	   data = data.hasOwnProperty('d') ? data.d :  data;										

	   $("#responseMessage").html(data.Text);
	},
error:   function(httpRequest, status, errorThrown) {

	   $("#responseMessage").addClass("error");
	   $("#responseMessage").html("There was a problem calling the Echo Service. Error : " + errorThrown + "!");							
	}
});
EchoPage.svc.cs
What’s the deal with the .d in the AJAX success function?

Since ASP.NET 3.5 Microsoft’s WCF Service encapsulates all JSON responses in a “d” name-value pair for security reasons. You can read more about it here and here. A side-effect of this is that we need to handle the encapsulation in our client-side Javascript …

data = data.hasOwnProperty('d') ? data.d :  data;
EchoPage.aspx
What’s the deal with the JSON.stringify() ?

When submitting a request to the EchoService using JQuery’s AJAX the data needs to be in the form of a string. JSON.stringify() serializes our Message (which is a JSON object) so that it can be submitted properly.

$.ajax({
type  : "POST",
url   : "EchoService.svc/Echo",
data  : '{ "message" : ' + JSON.stringify(jsonMessage) + '}',

...
EchoPage.aspx

Server-Side (.aspx.cs)

The server-side portion of the EchoPage is relatively sparse. When we start optimizing our code later it will disappear entirely. For now, the server-side portion of the EchoPage creates a JSON representation of an empty Message.

protected string jsonMessage;

protected void Page_Load(object sender, EventArgs e)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Message));
using (MemoryStream ms = new MemoryStream())
{
serializer.WriteObject(ms, new Message());
jsonMessage = Encoding.Default.GetString(ms.ToArray()); // Viola! 
}
}
EchoPage.aspx.cs

This JSON Message is exposed to the client-side Javascript as a var and is used in the JQuery AJAX submission to the EchoService.

var jsonMessage = <%= jsonMessage %>;	
EchoPage.aspx

Okay, great. Is that it?

No, although the above code works there are a few issues that we should probably look into …

  1. ASP.NET’s JSON security is a nice touch. However, our handling of the “d” encapsulation on the client-side of the EchoPage is clunky.
  2. Our serialization of the Message object into JSON on the server-side of the EchoPage is very specific to a particular class.
  3. If an error occurs while the EchoService is processing a Message there is no way to properly notify the EchoPage so that it might handle the error gracefully.

Polishing the Echo Service

JQuery’s AJAX dataFilter

JQuery exposes a customizable “dataFilter” for it’s AJAX calls. Declaring a dataType instructed JQuery to implicitly handle the response from a Web Service. Declaring a dataFilter puts the responsibility solely on the developer.

We can leverage JQuery’s AJAX dataFilter when handling ASP.NET’s “d” security feature on the EchoPage. Notice that we need to programatically deserialize the EchoService’s response into a Javascript object using JSON.parse().

$.ajax({
type : "POST",
url : "EchoService.svc/Echo",
data : '{ "message" : ' + JSON.stringify(jsonMessage) + '}',	
/* dataType: "json",  */
contentType : "application/json; charset=utf-8",      
dataFilter :   function(data) {

			  data = JSON.parse(data);

			  return data.hasOwnProperty("d") ? data.d : data;
		  },				       
success: function(data, textStatus, httpRequest) {
											  
	   /* data = data.hasOwnProperty('d') ? data.d :  data; */										
	
	   $("#responseMessage").html(data.Text);
	},	
error:   function(httpRequest, status, errorThrown) {

	   $("#responseMessage").addClass("error");
	   $("#responseMessage").html("There was a problem calling the Echo Service. Error : " + errorThrown + "!");							
	}
});   
EchoPage.aspx

JSON Serialization Extension Method

NET 3.5 introduced Extension Methods. Extension Methods allow you to add new methods to an existing type. Adding an extension method to the .NET base object class exposes the method to all classes.

Currently the EchoPage can only serialize a Message into JSON. However, by moving the code into an Extension Method we can clean up the EchoPage and eliminate a lot of (potentially) redundant code. This is what the new .GetJsonString() Extension Method looks like …

/// <summary>
/// Extension methods to .NET classes.
/// </summary>
public static class Extensions
{
/// <summary>
/// Extends object to with a JSON serializer.
/// </summary>        
/// The object as a serialized JSON string.
public static string GetJsonString(this object obj)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
using (MemoryStream ms = new MemoryStream())
{
 serializer.WriteObject(ms, obj);
 return Encoding.Default.GetString(ms.ToArray());
}
}
}
Extensions.cs

The EchoPage can now call .GetJsonString() directly from the Message data object.

var jsonMessage = <%= (new SimpleAJAXEchoService.Message()).GetJsonString() %>
EchoPage.aspx

As far as the server-side code for the EchoPage? Gone.

/// <summary>
/// An Web Page that "talks" to the Echo Service using AJAX.
/// </summary>
public partial class EchoPage : System.Web.UI.Page
{        
// The addition of the .GetJsonString() method eliminates all of this code. Furthermore, it can be called against any class derived from 
// object (which is pretty much anything).

///// <summary>
///// A serialized JSON representation of the Message class. Will be exposed to the EchoPage's Javascript and used to submit data to 
///// the EchoService.
///// </summary>
//protected string jsonMessage;

//protected void Page_Load(object sender, EventArgs e)
//{
//    // We'll need to submit an instance of the Message class to the EchoService from the EchoPage using Javascript. JSON is an ideal format to 
//    // work with. Let's serialize an instance of Message into a JSON string and expose it to the EchoPage's Javascript.
//    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Message));
//    using (MemoryStream ms = new MemoryStream())
//    {
//        serializer.WriteObject(ms, new Message());
//        jsonMessage = Encoding.Default.GetString(ms.ToArray()); // Viola! 
//    }
//}
}
EchoPage.aspx.cs

Encapsulating the Response

By wrapping the response from the EchoService we can more effectively handle errors that it might throw on the EchoPage. We can wrap up the EchoService’s response Message in a new ClientResponse wrapper. In addition to a Payload (the Message) the ClientResponse exposes an IsSuccessful and ErrorMessage.

/// <summary>
/// A generic client response wrapper. Wraps a web service response so that 
/// additional (troubleshooting) information can be returned alongside the payload.
/// </summary>
/// The payload type to encapsulate.
[DataContract]
public class ClientResponse
{
/// <summary>
/// The data to return to the client.
/// </summary>
[DataMember]
public T Payload { get; set; }

/// <summary>
/// True, if the data was retrieved sucessfully.
/// </summary>
[DataMember]
public bool IsSuccessful { get; set; }

/// <summary>
/// The error message if the data was not retrieved sucessfully.
/// </summary>
[DataMember]
public string ErrorMessage { get; set; }

/// <summary>
/// Constructor.
/// </summary>
public ClientResponse()
{
IsSuccessful = true;
ErrorMessage = string.Empty;            
}

/// <summary>
/// Constructor.
/// </summary>
/// True, if the data was retrieved sucessfully.
/// The data to return to the client.
/// The error message if the data was not retrieved sucessfully.
public ClientResponse(bool isSuccessful, T payload, string errorMessage) : this()
{
IsSuccessful = isSuccessful;
Payload = payload;
ErrorMessage = errorMessage;
}
}
ClientResponse.cs

We can leverage JQuery’s AJAX dataFilter to implictly handle the ClientResponse wrapper. Any errors incurred will now be passed onto JQuery’s AJAX error() function.

$.ajax({
type: "POST",
url: "EchoService.svc/Echo",
data: '{ "message" : ' + JSON.stringify(jsonMessage) + '}',	
/* dataType: "json",  */
contentType: "application/json; charset=utf-8",      
dataFilter: function(data) {

		  data = JSON.parse(data);

		  data = data.hasOwnProperty("d") ? data.d : data;

		  if (data.hasOwnProperty("IsSuccessful")) {

			 if (data.IsSuccessful == true) {

				return data.Payload;	
			 }
			 else {

				var errorMessage = "Error";

				if (data.hasOwnProperty("ErrorMessage") && data.ErrorMessage !== null) {

				   errorMessage = data.ErrorMessage;
				}

				throw errorMessage;			
			 }						
		  }					

		  return data;					
	   },	
error:   function(httpRequest, status, errorThrown) {

		$("#responseMessage").addClass("error");
		$("#responseMessage").html("There was a problem calling the Echo Service. Error : " + errorThrown + "!");							
	}
});
EchoPage.aspx

Conclusion

Okay, sure. The Echo Service was a simple example. But hey! It showcased a whole bunch of cool technologies in a nice little package. Now, next time someone asks you on the street, “Excuse me, but what is AJAX?” you can look them in the eyes and reply, “Let me tell you about a simple little Echo Service that I know…”.

The source code is available here. Please let me know if you have and questions or comments.