tags:

views:

74

answers:

2

For simplicity, imagine that I have a list of lists. I want to display a html table where there is a row for each element of the top-level list and each column in the row is an element of the child list.

So

List(List(1,2,3), List(4,5,6), List(7,8,9))

would result in an html table that is displayed like this:

1     2     3
4     5     6
7     8     9
10    11    12

Here's my attempt (the template)

    <table>
        <lift:viewQuery.res2>
            <tr>
            <a:row>
                <td><a:num/></td>
            </a:row>
            </tr>
        </lift:viewQuery.res2>
    </table>

And the relevant method in the snippet:

def res2(in :NodeSeq) : NodeSeq = {
  val data = List(List(1,2,3), List(4,5,6), List(7,8,9), List(10,11,12))

  def bindChild(child : List[Int],in :NodeSeq) = {
    child.flatMap(c => Helpers.bind("a", in,
                                    "num" -> c.toString))
  }
  data.flatMap(childList => Helpers.bind("a", in,
                                         "row" -> bindChild(childList, in)))
}

When i go to the page it gives me the following errors:

error on line 28 at column 23: Namespace prefix a on row is not defined
error on line 29 at column 31: Namespace prefix a on num is not defined
error on line 34 at column 23: Namespace prefix a on row is not defined
error on line 35 at column 31: Namespace prefix a on num is not defined
...

Any ideas on the best way to handle this?

A: 

If you don't need a Lift-specific answer, something like this could work

val data = List(List(1,2,3), List(4,5,6), List(7,8,9))
<table>{data.map(row => <tr>{row.map(col => <td>{col}</td>)}</tr>)}</table>

Your actual use case might be a bit more complex though, so this might not be applicable.

Kristian Domagala
Yeah, this works, i was hoping for something a bit cleaner.
jmo
+1  A: 

You are close. Try this:

<table>
    <lift:viewQuery.res2>
        <a:row>
        <tr>
            <b:cell><td><cell:num/></td></b:cell>
        </tr>
        </a:row>
    </lift:viewQuery.res2>
</table>

And for your snippet:

def res2(in :NodeSeq) : NodeSeq = {
  import Helpers._
  val data = List(List(1,2,3), List(4,5,6), List(7,8,9), List(10,11,12))

  def cell(x: List[Int])(template: NodeSeq) = 
    x.flatMap{y: Int => bind("cell", template, "num" -> y) }
  def row(x: List[List[Int]])(template: NodeSeq) = 
    x.flatMap {y =>  bind("b", template, "cell" -> { x: NodeSeq => cell(y)(x)} ) }

  bind("a", in, "row" -> { x: NodeSeq => row(data)(x) })
}

I've left in some types and been a bit more explicit so you can see what exactly is going on. Your problem was that you were using the same template in for every level, when what you wanted to do was progressively narrow down the template. So in the above template, we want everything inside of the <a:row> tags to repeat for each top level item of the list and then everything inside of <b:cell> to repeat for each Int. The way to do that is by creating a FuncBindParam which is what we're doing with the following line:

 "row" -> { x: NodeSeq => row(data)(x) }

Lift will then pass in the xhtml that was contained inside of the <a:row> tag as an argument to the curried function. And the flatMap calls inside of the curried functions take care of repeating the template as many times as are needed.

Aaron