Suppose this is the structure that we want to render as a table:
data Table = Table
{
caption :: T.Text
, rows :: [[Int]]
}
A simple template could be something like
<body>
<mytable>
<table>
<caption><mycaptiontext/></caption>
<myrow>
<tr>
<mydata>
<td><mydatavalue/></td>
</mydata>
</tr>
</myrow>
</table>
</mytable>
</body>
Where mytable
, mycaptiontext
, myrow
, mydata
and mydatavalue
are the tags that will be bound to splices. mytable
in particular will be bound to a top-level splice.
There are three levels to consider: the table as a whole, the rows, and the data inside the rows. For each level, we are going to define a function which takes a runtime action and returns a compiled splice.
For the "row data" level, the runtime action carries the list of values in a row:
dataSplice :: Monad m => RuntimeSplice m [Int] -> C.Splice m
dataSplice = C.manyWithSplices C.runChildren splicefuncs
where
splicefuncs = do
"mydatavalue" ## (C.pureSplice . C.textSplice $ T.pack . show)
For the "rows" level, the runtime action carries the full list of rows:
rowSplice :: Monad m => RuntimeSplice m [[Int]] -> C.Splice m
rowSplice = C.manyWithSplices C.runChildren splicefuncs
where
splicefuncs = do
"mydata" ## dataSplice
Notice how we use dataSplice
in the definition.
For the "whole table" level, the runtime action carries a Table
:
tableSplice :: Monad m => RuntimeSplice m Table -> Splice m
tableSplice = C.withSplices C.runChildren splicefuncs
where
splicefuncs = do
"mycaptiontext" ## (C.pureSplice . C.textSplice $ caption)
"myrow" ## (rowSplice . liftM rows)
Notice how we use liftM
here, transforming a RuntimeSplice m Table
into a RuntimeSplice m [[Int]]
so that the type fits with what rowSplice
expects.
Now suppose we have a runtime action that produces the table data:
runtime :: Monad m => RuntimeSplice m Table
runtime = return $ Table "This is the caption" $
[ [3, 5, 6], [8, 3, 7 ] ]
(This one is trivial. A more complex action could fetch the data from a database, for example. Remember that, in Snap, RuntimeSplices
have access to all the Snaplet
machinery.)
We can combine tableSplice
with runtime
and register the result as a top-level splice in the Heist configuration, bound to the mytable
tag. Like this:
let heistConfig = mempty {
hcCompiledSplices = do
"mytable" ## (tableSplice runtime)
.... other top-level splices here ...
}
This should work.
More examples of rendering nested structures with compiled Heist can be found in this tutorial.