Overview
The IHP.ViewSupport module provides the foundation for building views in IHP. It includes:
- View type class and rendering functions
- HSX templating support
- Form helpers and CSS framework integration
- Helper functions for URLs and paths
- Time formatting utilities
This module is automatically available via IHP.ViewPrelude in all view files.
Core Types
The main type class that all views must implement.data ShowPostView = ShowPostView { post :: Post }
instance View ShowPostView where
html ShowPostView { .. } = [hsx|
<div class="post">
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
|]
Type alias for HtmlWithContext ControllerContext. This is the return type of view rendering.
Type alias for Html -> Html. A layout wraps view content.myLayout :: Layout
myLayout inner = [hsx|
<!DOCTYPE html>
<html>
<body>
<header>My App</header>
{inner}
</body>
</html>
|]
View Type Class
Render the view as HTML. Required method.instance View IndexView where
html IndexView { posts } = [hsx|
<div class="posts">
{forEach posts renderPost}
</div>
|]
Hook that runs before rendering. Optional method.instance View MyView where
beforeRender view = do
setLayout customLayout
html MyView { .. } = [hsx|...|]
Render the view as JSON. Optional method.instance View ApiUserView where
html view = error "Not implemented"
json ApiUserView { user } = toJSON user
Template Helpers
forEach
Foldable f => f a -> (a -> Html) -> Html
Iterate over a collection and render each item.[hsx|
<ul>
{forEach posts \post -> [hsx|
<li>{post.title}</li>
|]}
</ul>
|]
Get a string ID for the current view (e.g., “posts-show”).-- In Web.View.Posts.Show
[hsx|<div id={currentViewId}>...</div>|]
-- Renders: <div id="posts-show">...</div>
nl2br
Textual text => text -> Html
Convert newlines to <br> tags.[hsx|<p>{nl2br post.body}</p>|]
-- "Hello\nWorld" becomes "Hello<br/>World"
Remove HTML tags from text.let clean = stripTags "<b>Hello</b> World"
-- Returns: "Hello World"
URL and Path Functions
pathTo
HasPath action => action -> Text
Generate a URL path for an action.[hsx|<a href={pathTo ShowPostAction { postId }}>View Post</a>|]
-- Generates: <a href="/ShowPost?postId=...">View Post</a>
urlTo
HasPath action => action -> Text
Generate a complete URL for an action (includes domain).let url = urlTo ShowPostAction { postId }
-- Returns: "https://example.com/ShowPost?postId=..."
isActivePath
PathString controller => controller -> Bool
Check if the current request path matches the given path.[hsx|
<a href={PostsAction} class={classes ["nav-link", ("active", isActivePath PostsAction)]}>
Posts
</a>
|]
isActivePathOrSub
PathString controller => controller -> Bool
Check if the current path starts with the given path.-- If current path is /Posts/123
isActivePathOrSub PostsAction -- Returns: True
isActiveController
Data controller => controller -> Bool
Check if the current controller matches.when (isActiveController PostsController) $ ...
isActiveAction
HasPath action => action -> Bool
Check if the current action matches.[hsx|
<li class={classes [("active", isActiveAction ShowPostAction { postId })]}>
...
</li>
|]
Asset Management
Add cache buster to asset paths.[hsx|<script src={assetPath "/app.js"}></script>|]
-- Renders: <script src="/app.js?v=9be8995c..."></script>
Get the current asset version string.let version = assetVersion
-- Returns: "9be8995c-7055-43d9-a1b2-43e05c210271"
Configure via the IHP_ASSET_VERSION environment variable.
Request Access
Access the current WAI Request.let path = theRequest.rawPathInfo
let method = theRequest.requestMethod
CSS Framework Integration
Get the current CSS framework configuration.let framework = theCSSFramework
fromCSSFramework
KnownSymbol field => Proxy field -> appliedFunction
Access a CSS framework function.let submitClass = fromCSSFramework @"submitClass"
From IHP.View.Form:
formFor
record -> (FormContext record -> Html) -> Html
Create a form for a record with automatic CSRF protection.renderForm :: Post -> Html
renderForm post = formFor post \form -> [hsx|
{textField #title}
{textField #body}
{submitButton}
|]
textField
KnownSymbol fieldName => Proxy fieldName -> Html
Render a text input field.
Render a submit button.{submitButton { label = "Create Post" }}
selectField
KnownSymbol fieldName => Proxy fieldName -> [a] -> Html
Render a select dropdown.{selectField #status ["draft", "published", "archived"]}
checkboxField
KnownSymbol fieldName => Proxy fieldName -> Html
Render a checkbox.{checkboxField #isPublished}
hiddenField
KnownSymbol fieldName => Proxy fieldName -> Html
Render a hidden input field.
From IHP.View.TimeAgo:
Render a time as “5 minutes ago”, “2 hours ago”, etc.[hsx|<span>Posted {timeAgo post.createdAt}</span>|]
-- Renders: <span>Posted 5 minutes ago</span>
Validation Support
getValidationFailure
KnownSymbol field => Proxy field -> Maybe Text
Get validation error for a field.case getValidationFailure #email of
Just error -> [hsx|<div class="error">{error}</div>|]
Nothing -> mempty
hasValidationError
KnownSymbol field => Proxy field -> Bool
Check if a field has a validation error.let inputClass = if hasValidationError #email
then "form-control is-invalid"
else "form-control"
Flash Messages
Render all flash messages.-- In layout:
[hsx|
<body>
{renderFlashMessages}
{inner}
</body>
|]
Auto Refresh
From IHP.AutoRefresh.View:
Enable auto-refresh for the current view.instance View DashboardView where
html DashboardView { .. } = [hsx|
{autoRefresh}
<div>Dashboard content that updates in real-time</div>
|]
Modal Support
From IHP.Modal.ViewFunctions:
Render content in a modal.[hsx|
{modal [hsx|
<h2>Delete Confirmation</h2>
<p>Are you sure?</p>
|]}
|]
Live Reload
Get the WebSocket URL for live reload in development.let wsUrl = liveReloadWebsocketUrl
-- Returns: "ws://localhost:8001"
HSX Helpers
Quasi-quoter for HSX templates.[hsx|<div class="container">{content}</div>|]
Convert a value to HTML.{toHtml "Hello World"}
{toHtml 42}
Insert raw HTML without escaping.{preEscapedToHtml "<strong>Bold</strong>"}
See Also