A question that has been coming up with some regularity lately has to do with doing dynamic, AJAX based work with the content engine in dotCMS. For some time, dotCMS has offered DWR as a means of handling those needs, but ultimately, DWR is a cumbersome, limited tool. In the end, there are two other ways of getting similar results which are much easier for most people to implement.
Since we’re going to ignore the existence of DWR, you’re going to need to be familiar with something like jQuery for doing AJAX work. You’re not going to get away from using JavaScript (it’s… you know… sort of integral to the whole concept of AJAX), but we can at least use something that’s more common and familiar. It doesn’t have to be jQuery, but that’s what I’ll be using here.
Calling a Proxy Page
This is the simplest way of handling AJAX needs. First, we’ll assume that you already know the context that the AJAX response is going to be used in. In that case, we’ll return some actual HTML ready to be inserted into a page. Set up a purely blank template if you don’t already have one (e.g. it has no other HTML on the page besides that supplied from its container), and include a single container. Now, make a page with that template, add a Simple Widget, and code it out to pull some content.
## CHECK TO SEE IF A CATEGORY FILTER WAS SUPPLIED
#if($UtilMethods.isSet($request.getParameter('cat')) && $request.getParameter('cat').length() > 0)
#set($queryParams = " +categories:${request.getParameter('cat')}")
#end
## PULL THE LIST OF PRODUCT CONTENTLETS
#set($productList = $dotcontent.pull("+structureName:Product$!{queryParams}",0,"modDate desc"))
#if($productList.size() > 0)
#set($numResults = "$!{productList.size()} product")
## CHECK TO PLURALIZE 'product'
#if($productList.size() > 1)
#set($numResults = "$!{numResults}s")
#end
#else
#set($numResults = 'Sorry, no products were found in that category')
#end
<p>Found: $!{numResults}</p>
<ol>
## SHOW THE LIST OF PRODUCTS
#foreach($con in $productList)
<li><a href="/products/$!{con.urlTitle}">$!{con.title}</a></li>
#end
</ol>
This is a very simple example of an HTML response you could build to have the page return. Realistically, you can include as much or as little information here as you want, it just depends on what you plan to do with the data on the page where it ends up. In this case, assume it’s a basic search filter to show products on a page, allowing the visitor to filter based on the available categories. The result is a basic ordered list of links to the products that matched the search.
Next it’s all about setting up your front end page where visitors will view the content. Create a second page (using a normal template), and we’ll add in a search dropdown box and a little JavaScript to run things.
<form id="productSearch" method="get" action="">
<label for="productCats">Filter by:</label>
<select id="productCats">
<option value="">ALL</option>
#foreach($cat in $categories.getChildrenCategoriesByKey('productTypes'))
<option value="${cat.key}">${cat.categoryName}</option>
#end
</select>
</form> <!-- ${esc.h}productSearch -->
<div id="productSearchResults"></div>
<script type="text/javascript">
${esc.d}(document).ready(function() {
var showProducts = function(cat) {
${esc.d}.ajax({
type: 'GET',
url: 'product-response.dot',
data: 'cat=' + cat,
dataType: 'html',
success: function(data) {
${esc.d}('${esc.h}productSearchResults').html(data);
}
});
};
$('${esc.h}productCats').change(function() {
var cat = $(this).val();
showProducts(cat);
});
showProducts(null);
});
</script>
So, very simple – search happens, jQuery requests info, response is returned, HTML is inserted into div. It’s simple to do, and works great when you know how the HTML is going to be handled.
The JSONContent Servlet
In 1.9, there is a new servlet tool you can use now that will return content as either JSON or XML data on demand. This is perfect when you don’t necessarily know what the HTML markup is that will be needed. And better, simply calling http://www.yoursite.com/JSONContent will give you the basic instructions to using the servlet. It takes six different parameters:
- q: URLEncoded lucene query (required, try the
$UtilMethods.encodeURL($query)viewtool) - type: json or xml (default: xml)
- limit: number of results (default: 10, max of 1000)
- offset: start at specific result number (default: 0)
- orderBy: field to order by (default: modDate)
- debug: true/false; if set, will return JSON as text/plain (defaults: false)
By making an AJAX request to this servlet we can basically do the same job as above. For instance, instead of hitting the product-response.dot page in the JavaScript, we could do http://demo.dotcms.org/JSONContent/?type=xml&q=%2BstructureName:product. This returns all the info on the contentlets in JSON format (remember, you could change the type parameter if you want to stick with XML) that we could then parse into the page. By working this way, we can drop the need for the proxy page, and we only need our main page to send this query. For the same kind of page, the changes would look something like:
<form id="productSearch" method="get" action="">
<label for="productCats">Filter by:</label>
<select id="productCats">
<option value="">ALL</option>
#foreach($cat in $categories.getChildrenCategoriesByKey('productTypes'))
<option value="${cat.categoryVelocityVarName}">${cat.categoryName}</option>
#end
</select>
</form> <!-- ${esc.h}productSearch -->
<div id="productSearchResults"></div>
<script type="text/javascript">
${esc.d}(document).ready(function() {
var showProducts = function(cat) {
var catQ = '';
if(cat && cat.length > 0) {
catQ = '%20%2Bcategories:' + cat;
}
${esc.d}.ajax({
url: '/JSONContent/',
data: 'type=json&q=%2BstructureName:product' + catQ,
dataType: 'json',
success: function(data) {
${esc.d}('${esc.h}productSearchResults').html('');
${esc.d}('${esc.h}productSearchResults').append('<p>Found: ' + data.contentlets.length + ' products</p><ol></ol>');
${esc.d}.each(data.contentlets, function(i, contentlet) {
${esc.d}('${esc.h}productSearchResults ol').append('<li>' + contentlet.title + '</li>');
});
}
});
};
${esc.d}('${esc.h}productCats').change(function() {
var cat = ${esc.d}(this).val();
showProducts(cat);
});
showProducts(null);
});
</script>
This effectively produces the exact same results as above. The only change we made to the form itself was the dropdown values now use the category’s Velocity variable name (in the previous example, we used the category key). Otherwise we get a JSON data response back from the /JSONContent servlet, and use the jQuery to loop the results into the page.
Conclusions
Overall, either way works just as well as another, and it is up to you which works best for your style or application. Additionally, you could write your own custom tools to output XML or JSON from dotCMS for other applications. The first method works well when you know the context you’ll use the response in and when you want to limit the amount of JavaScript you’re using. The second way is good when you may not know how the response is used or if you need to manipulate the base data before displaying it.
Photo Credit:
Sorry, it seems there aren't any similar posts yet.





It was a great article, except I would disagree with this statement – “DWR is a cumbersome, limited tool.” I think DWR would be a great tool to use for this if more methods were exposed in dotcms such as a way to query and pull content.
Great post – and using JSON means we can write pages (called by AJAX) that can act as services for more than just returning HTML.
Rob
Great post. I have a question though.
How do I get full url of an image field? It looks the json serverlet returns image id instead of its URL.
I have same question as Moon – how do you get the full url of an image field? If it’s not possible, please post the answer anyway so we’re not wondering.
insert tumbleweed here
Sorry guys, my email alerts weren’t working apparently on this. If you have the ID, you can always predictively construct the URL using the SpeedyAssetServlet
.
1.7
/dotAsset/${imageIdentifier}
1.9
/contentAsset/image/${imageIdentifier}