It's quite evident CQ utilizes AJAX frequently.
If you've ever opened your network tab while using CQ you'll see asynchronous requests going off here and there. When authoring new components, navigating content, developing in CRXDE lite and much more. If you're unsure what it is, AJAX lets us make asynchronous server request to content without having to do a full page reload.
As a CQ developer, we create may cq:Component's that are rendered with a page request but what if we want to create one that is requested via AJAX.
Well, why do we need to do this in CQ?
Sometimes we want a sleeker and more fluid user experience and not a full page reload every time for things like search or galleries.
However, in a lot of cases, it has to do with our Dispatcher. If we want dynamic pieces of content based on different users or situations we need to request via AJAX because the Dispatcher caches our rendered HTML for us so we don't have to re-render our pages for every request. This does not mean we cannot still leverage the Dispatcher, our separate request can also be cached ( using selectors ) with the default dispatcher settings.
With CQ Personalization Framework we collect specific data on the client-side and want it to render differently from user to user, AJAX needs to be used in this scenario as well.
How is this usually done?
Most common cases we need to create separate service that returns json to request the data we want. We request the data and merge it to an HTML template with Javascript. So we need to create some new things here
- A service
- An HTML template or even worse HTML embed in Javascript
- Some additional component-specific Javascript
- cq:Component
What if there is an easy solution to create a component with AJAX using a Sling trick
A nice feature that I don't know if a lot of people are aware of is that you can request specific components on your page by request the URL to the component Node.
You still need to create two things but your Javascript can possibly be reused for any AJAX component you create in the future.
- cq:Component
- Javascript library
We use the knowledge of our node structure and make a request along the lines of this
/my-page/jcr:content/my-component.{selector}.html?{url-param}={param-value}
Since we want our component to render differently in different situations we can either use selectors or URL parameters to do such and handle them in our component.
Then all we do is re-request our component and replace it on completion.
$.get("/my-page/jcr:content/my-component.{selector}.html?{url-param}={param-value}")
.success(function(data){
$("component-id").replaceWith($(data));
});
In our JSP we add some if statements using JSTL to render our content differently.
It's up to you to decide how and when you want to change your output based on selectors or request parameters.
<c:set var="selectorArray" value="${'|'}${fn:join(slingRequest.selectors, '|')}${'|'}" />
<c:if test="${ fn:contains(selectorArray, '|testSelectorVal|') }">
//something rendered differently
</c:if>
<c:set var="paramValArray" value="${'|'}${fn:join(paramValues['testParam'], '|')}${'|'}" />
<c:if test="${ fn:contains(paramValArray, '|testParamVal|') }">
//something rendered differently
</c:if>
Avoid Hard Coding
If we don't want to hard code our id, paths, selectors, and parameters and we want to be able to put more than one of these components per page
I recommend using unique identifiers, a data-attribute, and a class.
I recommend using unique identifiers, a data-attribute, and a class.
<div id="${currentNode.identifier}" class="ajax-container"
data-ajax-path="${currentNode.path}">
</div>
We will create a Javascript module with the following function design as desired, this should be inside a cq:clientLibs folder.
window.AjaxComponent = (function(){
var module = {};
var buildFullPath = function(path,selectors,params){...};
module.executeCompentAjaxCall = function(id,path,selectors,params){
$.get(buildFullPath(path,selectors,params))
.done(function(data){
$(id).replaceWith($(data));
});
};
module.getSelectors = function(){...};
module.getParams = function(){...};
$(document).ready(function(){
$(".ajax-container").each(function(i,ajaxContainer){
module.executeCompentAjaxCall($(ajaxContainer).attr("id"),
$(ajaxContainer).attr("data-ajax-path"),
module.getSelectors(), module.getParams());
});
return module;
})();
These are just suggestions and you may develop your Javascript library however you desire. This assumes your method of getting selectors and parameters are the same for every component and that you want your component requested on document ready.
If our AJAX request is called on document ready, I recommend hiding it via CSS first then showing the updated content or not rendering anything initially so we don't get a flickering effect. If the request is on user interaction a loading overlay is nice while the content is being requested.
In Conclusion
Requesting the entire component via Sling saves us a lot of work and is a very clean solution with less development effort. The main drawback is we are requesting slightly more data than using a JSON service.
If you plan on having dynamic content in one of your cq:Components use AJAX and this technique!
No comments:
Post a Comment
If you have any doubts or questions, please let us know.