Skip to content

Instantly share code, notes, and snippets.

@sofoklis
Created February 8, 2012 08:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sofoklis/1766971 to your computer and use it in GitHub Desktop.
Save sofoklis/1766971 to your computer and use it in GitHub Desktop.
lift json autocomplete snippet
import net.liftweb.http._
import S._
import js._
import JsCmds._
import JE._
import net.liftweb.textile._
import net.liftweb.common._
import net.liftweb.util._
import Helpers._
import scala.xml._
import net.liftweb.json._
import net.liftweb.json.JsonParser._
import com.papasofokli.snippet.JsonAutocomplete._
import net.liftweb.json.Serialization.{ read, write }
class JsonAutocomplete extends Loggable {
/**
* Create the json call, the createJsonFunc takes a partial function that
* handles the incoming request.
*/
val (call : JsonCall, jsCmd : JsCmd) = S.createJsonFunc(receiveJson)
/**
* Partial function that takes care of the incoming request and redirects to the corresponding handler,
* Here you are matching directly on the json object defined bellow, you can have various case classes
* and respond accordingly for each request. Hhere i have only 2 kinds of json request, everything
* else goes to the log
*/
def receiveJson : PartialFunction[JsonAST.JValue, JsCmd] = {
case st : JsonAST.JValue ⇒ st.extractOpt[SearchTerm] match {
case Some(SearchTerm(term)) ⇒ findTerms(term)
case None ⇒ st.extract[IdNameCategory] match {
case idc : IdNameCategory ⇒ handleIdNameCategory(idc)
}
}
case any ⇒ logger.info("anything else goes to the log" + any.toString)
}
/**
* Create a javascript to call the function to call the server with the right parameters,
* unfortunately i cannot use the generated one since it doesn't provide the success function
*/
val functionDef = JsRaw("function " +
call.funcId + "(obj, onSuccess, onError) {liftAjax.lift_ajaxHandler('" +
call.funcId + "='+ encodeURIComponent(JSON.stringify(obj)), onSuccess, onError);}")
/**
* Add the implicit formats for the de/serialization
*/
implicit val formats = DefaultFormats
/**
* My simple find function simply returns the value i need to show on the client.
* Here i am using the fantastic lift json support to serialize the case class
* and send it. The only problem was i could not find a way to send back a json object.
*/
def findTerms(term : String) : JsCmd = {
val items = dropDownList.filter(_.value.contains(term)) take 10
val ast = net.liftweb.json.Extraction.decompose(items)
JsRaw(write(ast))
}
/**
* Simple handler, can write to database or anything else, now i am just sending an alert.
*/
def handleIdNameCategory(idc : IdNameCategory) : JsCmd = Alert("received " + idc.toString)
/**
* Render function simple replaces the script
*/
def render : CssSel = script
/**
* Register the callbacks on the autocomplete, very similar to the original code from
* jQueryUI autocomplete
*/
def script : CssSel = "#script" #> Script(functionDef & JsRaw("""
$(function() {
var items = [];
function log( message ) {
$( "<div/>" ).text( message ).prependTo( "#log" );
$( "#log" ).scrollTop( 0 );
}
$( "#city" ).autocomplete({
delay: 1000,
source: function( request, response ) {
// I had to do this so i can pass the success and on error functions
""" + call.funcId + """({'term': request.term },
//On Success
function(items){
// Unfortunately i had to make this hack since i could not return a json from my snippet
items = items.replace(/[\n;]/g, "");
response(JSON.parse(items));
},
//On error
function(){
items =[];
response(items)}
);
},""" +
"""
minLength: 2,
select: function( event, ui ) {
""" +
call.funcId + "(ui.item);" +
"""
},
open: function() {
$( this ).removeClass( "ui-corner-all" ).addClass( "ui-corner-top" );
},
close: function() {
$( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" );
}
});
});
"""))
}
object JsonAutocomplete {
/**
* Case classes to be used for the messages between the client and server, very easy to
* serialize and parse
*/
case class IdNameCategory(val label : String, val value : String, val category : String)
/**
* Simple query for a search in the snippet
*/
case class SearchTerm(val term : String)
/**
* Generate some items to check how it works
*/
val dropDownList = for (i ← 0 to 8000) yield IdNameCategory("item" + i, "item" + i, "cat" + (i % 5))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment