AEM 62 - Touch UI Search based Pathbrowser for Autocomplete

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());
}());



2 comments:

  1. Hi Sreekanth,

    I have seen many of your articles and its very good that mainly on customization in AEM. But I am not understanding the exact code logic how it written.

    May I know, if any document or anything else, that I can learn.

    Thanks,
    Ratna.

    ReplyDelete
  2. I tried to use but it was only finding if full word....I built in *searchterm* so it will find contains. Makes it work better.

    ReplyDelete