The AutoComplete widget provides a flexible and accessible way to offer suggestions or some kind of filtering information as a user type text information in an input field . If you have used Google , you might see that Google starts providing autocomplete suggestion with entries depending on user’s current input .
Lift provides a very easy way to implement Autocomplete using Scala .
Here , I am going to explain two ways to implement Autocomplete Functionality in Lift using Scala .
1) Using this way , you can use Lift’s Autocomplete widget but with limited features .
Here is how we integrate it into your Lift application.
a)Initialisation
Add below line in Boot.Scala
import net.liftweb.widgets.autocomplete.AutoComplete
class Boot {
def boot {
...
AutoComplete.init
...
}
}
b) Snippet Binding
In order to implement in Snippet , follow below lines :-
"autocomplete" ->AutoComplete(startValue,
getResults _,
value => takeAction(value)
, List(("minChars" -> "3"))
/**
where:
startValue is a string for the initial value of the AutoComplete text field.
getResults is a function to populate the Autocomplete drop down results.
takeAction is a function called when the form is submitted, containing the value of the widget
**/
def getResults(current: String, limit: Int): Seq[String] = {
...
}
/**
where
current is the string typed into the autoComplete text field.
limit is an integer to limit the number of Strings returned by the function
**/
def takeAction(str : String) = {
println("Selected Value"+str)
}
2) If you have used Facebook , you might agree that at many places , Facebook provides filtered information in a very nice way . Using above way , you could not implement like this .
For this you have to extend Autocomplete.Scala and modify it . In one of my project , I implemented Auto complete widget in facebook style by modifying Autocomplete.Scala .
package it.autocompleteexample.lib {
import _root_.scala.xml.{ NodeSeq, Node, Elem, PCData, Text, Unparsed }
import _root_.net.liftweb.common._
import _root_.net.liftweb.util._
import _root_.net.liftweb.http._
import _root_.net.liftweb.http.js._
import JsCmds._
import JE._
import S._
import SHtml._
import Helpers._
object AutoComplete {
def apply(start: String,
options: (String, Int) => Seq[(String, String, String, String, String)],
onSubmit: String => Unit,
jsonOptions: List[(String, String)],
handler: JsCmd,
attrs: (String, String)*) = new AutoComplete().render(start, options, onSubmit, jsonOptions, handler, attrs: _*)
def init() {
import _root_.net.liftweb.http.ResourceServer
ResourceServer.allow({
case "autocomplete" :: _ => true
})
}
}
class AutoComplete {
def render(start: String,
options: (String, Int) => Seq[(String, String, String, String, String)],
onSubmit: String => Unit,
jsonOptions: List[(String, String)],
handler: JsCmd,
attrs: (String, String)*): Elem = {
val f = (ignore: String) => {
val q = S.param("q").openOr("")
val limit = S.param("limit").flatMap(asInt).openOr(10)
PlainTextResponse(options(q, limit).map(item => item._1 + "|" + item._2 + "|" + item._3 + "|" + item._4 + "|" + item._5).mkString("\n"))
}
fmapFunc(SFuncHolder(f)) { func =>
val what: String = encodeURL(S.contextPath + "/" + LiftRules.ajaxPath + "?" + func + "=foo")
val id = Helpers.nextFuncName
fmapFunc(SFuncHolder(onSubmit)) { hidden =>
/* merge the options that the user wants */
val jqOptions = ("minChars", "0") ::
("matchContains", "true") :: ("delay", "600") ::
("max", "20") ::
Nil ::: jsonOptions
val json = jqOptions.map(t => t._1 + ":" + t._2).mkString("{", ",", "}")
val autocompleteOptions = JsRaw(json)
val onLoad = JsRaw("""
jQuery(document).ready(function(){
var data = """ + what.encJs + """;
jQuery("#""" + id + """").autocomplete(data, """ + autocompleteOptions.toJsCmd + """).result(function(event, dt, formatted) {
jQuery("#searchValue").val(formatted);
jQuery("#""" + id + """").val(dt[1]);
if(dt[0] == "-1"){
jQuery("#searchValue").val(dt[0]);
jQuery("#""" + id + """").val(dt[1]);
}else if(dt[0] == ""){
jQuery("#searchValue").val(dt[0]);
jQuery("#""" + id + """").val(dt[0]);
} """ + handler.toJsCmd + """
});
});""")
<span>
<head>
<link rel="stylesheet" href={ "/" + LiftRules.resourceServerPath + "/autocomplete/jquery.autocomplete.css" } type="text/css"/>
<script type='text/javascript' src='js/jquery.autocomplete.js'></script>
<script type="text/javascript">{ Unparsed(onLoad.toJsCmd) }</script>
</head>
{
attrs.foldLeft(<input type="text" id={ id } value={ start } placeholder={"Add "+category.getOrElse("")+" ...."} />)(_ % _)
}<input type="hidden" name="searchValue" id="searchValue" value=""/>
<input type="hidden" name={ hidden } id={ hidden } value={ start }/>
</span>
}
}
}
}
}
In the Snippet :
AutoComplete("", getResults _, value => takeAction(value)
, List(("minChars" -> "3"),
("formatItem", """function(row, i, max) { return "<table><tr><td><img src='" + row[2] + "' width='40' height='50' /> </td><td>" + row[1]+"<br>"+row[3]+"<br>"+row[4]+"</td><tr><table>"; }""")), SHtml.ajaxCall(
JE.ValById("searchValue"), term => {
SHtml.ajaxInvoke(() => {
takeActionForItem(term)
})._2.cmd
})._2.cmd)
def getResults(current: String, limit: Int): Seq[(String, String, String, String, String)] = {
...
}
In the above code , getResults is returning sequence of 5 strings . I have modified Autocomplete.Scala as per requirement . You can modified this file , if you want to make your Autocomplete widget to be more attractive .
Lift’s Auto complete Widget is basically a JQuery’s AutoComplete widget . You can use Jquery’s Autocomplete property in Autocomplete.Scala .








What is the HTML side of this code to make it all work?
Hey Joe ,
You have to call AutoCompleteSnippet class in your html .