views:

201

answers:

2

In a Lift application, I’d like to add a special tag which takes the <tbody> part of the next table and adds odd and even classes (for example) to each <tr> tag. Alternating, of course. While I have found a way to add another attribute to all <tr> tags, there are still a few problems left (see code below).

First, it doesn’t work. cycle.next is called too often, so in the end, everything is an odd row. Other problems are that the code doesn’t exclude inner tables (so a nested <tr> would be transformed as well) and that it also affects the <thead> part of the table.

Ideas to make this code work? (Of course, if there already is a lift-based solution – without jQuery – for this, I’ll gratefully take it.)

// Helper class for looping
class Loop(val strs: String*) {
    val stream_iter = Stream.continually(strs.toStream).flatten.iterator
    def next = stream_iter.next
}

val cycle = new Loop("even", "odd")

val rr = new RewriteRule {
  override def transform(n: Node): Seq[Node] = n match {
    // match any Elem
    case elem : Elem => elem match {
        // If it’s a <tr> do some further processing
        case Elem(_, "tr", att @ _, _, _*) => 
            elem % Attribute(None, "class", Text(
                // add the attribute and concatenate with others
                List(att.get("class").getOrElse("").toString, cycle.next).reduceLeft(_+" "+_).trim
                ), Null) toSeq
        case other => other
    }
    case other => other
  }
}

val rt = new RuleTransformer(rr)

val code = <table>
  <thead><tr><td>Don’t</td><td>transform this</td></tr></thead>
  <tbody>
    <tr class="otherclass">
      <td>r1c1</td><td>r1c2</td>
    </tr>
    <tr>
      <td>r2c1</td><td>r2c2</td>
    </tr>
    <tr>
      <td>r3c1</td><td>r3c2</td>
    </tr>
  </tbody>
</table>

println(rt(code))
+1  A: 

There are 2 different ways that I accomplish alternate table row classes:

1 - Use the jQuery colorize plugin

2 - Use zipWithIndex when looping through your data to create the table:

doQuery.zipWithIndex.map{ case (log, i) => {  
    <tr class={if (i % 2 == 0) "even" else "odd"}>  
        <td>Data</td>  
    </tr>  
}  
Tim N
As I’ve said, I don’t want to use jQuery for this. Also, I don’t want to create indices on the server side because I think, odd/even rows are something the template designer needs to care about and not the programmer. (The programmer might create the template tag to do so, though.)
Debilski
A: 

The problem with RewriteRules seems to be that they nest too deeply. That is, once a rule for adding attributes to <tr> is started, it is not possible to stop it. (At least, it did not work for me.) However, I have found a recursive solution which works for me. Also, as long as there is a <tbody> inside, the recursion will stop early. If there isn’t, we might have a problem…

abstract class Loop {
    val stream_iter = Stream.continually(elems.toStream).flatten.iterator
    def next = stream_iter.next
    def elems: Seq[String]
}
class Cycle extends Loop { override def elems = List("odd", "even") }

// Call this when in <tbody>
def transformChildren(sn: Seq[Node]): Seq[Node] = {
    // Start a new cycle
    val cycle = new Cycle
    sn.map{ node => node match {
        case Elem(prefix, "tr", att, scope, ch @ _*) => 
            Elem(prefix, "tr", att, scope, ch:_*) % Attribute(None, "class", Text(
                List(att.get("class").getOrElse("").toString, cycle.next).reduceLeft(_+" "+_).trim
                ), Null)
        case other => other
        }
    }
}

// Look for first <tbody>, transform child tr elements and stop recursion
// If no <tbody> found, recurse
def recurse(sn: NodeSeq): NodeSeq = sn.map{ node =>
    node match {
        case Elem(prefix, "tbody", att, scope, ch @ _*)
            => Elem(prefix, "tbody", att, scope, transformChildren(ch):_*)
        case Elem(prefix, label, att, scope, ch @ _*)
            => Elem(prefix, label, att, scope, recurse(ch):_*)
        case other => other
    }
}

val code = <table>
  <thead><tr><td>Don’t</td><td>transform this</td></tr></thead>
  <tbody>
    <tr class="otherclass">
      <td>r1c1</td><td>r1c2</td>
    </tr>
    <tr>
      <td><table><tbody><tr><td>Neither this.</td></tr></tbody></table></td><td>r2c2</td>
    </tr>
    <tr>
      <td>r3c1</td><td>r3c2</td>
    </tr>
  </tbody>
</table>

println(recurse(code))

Gives:

<table>
  <thead><tr><td>Don’t</td><td>transform this</td></tr></thead>
  <tbody>
    <tr class="otherclass odd">
      <td>r1c1</td><td>r1c2</td>
    </tr>
    <tr class="even">
      <td><table><tbody><tr><td>Neither this</td></tr></tbody></table></td><td>r2c2</td>
    </tr>
    <tr class="odd">
      <td>r3c1</td><td>r3c2</td>
    </tr>
  </tbody>
</table>
Debilski