Как создать таблицу с неизвестным (на момент сборки) количеством столбцов


Для простоты представьте, что у меня есть список списков. Я хочу отобразить таблицу html, в которой есть строка для каждого элемента списка верхнего уровня, а каждый столбец в строке является элементом дочернего списка.

Итак

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

Приведет к html-таблице, которая будет отображаться следующим образом:

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

Вот моя попытка (шаблон)

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

И соответствующий метод во фрагменте:

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)))
}

Когда я перехожу на страницу, она дает мне следующее ошибки:

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
...

Есть идеи по поводу наилучшего способа справиться с этим?

2 2

2 ответа:

Вы близки. Попробуйте это:

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

И для вашего фрагмента:

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) })
}

Я оставил в некоторых типах и был немного более явным, чтобы вы могли видеть, что именно происходит. Ваша проблема заключалась в том, что вы использовали один и тот же шаблон in для каждого уровня, когда то, что вы хотели сделать, было постепенным сужением шаблона. Итак, в приведенном выше шаблоне мы хотим, чтобы все внутри тегов <a:row> повторялось для каждого элемента верхнего уровня списка, а затем все внутри тегов <b:cell>, чтобы повторите для каждого Int. Чтобы сделать это, нужно создать FuncBindParam, что мы и делаем со следующей строкой:

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

Lift затем передаст xhtml, который содержался внутри тега <a:row> в качестве аргумента функции curried. А вызовы flatMap внутри функций curried заботятся о повторении шаблона столько раз, сколько необходимо.

Если вам не нужен конкретный ответ на лифт, что-то вроде этого может сработать

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>

Ваш фактический вариант использования может быть немного сложнее, поэтому это может быть неприменимо.