How do I change the <title> in an IHP app?

144 Views Asked by At

I see <title>App</title> is hardcoded in defaultLayout in Web/View/Layout.hs, and Web/View/Context.hs has

    let viewContext = ViewContext {
            requestContext = ?requestContext,
            user = currentUserOrNothing,
            flashMessages,
            controllerContext = ?controllerContext,
            layout = let ?viewContext = viewContext in defaultLayout
        }

but how do I use a different function for a specific View? The html function of the View class doesn't seem to return a new context, just a new html for part of the page.

2

There are 2 best solutions below

0
On BEST ANSWER

There is now a pageTitle function you could use in view (e.g. your layout) like this:

[hsx|
    <head>
        <title>{pageTitle}</title>
    </head>
|]

https://github.com/digitallyinduced/ihp/blob/18f104da69c526ff9e8ad3a6cdaedc6d39afb38c/IHP/PageTitle/ViewFunctions.hs#L54

Which can in turn be set in action using setTitle

action ShowProjectAction { projectId } = do
    project <- fetch projectId
    setTitle (get #title project)

https://github.com/digitallyinduced/ihp/blob/d98c3d6eb8145d859c9ce461e7d1367be5be7337/IHP/PageTitle/ControllerFunctions.hs#L32

Latest IHP also generates the following layout s.t. you get it automatically with a fallback:

defaultLayout :: Html -> Html
defaultLayout inner = H.docTypeHtml ! A.lang "en" $ [hsx|
<head>
    {metaTags}

    {stylesheets}
    {scripts}

    <title>{pageTitleOrDefault "App"}</title>
</head>
<body>
    <div class="container mt-4">
        {renderFlashMessages}
        {inner}
    </div>
</body>
|]

0
On

Figured it out: There's a beforeRender function you can override in the View class where you can set it, e.g.

data ShowView = ShowView { targets :: [Include "comments" Post], postTitle :: Text }

instance View ShowView ViewContext where
    beforeRender (context, view) = (context { layout = myLayout (postTitle view) }, view)
    html ShowView { .. } = [hsx| … |]

(and set postTitle in Web/Controller/Posts.hs), then change Web/View/Layout.hs to do

defaultLayout :: Html -> Html
defaultLayout = myLayout "App"

myLayout :: Text -> Html -> Html
myLayout title inner = H.docTypeHtml ! A.lang "en" $ [hsx|
<head>
    {metaTags}

    {stylesheets}
    {scripts}

    <title>{title}</title>
</head>
<body>
    <div class="container mt-4">
        {renderFlashMessages}
        {inner}
    </div>
</body>
|]