# IHP: Controllers, Views, Forms, and Authentication --- ## Controllers and Actions Controllers live in `Web/Controller/`. Each action is a constructor in `Web/Types.hs` and a case in the controller's `action` handler. ```haskell -- Web/Controller/Widgets.hs instance Controller WidgetsController where action WidgetsAction = do widgets <- query @Widget |> fetch render IndexView { .. } action ShowWidgetAction { widgetId } = do widget <- fetch widgetId events <- query @InteractionEvent |> filterWhere (#widgetId, widgetId) |> orderByDesc #occurredAt |> limit 20 |> fetch render ShowView { .. } action CreateWidgetAction = do let widget = newRecord @Widget widget |> fill @'["name", "widgetType", "hubId"] |> validateField #name nonEmpty |> ifValid \case Left widget -> render NewView { widget } Right widget -> do widget <- createRecord widget setSuccessMessage "Widget created" redirectTo WidgetsAction ``` ### Key Patterns | Pattern | Description | |---------|-------------| | `fill @'["fieldA", "fieldB"]` | Type-safe HTTP param binding; compile error if field doesn't exist | | `validateField #name nonEmpty` | Chains a validator onto the record | | `ifValid \case Left r -> ... Right r -> ...` | Branches on validation success/failure | | `createRecord r` | INSERT; returns the persisted record | | `updateRecord r` | UPDATE | | `deleteRecord r` | DELETE | | `redirectTo SomeAction { ... }` | Type-safe redirect | | `render SomeView { .. }` | Passes in-scope bindings to the view (record wildcard) | | `respondJson value` | JSON response (no view) | | `respondHtml someHtml` | Partial HTML response (useful with htmx) | ### Before Filters ```haskell instance Controller WidgetsController where beforeAction = do ensureIsUser -- redirects to login if not authenticated -- custom per-controller guards here action ... ``` ### ControllerContext An implicit key-value map threaded through all actions and views. Stores current user, request, flash messages, layout. Access via `fromContext @SomeType` or framework helpers like `currentUser`. --- ## Views and HSX ### The View Typeclass Views are data types with a `View` instance: ```haskell data ShowView = ShowView { widget :: !Widget , events :: ![InteractionEvent] } instance View ShowView where html ShowView { .. } = [hsx|
|] ``` Views can also implement a `json` method for content-negotiated API responses. ### HSX Syntax HSX is a quasi-quoter (`[hsx| ... |]`) that compiles to BlazeHtml. Checked at compile time. ```haskell [hsx| -- Interpolation (auto-escaped, XSS-safe) {widget.name} -- Raw HTML (opt-out of escaping)