Goal
Touch UI pathbrowser widget of AEM - /libs/granite/ui/components/foundation/form/pathbrowser is based on paths, showing the results of path selector - .pages.json; this post is on extending ootb pathbrowser and creating a new search based pathbrowser
For demo purposes, the linkURL property - /libs/foundation/components/image/cq:dialog/content/items/column/items/linkURL, of foundation image component dialog was modified to use search based pathbrowser
Demo | Package Install
Widget and Query Parameters
sling:resourceType - String - /apps/eaem-touchui-search-pathbrowser/search-pathbrowser
queryParameters - String[] - jcr:content/cq:template=/apps/geometrixx/templates/contentpage
jcr:content/cq:lastModifiedBy=admin
Inline Dialog
Full Screen Dialog
Solution
1) Login to CRXDE Lite and create folder /apps/eaem-touchui-search-pathbrowser
2) Create sling:Folder /apps/eaem-touchui-search-pathbrowser/search-pathbrowser for adding the widger renderer jsp
3) Add the widget renderer jsp /apps/eaem-touchui-search-pathbrowser/search-pathbrowser/search-pathbrowser.jsp with the following code extending /libs/granite/ui/components/foundation/form/pathbrowser
<%@ page import="com.adobe.granite.ui.components.Config" %>
<%@include file="/libs/granite/ui/global.jsp" %>
<%
Config mCfg = cmp.getConfig();
String SEARCH_PATHBROWSER_WRAPPER_ID = "eaem-search-pathbrowser-wrapper-" + mCfg.get("name", String.class).substring(2);
String EAEM_PREFIX = "eaem.granite.ui.search.pathBrowser";
%>
<div id="<%=SEARCH_PATHBROWSER_WRAPPER_ID%>">
<%--include ootb pathbrowser--%>
<sling:include resourceType="/libs/granite/ui/components/foundation/form/pathbrowser"/>
</div>
<script>
(function($){
var wrapper = $("#<%=SEARCH_PATHBROWSER_WRAPPER_ID%>"),
pathBrowser = wrapper.find("[data-init='pathbrowser']");
if(_.isEmpty(pathBrowser)){
console.log("EAEM - search path browser wrapper not found");
return;
}
//set the search based pathbrowser loaders and renderers defined in search-based-pathbrowser.js
pathBrowser.attr("data-autocomplete-callback", "<%=EAEM_PREFIX%>" + ".autocompletecallback");
pathBrowser.attr("data-option-loader", "<%=EAEM_PREFIX%>" + ".optionLoader");
pathBrowser.attr("data-option-renderer", "<%=EAEM_PREFIX%>" + ".optionRenderer");
}(jQuery));
</script>
4) Create a clientlib /apps/eaem-touchui-search-pathbrowser/clientlib with categories cq.authoring.dialog and dependencies underscore
5) Add file /apps/eaem-touchui-search-pathbrowser/clientlib/js.txt with the following content
search-based-pathbrowser.js
6) Add file /apps/eaem-touchui-search-pathbrowser/clientlib/search-based-pathbrowser.js for adding the necessary autocomplete callback, option loader and option renderer used by the widget, with following code
(function(){
var EAEM_PREFIX = "eaem.granite.ui.search.pathBrowser",
ROOT_PATH = "rootPath",
QUERY_PARAMS = "queryparameters", // somehow queryParameters is read as queryparameters
QUERY = "/bin/querybuilder.json?";
//executed when user initiates search in pathbrowser by typing in a keyword
function searchBasedAutocompleteCallback(){
return{
name: EAEM_PREFIX + '.autocompletecallback',
handler: autoCompleteHandler
};
function autoCompleteHandler(searchTerm){
var self = this, deferred = $.Deferred();
if(_.isEmpty(searchTerm)){
return;
}
var searchParams = getSearchParameters(self, searchTerm);
self.optionLoader(searchParams, callback);
function callback(results){
if(_.isEmpty(results)){
deferred.resolve([]);
return;
}
self.options.options = results;
deferred.resolve(_.range(results.length));
}
return deferred.promise();
}
function getSearchParameters(widget,searchTerm){
var searchParams = {
fulltext: searchTerm
};
var path = widget.$element.data(ROOT_PATH), tokens,
queryParams = widget.$element.data(QUERY_PARAMS);
if(!_.isEmpty(path)){
searchParams.path = path;
}
if(!_.isEmpty(queryParams)){
queryParams = queryParams.split("");
_.each(queryParams, function(param, index){
tokens = param.split("=");
searchParams[ (index + 1) + "_property" ] = tokens[0];
searchParams[ (index + 1) + "_property.value" ] = tokens[1];
})
}
return searchParams;
}
}
CUI.PathBrowser.register('autocompleteCallback', searchBasedAutocompleteCallback());
//the option loader for requesting query results
function searchBasedOptionLoader() {
return {
name: EAEM_PREFIX + ".optionLoader",
handler: optionLoaderHandler
};
function optionLoaderHandler(searchParams, callback) {
var query = QUERY;
_.each(searchParams, function(value, key){
query = query + key + "=" + value + "&";
});
query = query.substring(0, query.length - 1);
console.log("EAEM - Search query - " + query);
$.get(query).done(handler);
function handler(data){
var results = [];
if(!_.isEmpty(data.hits)){
results = _.pluck(data.hits, "path");
}
if (callback){
callback(results);
}
}
return false;
}
}
CUI.PathBrowser.register('optionLoader', searchBasedOptionLoader());
//option renderer for creating the option html
function searchBasedOptionRenderer() {
return {
name: EAEM_PREFIX + ".optionRenderer",
handler: optionRendererHandler
};
function optionRendererHandler(iterator, index) {
var value = this.options.options[index];
return $('<li class="coral-SelectList-item coral-SelectList-item--option" data-value="'
+ value + '">' + value + '</li>');
}
}
CUI.PathBrowser.register('optionRenderer', searchBasedOptionRenderer());
}());