Forum Discussion

DougS's avatar
DougS
Khoros Oracle
14 years ago

Ajax search Before Post (Freemarker)

 

Description

This example shows how to create a Freemarker component that makes an AJAX call to the Lithium REST API using JQuery to render a list of messages that match a search word after you've typed your message subject and have taken the cursor out of the search box.

 

Some important points that this example demonstrates:

  • How to use the <@liaAddScript> directive to add javascript to the page that will render below Lithium core JavaScript
  • The proper way to namespace your JavaScript and guard against collisions with other JavaScript on the page
  • How to make an AJAX call to the Lithium REST API

Requirements

Lithium version 9.18 or higher, Lithium Studio

 

 

How to add this to a Lithium community

There are two parts to adding an AJAX search to a page:

  • Add a text key to be passed into the AJAX search component.
  • Create the custom component.

To add a text key:

 

  1. Go to Studio > Text Editor.
  2. Click Search.
    This empty search opens the list of text keys that you can edit.
  3. In the Text Properties area, enter this custom text key.

    custom.component.ajax_search_before_post.results_label = You may be interested in these similar messages:
  4. Click Save.

 

To add a custom component:

  1. In Studio, go to the Components tab.
  2. Click New Component to create a new component. Enter a name and click Save.
    For example, ajax-search-before-post.
  3. Add the following markup to the component and click Save.

 

<#assign result_list_size = 3 />
<#assign subject_input_class = "lia-form-subject-input" />
<#assign results_label = text.format("custom.component.ajax_search_before_post.results_label") />

<@liaAddScript>
;(function($) {
if (typeof LITHIUM.CUSTOMER == "undefined") {
LITHIUM.CUSTOMER = {};
}
if (typeof LITHIUM.CUSTOMER.EXAMPLE == "undefined") {
LITHIUM.CUSTOMER.EXAMPLE = {};
}
LITHIUM.CUSTOMER.EXAMPLE.AjaxSearch = function() {
$(document).ready(function() {
$(".${subject_input_class}").each(function(index, subject_input) {
var subject_input = $(subject_input);
subject_input.parent().append("<div class='ajax-message-subject-search-results'></div>");

subject_input.blur(function() {
var subj_input = $(subject_input);
var subjectValue = subj_input.val();

if (subjectValue.length > 0) {
$.ajax({
type: "GET",
url: "/${community.id}/restapi/vc/search/messages",
data&colon; ({
"q":subjectValue,
"page_size":"${result_list_size}",
"restapi.response_style":"view",
"xslt":"json.xsl"
}),
success: function(result) {
$(subj_input).parent().find(".ajax-message-subject-search-results").each(function(index, results_div) {
if (result.response.messages == null || result.response.messages.message.length < 1) {
results_div.empty();
} else {
var msgMarkup = "<div><span>${results_label}</span></div>";
msgMarkup += "<table class='lia-list-wide'>";
$.each(result.response.messages.message, function(index, msg) {
msgMarkup += "<tr><td><a href='" + msg.view_href + "'>" + msg.subject.$ + "</a></td></tr>";
});
msgMarkup += "</table>";
$(results_div).empty().append(msgMarkup);
}
});
}

});
}
});
});
});
};
})(LITHIUM.jQuery);

LITHIUM.CUSTOMER.EXAMPLE.AjaxSearch();
</@liaAddScript>

Example

Go to the Code Sample 1 Example Page to see this in action.

 

  • Doug, thanks for this example.  We implemented it, but ran into some problems.  We fixed them, and I want to share the modified code.

     

    <#assign result_list_size = 3 />
    <#assign subject_input_class = "lia-form-subject-input" />
    <#assign results_label = text.format("custom.component.ajax_search_before_post.results_label") />

    <@liaAddScript>
    ;(function($) {
    if (typeof LITHIUM.CUSTOMER == "undefined") {
    LITHIUM.CUSTOMER = {};
    }
    if (typeof LITHIUM.CUSTOMER.EXAMPLE == "undefined") {
    LITHIUM.CUSTOMER.EXAMPLE = {};
    }
    LITHIUM.CUSTOMER.EXAMPLE.AjaxSearch = function() {
    $(document).ready(function() {
    $(".${subject_input_class}").each(function(index, subject_input) {
    var subject_input = $(subject_input);
    subject_input.parent().append("<div class='ajax-message-subject-search-results'></div>");

    subject_input.blur(function() {
    var subj_input = $(subject_input);
    var subjectValue = subj_input.val();

    if (subjectValue.length > 0) {
    $.ajax({
    type: "GET",
    url: "/${community.id}/restapi/vc/search/messages",
    data: ({
    "q":subjectValue,
    "page_size":"${result_list_size}",
    "restapi.response_style":"view",
    "xslt":"json.xsl"
    }),
    success: function(result) {
    $(subj_input).parent().find(".ajax-message-subject-search-results").each(function(index, results_div) {
    if (result.response.messages == null || result.response.messages.message.length < 1) {
    $(results_div).empty();
    } else {
    var msgMarkup = "<div><span>${results_label}</span></div>";
    msgMarkup += "<table class='lia-list-wide'>";
    if (typeof result.response.messages.message.length === 'undefined') {
                                                    msgMarkup += "<tr><td><a href='" + result.response.messages.message.view_href + "'>" + result.response.messages.message.subject.$ + "</a></td></tr>";
    } else {
    $.each(result.response.messages.message, function(index, msg) {
    msgMarkup += "<tr><td><a href='" + msg.view_href + "'>" + msg.subject.$ + "</a></td></tr>";
    });
    }
    msgMarkup += "</table>";
    $(results_div).empty().append(msgMarkup);
    }
    });
    }

    });
    }
    });
    });
    });
    };
    })(LITHIUM.jQuery);

    LITHIUM.CUSTOMER.EXAMPLE.AjaxSearch();
    </@liaAddScript>

    The typeof conditional check is needed there because the json XSLT returns a single element as the object itself instead of a one-element array.  I think the XSLT should be updated so that all results are returned as arrays.  This will help avoid special casing like this.

    • xorrkaz's avatar
      xorrkaz
      Genius

      At LiNC this year, it was asked how to enhance this version to support an idle time lookup instead of an onBlur lookup.  We have done this in our Tech Zone implementation.  Here are the mods to the version I posted last.  This version will initiate a search after 750 ms of idle time (as well as on blur).

       

      <#assign result_list_size = 3 />
      <#assign subject_input_class = "lia-form-subject-input" />
      <#assign results_label = text.format("custom.component.ajax_search_before_post.results_label") />

      <@liaAddScript>
      ;(function($) {
      if (typeof LITHIUM.CUSTOMER == "undefined") {
      LITHIUM.CUSTOMER = {};
      }
      if (typeof LITHIUM.CUSTOMER.EXAMPLE == "undefined") {
      LITHIUM.CUSTOMER.EXAMPLE = {};
      }
      LITHIUM.CUSTOMER.EXAMPLE.AjaxSearch = function() {
      $(document).ready(function() {
      $(".${subject_input_class}").each(function(index, subject_input) {
      var subject_input = $(subject_input);
      var idleTime = 0;
      var searchable = false;
      var last_search = "";
      subject_input.parent().append("<div class='ajax-message-subject-search-results'></div>");

      var idleInterval = setInterval(function() {
                              timerIncrement(subject_input);
                          }, 250);

      $(subject_input).keyup(function(e) {
      idleTime = 0;
      searchable = true;
      });

      function incrementTimer() {
      idleTime++;
      if (idleTime > 2 && searchable && last_search != subject_input.val()) {
                                  $(subject_input).trigger("myIdleEvent", [ subject_input ]);
                              }
                          }

      subject_input.bind('blur myIdleEvent', function(e, field, value) {
      var subj_input = $(subject_input);
      var subjectValue = subj_input.val();
      searchable = false;

      if (subjectValue.length > 0) {
      $.ajax({
      type: "GET",
      url: "/${community.id}/restapi/vc/search/messages",
      data: ({
      "q":subjectValue,
      "page_size":"${result_list_size}",
      "restapi.response_style":"view",
      "xslt":"json.xsl"
      }),
      success: function(result) {
      $(subj_input).parent().find(".ajax-message-subject-search-results").each(function(index, results_div) {
      if (result.response.messages == null || result.response.messages.message.length < 1) {
      $(results_div).empty();
      } else {
      var msgMarkup = "<div><span>${results_label}</span></div>";
      msgMarkup += "<table class='lia-list-wide'>";
      if (typeof result.response.messages.message.length === 'undefined') {
      msgMarkup += "<tr><td><a href='" + result.response.messages.message.view_href + "'>" + result.response.messages.message.subject.$ + "</a></td></tr>";
      } else {
      $.each(result.response.messages.message, function(index, msg) {
      msgMarkup += "<tr><td><a href='" + msg.view_href + "'>" + msg.subject.$ + "</a></td></tr>";
      });
      }
      msgMarkup += "</table>";
      $(results_div).empty().append(msgMarkup);
      }
      });
      }

      });
      }
      });
      });
      });
      };
      })(LITHIUM.jQuery);

      LITHIUM.CUSTOMER.EXAMPLE.AjaxSearch();
      </@liaAddScript>

       

      • iftomkins's avatar
        iftomkins
        Maven

        xorrkaz Thanks for the awesome contributions. It's been 3 years, so before I start trying to implement your example, just wanted to see if you could think of a reason why it wouldn't work currently? Thanks!

    • DougS's avatar
      DougS
      Khoros Oracle
      Ah, good catch -- thanks for the fix xorrkaz!
  • Hey Doug -

     

    I'm trying to apply some jQuery plug-ins on some REST, but I'm having issues. The plug-ins work properly with just normal text, but not with the REST calls. Here is a sample below. I'm using the <@liscript> you mentioned above....Any ideas?

     

     <script src="/html/assets/jquery-1.4.4.min.js" type="text/javascript"></script>
     <script src="html/assets/jquery.jtruncate.pack.js" type="text/javascript"></script>

    <@liaAddScript>

    $(document).ready(function() {  

        $('#truncTest').jTruncate({  
            length: 25,  
            minTrail: 20,  
            moreText: "[see all]",  
            lessText: "[hide extra]",  
            ellipsisText: " ...",  
            moreAni: "fast",  
            lessAni: 2000  
        });  
        $('#desc').jTruncate({  
            length: 20,  
            minTrail: 20,  
            moreText: "[see all]",  
            lessText: "[hide extra]",  
            ellipsisText: " ...",  
            moreAni: "fast",  
            lessAni: 2000  
        }); 
    });
    (LITHIUM.jQuery);
    </@liaAddScript>


    <!-- This one below works just fine -->
    <span id="truncTest">TEST TEST TEST TEST TEST TEST TEST EST TEST TEST TEST TEST TEST TEST EST TEST TEST TEST TEST </span>

    <!-- This one doesn't -->
    <#assign count = rest("/categories/id/${category.id}/boards/count").value />    
    <h3 id="desc" style="margin-bottom:7px; font-size:12px;">${description}</h3>

    • catherine_OTX's avatar
      catherine_OTX
      Guide

      My mistake Doug! Turns out I was using the id tag when I should have been using the class tag. Thanks for your patience! Thanks, Catherine

  • Hello, I think I found a bug with liaAddScript and foreign language characters. I am trying to pass a french string into a component through REST and I am getting this error:

     

    Freemarker template 'home-news' processing failed:
    MissingFormatArgumentException:Format specifier 'C'

     

    Here is the code I was using:

     

    <@liaAddScript>
    $(document).ready(function() {

      $('.ac_body').jqueryPlugin({

          blogLink:'${latestPost.@view_href}' });

    });
    </@liaAddScript>

     

    When I use a regular script tag with javascript as the language it renders correctly. Any ideas?

     

    Thanks for your time!
    Catherine

    • DougS's avatar
      DougS
      Khoros Oracle
      not sure exactly what's happening here Catherine -- what does latestPost.@view_href render when the error occurs? Maybe you can assign it to a variable first and then render it to see?
    • DougS's avatar
      DougS
      Khoros Oracle
      The example pages were removed as part of a refresh a while back. I've added them back to our stage site -- as soon as the next Lithosphere production push happens (not sure when that will be) it should be back.