Как создать таблицу с неизвестным (на момент сборки) количеством столбцов
Для простоты представьте, что у меня есть список списков. Я хочу отобразить таблицу 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 ответа:
Вы близки. Попробуйте это:
<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>
Ваш фактический вариант использования может быть немного сложнее, поэтому это может быть неприменимо.