简体   繁体   中英

Heist: How do I insert a dynamic list of sub-templates into a template?

I am writing a site for online surveys. I have a list of questions that all go on one html page and the list is of unknown length. Each question has the form stored in template qu1.tpl and the page is qu.tpl . Now I want to:

  1. replace some names in qu1.tpl for each question

  2. replace some things in qu.tpl once

  3. and stick all instantiations of qu1.tpl into qu.tpl

Using what I learned in the tutorial, I tried to recursively replace a tag <qulist/> with <apply template="qu1.tpl"><qulist/> in qu.tpl using localHeist and bindString but this cannot work because qu.tpl is already rendered so the newly inserted apply tag doesn't resolve.

what should I do instead?

(I guess this is a more general question. If you can think of other applications the answer applies to, please add text and tags for the search engines.)

In Heist, when you're doing something that involves calculations on dynamic data you usually will use a splice. Your first two points can be handled by binding splices. For the third point, I would start by creating a splice function that renders a particular question template. It would look something like this:

questionSplice :: Monad m => Int -> Splice m
questionSplice n = do
  splices <- setupSplicesForThisQuestion
  mTemplate <- callTemplate (B.pack ("qu"++(show n))) splices
  return $ fromMaybe [] mTemplate

Now you can create a splice for the list of survey questions:

surveyQuestions :: Monad m => Splice m
surveyQuestions = do
  questions <- getSurveyQuestions
  mapSplices questionSplice questions

Then you can bind this splice to a particular tag and use it anywhere in qu.tpl or any other template.

The important part here is the callTemplate function. It is Heist's function for rendering templates from inside a TemplateMonad computation. I don't think it is talked about much in the tutorials because it hasn't been a use case that people are usually concerned with, and it's easy to miss in the API docs.

thanks to mightybyte for helping me out with this on irc. after the 8 hours that i was banned from answering to myself, here is my variant of the same answer:

  1. build a splice that reads template qu1.tpl, instantiates it (ie, fills in name and number of the question in the list), and returns it. the heist function callTemplate helps you with that. (this splice is called splicex in the pseudocode below.)

  2. write another splice that folds splicex so that you get a list of (instantiated) questions instead of a single one. (function splice in the pseudocode.)

  3. use bindSplice instead of bindString.

pseudocode (tested then modifed and ripped out of context) -

 ... -> let
          splice :: Monad m => Splice m
          splice = foldM (\ ts (s, i) ->
                             liftM ((++) ts) $ splicex (quName, quNumber))
                         []
                         (zip questionName [0..])

          splicex :: Monad m => (String, Int) -> Splice m
          splicex (quName, quNumber) =
              do
                mt <- callTemplate "qu1"
                        [ ("question-name", Data.Text.pack quName)
                        , ("question-number", Data.Text.pack $ show quNumber)
                        ]

                case mt of
                  Nothing -> error "splice rendering failed."
                  Just (t :: Template) -> return t
         in
           -- fill in the list of (instatiated) questions
           heistLocal (bindSplice "qulist" splice) $
           -- before that, instantiate the overall page
           instantiatePage $
           render "qu"

btw, my sourceforge avatar is too weak to create new tags. anybody care to tag this with 'heist'?

links:

http://snapframework.com/

#snapframework channel on the freenode IRC network.

http://snapframework.com/docs/tutorials/heist

When I tried to figure out template loops, I stumbled multiple times across this question. Sadly, everything here is probably outdated, version 0.5 (or below) while version 0.6 (I guess) introduced runChildrenWith.

A simple example for using runChildrenWith would be:

list_test_entries.tpl:

<listTestEntries>
  <dt><testEntry/></dt>
  <dd>This is part of the repeated template</dd>
</listTestEntries>

Site.hs:

listTestEntriesHandler :: Handler App App  ()
listTestEntriesHandler = do
    results <- getData 
    renderWithSplices "list_test_entries"
        ("listTestEntries" ## listTestEntriesSplice results)

getData :: Handler App App [String]
getData = return ["1", "2", "3"]

listTestEntriesSplice  :: [String] -> I.Splice AppHandler
listTestEntriesSplice = I.mapSplices (I.runChildrenWith . listTestEntrySplice)

listTestEntrySplice :: Monad m => String -> Splices (HeistT n m Template)
listTestEntrySplice dataEntry = do
  "testEntry" ## I.textSplice (T.pack $ "data: " ++ dataEntry)

See https://github.com/michaxm/heist-template-loop-demo for a running demo.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM