What would be a clean way to implement a simple CRUD interface in Lift and make it
Lets suppose we have a view
<table data-lift="CrudList">
<tr>
<td role="data">Item goes here</td>
<td><button role="remove" type="button">remove</button></td>
</tr>
<tr class="clearable">
<td>Item two here</td>
<td><button type="button">remove</button></td>
</tr>
<tr class="clearable">
<td>Item three!</td>
<td><button type="button">remove</button></td>
</tr>
</table>
<form data-lift="form.ajax">
<div data-lift="CrudList.create">
<input type="text" name="text"></input>
<button type="submit"></button>
</div>
</form>
And a snippet
object CrudList {
def render = {
def remove(item: String) = () => {
ListDAO.remove(item)
JE.JsRaw("""Some JavaScript to remove <tr> from the UI""")
}
ClearClearable &
"tr *" #> ListDAO.all.map(item => {
"role=data" #> item &
"role=remove" #> ajaxInvoke(remove(item))
})
}
def create = {
var text = ""
def process(): JsCmd = {
val item = ListDAO.create(text)
JsCmds.Noop // TODO: replace this with some JsCmd
// that will create and populate new table row in the UI
// without polluting the snippet with markup
}
"@text" #> SHtml.text(text, s => text = s) &
"button *+" #> SHtml.hidden(process)
}
}
Example might have bugs, purely for demonstration.
The render
snippet is straightforward - we modify to existing markup and render our list as table rows.
I'm a bit hesitant to complete the create
snippet. The code that persists the list item is straightforward, but I don't know how to approach the part that updates the <table>
with new <tr>
. I'd like to avoid polluting the snippet with markup leaving room for the designer to do with the table what they want. How would you complete this snippet?
The easiest way to do this would be to just swap out the entire HTML table. To do that, you can use a built in function in SHtml
that will memoize the initial transformation.
To do that, we'd give table
an ID like:
<table data-lift="CrudList" id="mytable">
Then in your snippet, you could do:
object CrudList {
object tableMemo extends RequestVar[Box[IdMemoizeTransform]](Empty)
def render = {
def remove(item: String) = () => {
ListDAO.remove(item)
tableMemo.get.foreach{ _.setHtml }
}
"#mytable" #> SHtml.idMemoize{ memo =>
tableMemo(memo)
ClearClearable &
"tr *" #> ListDAO.all.map(item => {
"role=data" #> item &
"role=remove" #> ajaxInvoke(remove(item))
})
}
}
def create = {
var text = ""
def process(): JsCmd = {
val item = ListDAO.create(text)
tableMemo.get.foreach{ _.setHtml }
}
"@text" #> SHtml.text(text, s => text = s) &
"button *+" #> SHtml.hidden(process)
}
}
Any call to tableMemo.get.foreach{ _.setHtml }
will reRender the table provided the first render took place and set the RequestVar
.
If you are looking to only reRender the affected rows, that gets a bit more challenging.
I would probably try something like this:
First, create a template with the HTML for a given row. In this example, we'll put it in templates-hidden/rowtemplate.html
. With the content:
<tr>
<td role="data">Item goes here</td>
<td><button role="remove" type="button">remove</button></td>
</tr>
Then, we'll modify the render to give each tr and retrieve the row from the template
val rowTemplate = Templates("templates-hidden" :: "rowtemplate" :: Nil) openOr <tr></tr>
def render = {
def remove(item: String) = () => {
ListDAO.remove(item)
JsCmds.Run("$('#' + item.id).remove()")
}
ClearClearable &
"tr" #> {
"tr" #> ListDAO.all.map(item => {
"* [id]" #> item.id &
"role=data" #> item &
"role=remove" #> ajaxInvoke(remove(item))
})
}.apply(rowTemplate)
}
Note: The first <tr>
above will bind to the TR in your html, the second will bind to the TR specified in the template.
def create = { var text = ""
def process(): JsCmd = {
val item = ListDAO.create(text)
val rowNS = {
"* [id]" #> item.id &
"role=data" #> item &
"role=remove" #> ajaxInvoke(remove(item))
}.apply(rowTemplate)
JsCmds.Run("tr:last").append(rowNS.toString)
}
"@text" #> SHtml.text(text, s => text = s) &
"button *+" #> SHtml.hidden(process)
}
I haven't tested that to make sure it all works, but hopefully will point you in the right direction.