Question

Well, this is another obstacle with compiled splices that I struggle to overcome. I had some trouble when I first did it with the original interpreted splices and now I am stuck translating it to compiled variant.

The original problem is described here: rendering a table

So I need to build this kind of structure:

map (\doc -> fmap (\f -> splice $ f at doc) fields ) documents

which is just just external and internal loops, the outer rendering raws and the inner rendering cells. It is all described in the original link.

It is currently implemented in interpreted mode like this:

tableRawS raw = mapSplices (\f -> tableCellS $ T.pack $ at f raw) (tableFields table)
  where tableCellS cell = runChildrenWithText $ "table-cell" ## cell

tableBodyS = mapSplices (\d -> runChildrenWith $ raws d) documents
  where docID d = T.pack $ show $ valueAt "_id" d
        raws d = do "table-raw" ## tableRawS d
                    "raw-id" ## textSplice $ docID d

I've been struggling for a few days now with no result. It is probably due to the lack of understanding compiled splices API. Please help!

EDIT

I did not provide enough details to my problem. The best approximation to the above interpreted variant that I managed to get is this:

tableBody = manyWithSplices runChildren tableRaw $ lift documents
tableRaw doc = do "table-raw" ## manyWithSplices runChildren rawCell $ lift labels
                    where rawCell label = "table-cell" ## pureSplice . textSplice $ at label doc 
                  "table-rawid" ## pureSplice . textSplice $ at "_id" doc
                    where oid = T.pack . show. valueAt "_id"

It does not work! The problem is translating the raw rendering function which is tableRaw. So I will give more details. The data I am feeding into the tableBody comes as a list of Map like structure:

document = ["_id" =: 12345, "name" =: "pretty", "model" =: "cool", "size" =: "big"]

So to pull the data out in a raw I need to map over list of labels

map (\l -> at l document) labels

I can do this with the interpreted varian without a problem. The interpreted example of tableRawS is parameterised by a ducument but maps over [labels]. I can not achieve this in compiled variant. I need both the document and the list of labels present in the tableRaw function so I can bind different cell-splices out of the document. This is real pain. I don't know how to do it. Whatever I do I end up with tableRaw mapping over document instead of mapping over list of labels.

Basically. I get [document] from the database with the above mentioned document structure, and I get labels from the database, where

 labels = ["name", "model", "size"] -- table fields

I get data from the document with "at :: Label -> Value" Having all that, how do I render this template:

<table class="table" data-title=${table-name}>
<thead>
  <tr>
    <th>#</th>
    <table-head>
      <th> <table-hcell/> </th>
    </table-head>
  </tr>
</thead>
<tbody>
  <table-body>
    <tr data-oid=${table-rowid}>
      <td> <input type="checkbox"/> </td>
      <table-raw>
        <td> <table-rcell/> </td>
      </table-raw>
    </tr>
  </table-body>
</tbody>

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top