How to use pattern matching in Binding.scala? - scala

I have the following model:
case class CarBinding(ownerId: Var[String], specs: Var[Option[Specs]])
Specs is a trait and has the following concrete types:
trait Specs {
def name: String
}
case class SportsCarSpecs(name: String, details: Details) extends Specs
In my Scala.js app, I now want to create a table and list all the entries:
#dom
def buildTable(): Binding[BindingSeq[Node]] = {
val data = Vars.empty[CarBinding]
/* Initial population. */
// Some code...
<br/>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th class="col-md-1">
<small>Owner ID</small>
</th>
<th class="col-md-1">
<small>Specs</small>
</th>
</tr>
</thead>
<tbody>
{for (entry <- data) yield {
<tr>
<td>
<small>
{entry.ownerId.bind}
</small>
</td>
<td>
<small>
{entry.specs.bind match {
case Some(SportsCarSpecs(name, details)) => {name} <span>{details.ps}</span>
case _ => -
}}
</small>
</td>
</tr>
}}
</tbody>
</table>
</div>
}
However, I get the following error:
';' expected but $XMLSTART$< found.
[error] case Some(SportsCarSpecs(name, details)) => {name} <span>{details.ps}</span>
What am I doing wrong?

This:
entry.specs.bind match {
case Some(SportsCarSpecs(name, details)) => {name} <span>{details.ps}</span>
case _ => -
}
isn't a valid expression, so you can't interpolate it in the XML literal. If you had complete XML expressions in both branches, it should work. So the easiest fix I can see is to pull <small> inside:
<td>
{entry.specs.bind match {
case Some(SportsCarSpecs(name, details)) => <small>{name} <span>{details.ps}</span></small>
case _ => <small>-</small>
}}
</td>

Related

Lift - Binding list of values to template

Here is my template -
<div class="lift:Admin.showInvitees">
<tr class="success"> <!-- "success" "error" -->
<td><invitee:name></invitee:name></td>
<td><invitee:description></invitee:description></td>
<td><invitee:status></invitee:status></td>
<td></td>
</tr>
</div>
and here is the snippet -
class Admin {
def showInvitees(in: NodeSeq): NodeSeq = {
val invitees = Invitation.allInvitations
invitees.flatMap{invitee => bind("invitee", in, "name" -> invitee.name.is, "status" -> invitee.status.is, "description" -> invitee.description.is)}
}
}
I checked this question and used the solution. But it's not working for me. Values are not getting bound at all. This is what I get as output -
<tr class="success">
<td><invitee:name></invitee:name></td>
<td><invitee:description></invitee:description></td>
<td><invitee:status></invitee:status></td>
</tr>
Need help binding a list of values to template.
After the first answer I tried this as well -
def showInvitees(in: NodeSeq): NodeSeq = {
val invitees = Invitation.allInvitations
(".invitees" #> invitees.map{ inv =>
".invitee-name" #> inv.name.is &
".invitee-description" #> inv.description.is &
".invitee-status" #> inv.status.is
})(in)
}
Even this is not working-
Template -
<div class="lift:Admin.showInvitees">
<tr class="invitees success"> <!-- "success" "error" -->
<td><span class="invitee-name"></span></td>
<td><span class="invitee-description"></span></td>
<td><span class="invitee-status"></span> </td>
<td></td>
</tr>
</div>
This is the old syntax for binding values. Try the new one:
// snippet
class Admin {
def showInvitees = {
case class Invitee(name: String, descr: String, status: String)
val invitees = List(Invitee("Alex", "for cookies", "ok"), Invitee("Bob", "for beer", "kicked out"))
".invitee" #> invitees.map { i =>
".invitee-name" #> i.name.is &
".invitee-descr" #> i.description.is &
".invitee-status" #> i.status.is
}
}
}
// html
<div class="Admin.showInvitees">
<table>
<tr class="invitee success">
<td><span class="invitee-name"></span></td>
<td><span class="invitee-descr"></span></td>
<td><span class="invitee-status"></span></td>
</tr>
</table>
</div>
// result
<table>
<tbody><tr class="invitee success">
<td>Alex</td>
<td>for cookies</td>
<td>ok</td>
</tr><tr class="invitee success">
<td>Bob</td>
<td>for beer</td>
<td>kicked out</td>
</tr>
</tbody></table>

Lift framework: Not able to render table rows using comet properly

In my web page I have a text box in which user enters an item name that would be sent to server asynchronously, that message would be sent to CometActor then further it adds additional information to it and in the render method it tries to append a new row to existing table on the page asynchronously. Whenever I add an item always new row is getting placed on top of table rather than as table row, after that if I press refresh it is getting placed properly in the table. Here is
<table cellpadding="0" cellspacing="0" border="1" class="display" id="example">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody class="lift:comet?type=ProvisionComet">
<tr id="tr_content">
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
LiftActor object
object ItemsServer extends LiftActor with ListenerManager {
private var items = Vector[Item]()
private lazy val date: Box[Date] = DependencyFactory.inject[Date] // inject the date
def createUpdate = items
override def lowPriority = {
case s: String => items :+= new Item(items.length + 1, s, date.map(_.toString)); updateListeners()
case _ => None
}
}
Comet Actor
class ItemComet extends CometActor with CometListener {
private var items = Vector[Item]()
def registerWith = ItemsServer
override def lowPriority = {
case v: Vector[Item] => items = v; reRender()
}
def render = "#tr_content *" #> {
items.map(i => {
<td>{i.sNum}</td>
<td>{i.itemName}</td>
<td>{i.updatedTime.toString}</td>
})
}
The result before refresh is
The result after refresh
How to get the correct result without refreshing?
render method is the place where I am adding rows
Try moving the class attribute from tbody to table:
<table class="lift:comet?type=ProvisionComet" cellpadding="0" cellspacing="0" border="1" class="display" id="example">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody >
<tr id="tr_content">
<td></td>
<td></td>
<td></td>
</tr>
</tbody>

Scala Lift: how to get ajaxRadio parameters

I have ajaxRadio ineach row of a table.
when this Radio is selected, I want scala function to get two parameters:
1st one is the selected radio, 2nd one is the data key of the row.
my source is as follows:
val radioList = List("wideArea", "oldArea","newArea")
def liftForm(xhtml: NodeSeq): NodeSeq = {
var areaList = newOrderList.map(values =>{
<li id={values(1)} >
<table>
<thead></thead>
<tbody>
<tr class = "real">
<td class="listImage">
<img class="areaImage" src={values(6)}/>
</td>
<td style="vertical-align:top;">
<tr><p class="areaName">{values(4)}</p></tr>
<tr><p class="areaComment">{values(5)}</p></tr>
</td>
<td class="listCheck" >
{ajaxButton(S.?("delete"), () => doDelete(values(1)), "class" -> "button delete")}
</td>
</tr>
<tr>
<td></td>
<td>
{
val it = ajaxRadio[String](radioList,Box.legacyNullTest(values(2)),doRadioChange _).toForm.grouped(4)
for(i <- it)yield(<tr>{i.flatMap(y => <td> {y} </td>)}</tr>)
}
</td>
</tr>
</tbody>
</table>
</li>})
bind("list",xhtml,"areaList" -> <ul>{areaList}</ul>)
By doRadioChange _, I can only get selected radio. How can I get the the 2nd parameter: data key of the row. ie values(1)?
Your functions can close over all of the local state at that point in the function. You should be able to replace doRadioChange _ with:
doRadioChange(_, values(1))
Assuming, of course, that doRadioChange accepts the proper parameters.

Variable within scala template in play framework

I need to be able to declare variables and after some markup later I
need to reference them. In order to accomplish this, this is
simplified version of my scala template:
#(map1:
java.util.LinkedHashMap[String,java.util.LinkedHashMap[String,Object]])
#import scala.collection.JavaConversions._
#import play.Logger
#for( (key,value) <- map1) {
<div>
#{
val rmap = Foo.someMethod(value)
val baz = rmap.getOrElse("baz", null)
<table border="0" cellpadding="0" cellspacing="0" >
<tbody>
<tr>
<td rowspan="3">
<div class="bar">
#baz
</div>
</td>
</tr>
</tbody>
</table>
}
</div>
}
Is above valid scala template and if not how can I declare baz and
reference it later in the markup?
I am using 1.2.2RC2 and scala 0.9.1
I was curious so did some digging. See https://groups.google.com/forum/#!topic/play-framework/Mo8hl5I0tBQ - there is no way at the moment, but an interesting work-around is shown. Define utils/Let.scala:
package utils
Object Let {
def let[A,B](a:A)(f:A=>B):B = f(a)
}
and then
#import utils.Let._
#let(2+3){ answer =>
#answer <hr> #answer
}
It's a very functional way of handling it, but then, what'd you expect in Scala :)
You can just use a for comprehension:
#for( (key,value) <- map1;
rmap = Foo.someMethod(value);
baz = rmap.getOrElse("baz", null)
) {
<div>
<table border="0" cellpadding="0" cellspacing="0" >
<tbody>
<tr>
<td rowspan="3">
<div class="bar">
#baz
</div>
</td>
</tr>
</tbody>
</table>
</div>
}
... and if you don't have anything you need to loop over, you can just say #for(i <- List(1); <declare variables>){<html here>}

password input custom

Greetings
In my User.scala file:
I'm using the next class to customize the password input
The code is the next
import net.liftweb.mapper.MappedPassword
[...]
class User extends MegaProtoUser[User] {
[...]
override def _toForm: Box[NodeSeq] = {
S.fmapFunc({s: List[String] => this.setFromAny(s)}){funcName =>
Full(
<tr>
<td>{S.??("repeat")}</td>
<td><input id={fieldId} type='password' name={funcName} value={is.toString}/> </td>
</tr>
<tr>
<td>{S.??("repeat")}<td>
<td><input type='password' name={funcName} value={is.toString}/></td>
</tr>
)
}
}
[...]
}
the compiler displays the following error:
[ERROR] ....../org/santix/model/User.scala:209: error: value setFromAny is not a member of org.santix.model.User
[INFO] S.fmapFunc({s: List[String] => this.setFromAny(s)}){funcName =>
anyone have any idea?
There's just too little information to answer, but I can see some points here...
First, I imagine you might be copying from some example, since you use this.setFromAny. The code containing this snippet is inside the class User, which extends MegaProtoUser[User]. Alas, MegaProtoUser does not have any method setFromAny, and I imagine your own class User doesn't either, and this is the primary cause of the error.
Now, setFromAny is part of the class MappedPassword, which you import, but doesn't seem to use in any place, so I don't know why you imported it. However, I can see that MegaProtoUser has a member which returns a MappedPassword. This member is called password, so I imagine this might work:
S.fmapFunc({s: List[String] => this.password.setFromAny(s)}){funcName =>
PROBLEM SOLVED!!!!
class User extends MegaProtoUser[User] {
...
override lazy val password = new MyPassword(this) {
override def _toForm: Box[NodeSeq] = {
S.fmapFunc({s: List[String] => this.setFromAny(s)})
{funcName =>
Full(
<tr>
<td>{ passwordDisplayName }</td>
<td>
<input id={fieldId} type='password' name={funcName} value=''/>
<!-- by sanx, value={is.toString} -->
</td>
</tr>
<tr>
<td>{S.??("repeat")}</td>
<td>
<input type='password' name={funcName} value=''/>
</td>
</tr>
)
}
}
}
}