Skip to content

Instantly share code, notes, and snippets.

@lubiluk
Created July 12, 2012 15:58
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lubiluk/3099013 to your computer and use it in GitHub Desktop.
Save lubiluk/3099013 to your computer and use it in GitHub Desktop.
lift DataTable view
package code.view
import _root_.scala.xml.{ NodeSeq, Node, Elem, PCData, Text }
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._
import _root_.net.liftweb.json.JsonDSL._
import _root_.net.liftweb.json._
import scala.util.parsing.json.JSONArray
object DataTable {
def apply(
columns: List[String],
fun: (DataTableParams) => DataTableSource,
idOpt: Option[String],
jsonOptions: List[(String, String)],
attrs: (String, String)*) = new DataTable().render(columns, fun, idOpt, jsonOptions, attrs: _*)
def apply(
columns: List[String],
fun: (DataTableParams) => DataTableSource,
idOpt: Option[String],
attrs: (String, String)*) = new DataTable().render(columns, fun, idOpt, attrs: _*)
def apply(
columns: List[String],
fun: (DataTableParams) => DataTableSource,
id: String,
attrs: (String, String)*) = new DataTable().render(columns, fun, id, attrs: _*)
def apply(
columns: List[String],
fun: (DataTableParams) => DataTableSource,
id: String,
jsonOptions: List[(String, String)],
attrs: (String, String)*) = new DataTable().render(columns, fun, id, jsonOptions, attrs: _*)
}
abstract class DataTableSource(
val totalRecords: Long,
val totalDisplayRecords: Long) {
def jsonData: JValue
}
/**
* Class for holding data to be return to DataTable
*
* Use this class to return data as simple JSON array of arrays
*/
class DataTableArraySource(
totalRecords: Long,
totalDisplayRecords: Long,
val data: List[List[String]])
extends DataTableSource(totalRecords, totalDisplayRecords) {
def jsonData = JArray(data.map(r => JArray(r.map(JString(_)))))
}
/**
* Class for holding data to be return to DataTable.
*
* Use this class to return data as JSON array of objects, so you can set DT_RowId and DT_RowClass
*/
class DataTableObjectSource(
totalRecords: Long,
totalDisplayRecords: Long,
val data: List[List[(String, String)]])
extends DataTableSource(totalRecords, totalDisplayRecords) {
def jsonData = JArray(data.map(r => JObject(r.map(rr => JField(rr._1, JString(rr._2))))))
}
/**
* Class for holding params sent by DataTable
*/
class DataTableParams(
val displayStart: Long,
val displayLength: Long,
val columns: Int,
val search: String,
val regex: Boolean,
val searchable: List[Boolean],
val searches: List[String],
val regexes: List[Boolean],
val sortables: List[Boolean],
val sortingCols: Int,
val sortCols: List[Int],
val sortDirs: List[String],
val dataProps: List[String])
class DataTable {
/**
* Render a DataTable
*
* @param columns - Column names
* @param fun - function that will provide data
* @param id - Table id
* @param jsonOptions - additional options for DataTable configuration
* @param attrs - the attributes that can be added to the table
*/
def render(
columns: List[String],
fun: (DataTableParams) => DataTableSource,
id: String,
jsonOptions: List[(String, String)],
attrs: (String, String)*): Elem = {
val idOpt = Some(id)
render(columns, fun, idOpt, jsonOptions, attrs: _*)
}
/**
* Render a DataTable
*
* @param columns - Column names
* @param fun - function that will provide data
* @param id - Optional table id
* @param attrs - the attributes that can be added to the table
*/
def render(
columns: List[String],
fun: (DataTableParams) => DataTableSource,
id: String,
attrs: (String, String)*): Elem = {
val jsonOptions: List[(String, String)] = List()
val idOpt = Some(id)
render(columns, fun, idOpt, jsonOptions, attrs: _*)
}
/**
* Render a DataTable
*
* @param columns - Column names
* @param fun - function that will provide data
* @param idOpt - Optional table id
* @param attrs - the attributes that can be added to the table
*/
def render(
columns: List[String],
fun: (DataTableParams) => DataTableSource,
idOpt: Option[String],
attrs: (String, String)*): Elem = {
val jsonOptions: List[(String, String)] = List()
render(columns, fun, idOpt, jsonOptions, attrs: _*)
}
/**
* Render a DataTable
*
* @param columns - Column names
* @param fun - function that will provide data
* @param idOpt - Optional table id
* @param jsonOptions - additional options for DataTable configuration
* @param attrs - the attributes that can be added to the table
*/
def render(
columns: List[String],
fun: (DataTableParams) => DataTableSource,
idOpt: Option[String],
jsonOptions: List[(String, String)],
attrs: (String, String)*): Elem = {
val f = (ignore: String) => {
val columns = S.param("iColumns").dmap(0)(_.toInt)
val a = (1 to columns).map(i => S.param("bSearchable_" + i).dmap(false)(_.toBoolean)).toList
val params = new DataTableParams(
S.param("iDisplayStart").dmap(0)(_.toInt),
S.param("iDisplayLength").dmap(0)(_.toInt),
columns,
S.param("sSearch").dmap("")(_.toString),
S.param("bRegex").dmap(false)(_.toBoolean),
(1 to columns).map(i => S.param("bSearchable_" + i).dmap(false)(_.toBoolean)).toList,
(1 to columns).map(i => S.param("sSearch_" + i).dmap("")(_.toString)).toList,
(1 to columns).map(i => S.param("bRegex_" + i).dmap(false)(_.toBoolean)).toList,
(1 to columns).map(i => S.param("bSortable_" + i).dmap(false)(_.toBoolean)).toList,
S.param("iSortingCols").dmap(0)(_.toInt),
(1 to columns).map(i => S.param("iSortCol_" + i).dmap(0)(_.toInt)).toList,
(1 to columns).map(i => S.param("sSortDir_" + i).dmap("")(_.toString)).toList,
(1 to columns).map(i => S.param("mDataProp_" + i).dmap("")(_.toString)).toList)
val source = fun(params)
val json = ("iTotalRecords" -> source.totalRecords) ~
("iTotalDisplayRecords" -> source.totalDisplayRecords) ~
("sEcho" -> S.param("sEcho").dmap(0)(_.toInt)) ~
("aaData" -> source.jsonData)
JsonResponse(json)
}
fmapFunc(SFuncHolder(f)) { func =>
val where: String = encodeURL(S.contextPath + "/" + LiftRules.ajaxPath + "?" + func + "=foo")
val id = idOpt getOrElse Helpers.nextFuncName
val jqOptions = ("bProcessing", "true") ::
("bServerSide", "true") ::
("sAjaxSource", where.encJs) ::
Nil ::: jsonOptions
val json = jqOptions.map(t => t._1 + ":" + t._2).mkString("{", ",", "}")
val datatableOptions = JsRaw(json)
val onLoad = JsRaw("""
$(document).ready(function() {
$("#""" + id + """").dataTable(""" + datatableOptions.toJsCmd + """);
});""")
attrs.foldLeft(
<table id={ id }>
<head_merge>
<script type="text/javascript" src="/scripts/jquery.dataTables.js"></script>
{ Script(onLoad) }
</head_merge>
<thead>
<tr>
{ columns.map(c => <th>{ c }</th>) }
</tr>
</thead>
<tbody>
<tr>
{ columns.map(_ => <td></td>) }
</tr>
</tbody>
</table>)(_ % _)
}
}
}
@yobre
Copy link

yobre commented Feb 11, 2014

Hi Pawel,
I'm trying to implement a datatable with lift using the server processing.
I took inspiration from your snippet but I can not get it to work properly.
My test view is as follows:
def table = {
val cols = "Types value" :: "Actions" :: Nil

// this function returns data to populate table based on parameters passed in
val fun = (params: DataTableParams) => {
  val rows: List[(String, String)] = List(("1", "Actions"),
    ("2", "Actions"),
    ("3", "Actions"),
    ("4", "Actions"),
    ("5", "Actions"),
    ("6", "Actions"),
    ("7", "Actions"),
    ("8", "Actions"),
    ("9", "Actions"),
    ("10", "Actions"),
    ("11", "Actions"))

  val count = rows.length

  new DataTableObjectSource(count, count , rows.map( r =>
      List(("0", r._1),
            ("1", r._2),
            ("DT_RowId", "rowid_" + r._1)))
  )
}

DataTable(
  cols, // columns
  fun, // our data provider
  "my-table", // html table id
  List(("bFilter", "false")), // datatables configuration
  ("class", "table table-striped table-bordered")) // set css class for table

}

In this example, my goal is to display the first 10 rows and then display the eleventh by clicking on the second page of the paginator.
The problem is that I see 11 rows in both the first and in the second page.
The combo box, the paginator and the description at the bottom left work properly but the datatable never changes. What's wrong?
Thanks in advance.
Greetings

@lubiluk
Copy link
Author

lubiluk commented Feb 12, 2014

Hi Andrea,

First thing - you didn't configure your data table to display 10 rows per page. You need to set DisplayLength parameter in order to do that. (But I guess 10 is default so it may stay unconfigured)
Second and more important thing is that I can see that your function "fun" doesn't care about what "params" it gets. It should look at the displayStart and displayLength values and return data rows accordingly.

I know I didn't documented this thoroughly... yet.

Cheers,

@selbitschka
Copy link

Hi,
I just tried your DataTable View - its amazing - thanks a lot.
I think I found a little bug - dataTable parameters column counts start with 0 not 1, so I needed to adjust your parameter parsing to
...
(0 to columns-1).map(i => S.param("sSortDir_" + i).dmap("")(_.toString)).toList,
...

cheers
stefan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment