Let's say you want to display information about a list of persons using compiled splices (assume that we start from the scaffolding generated by snap init
.)
A very simple _persons.tpl
template with dummy values would be something like
<body>
<person>
<div>
<h1><name>dummy name</name></h1>
<p><age>77</age></p>
<p><location>jauja</location></p>
</div>
</person>
</body>
Where person
, name
, age
, and location
are the tags to be spliced.
We define a trivial Snaplet that holds the info
data Foo = Foo
{
_persons :: [Person]
}
makeLenses ''Foo
data Person = Person
{
_name :: Text
, _age :: Int
, _location :: Text
}
makeLenses ''Person
and we add it to the App
record:
data App = App
{ _heist :: Snaplet (Heist App)
, _sess :: Snaplet SessionManager
, _auth :: Snaplet (AuthManager App)
, _foo :: Snaplet Foo
}
we add the following to the app initializer
f <- nestSnaplet "foo" foo $ makeSnaplet "foo" "Foo Snaplet" Nothing $ return $ Foo $
[ Person "Ricardo" 33 "Los Cantones"
, Person "Luis" 38 "Montealto"
]
...
return $ App h s a f
This function constructs a Handler that returns the list of persons (using view
from Control.Lens
):
personH :: SnapletLens b Foo -> Handler b b [Person]
personH l = withTop l $ view persons <$> get
This function constructs the appropiate compiled splice from a RuntimeSplice
that produces a list of Persons. RuntimeSplice
s represent information that can only be known at run time, as opposed to load time:
personSplice :: Monad n => RuntimeSplice n [Person] -> C.Splice n
personSplice = C.manyWithSplices C.runChildren splicefuncs
where
splicefuncs = mconcat
[ "name" ## (C.pureSplice . C.textSplice $ view name)
, "age" ## (C.pureSplice . C.textSplice $ T.pack . show . view age)
, "location" ## (C.pureSplice . C.textSplice $ view location)
]
And this function can be used to register the splice in the global Heist configuration. Notice that we lift the Handler
into a RuntimeSplice
:
addPersonSplices :: HasHeist b => Snaplet (Heist b) ->
SnapletLens b Foo ->
Initializer b v ()
addPersonSplices h l = addConfig h $ mempty
{
hcCompiledSplices = "person" ## (personSplice . lift $ personH l)
}
Be sure to add this line to the app initializer:
addPersonSplices h foo
And to add the following pair to the app's routes:
("/persons", cRender "_persons")
If you now run the server, navigating to http://127.0.0.1:8000/persons
should show the list.
UPDATE
For the simpler case (no complex records, no lenses) in which you only want to show a list of strings.
The template could be something like:
<body>
<strings>
<p><string>dummy value</string></p>
</strings>
</body>
The top-level splice would be:
stringSplice :: Monad n => C.Splice n
stringSplice = C.manyWithSplices C.runChildren splicefuncs (return ["aa","bb","cc"])
where
splicefuncs = "string" ## (C.pureSplice . C.textSplice $ id)
This means "when we encounter the tag associated to this splice, perform an action that produces a list of strings, and for each string, render the contents of the tag, substituting the current string for the string
tag".
Notice that the signature of manyWithSplices forces the stuff to the right of the (##)
to have type RuntimeSplice n Text -> Splice n
. Here id
has type Text -> Text
. C.TextSplice
transforms it into something of type Text -> Builder
, and C.pureSplice
performs the final transformation into a RuntimeSplice n Text -> Splice n
.
In place of (return ["aa","bb","cc"])
you could provide a more complex action that connected a database and extracted the strings form there.
A function to register this splice would be:
addStringSplices :: HasHeist b => Snaplet (Heist b) -> Initializer b v ()
addStringSplices h = addConfig h $ mempty
{
hcCompiledSplices = "strings" ## stringSplice
}