Defining views

In the application record

type Application model message = {
        init :: model,
        view :: model -> Html message,
        update :: model -> message -> model
}

the view field maps the current state to markup. Whenever the model is updated, flame will patch the DOM by calling view with the new state.

A custom DSL, defined by the type Html, is used to write markup. You will need to qualify imports, e.g., prefix HE for HTML elements and HA for HTML attributes, properties and events

import Flame.HTML.Element as HE
import Flame.HTML.Attribute as HA

Attributes, properties and events

The module Flame.HTML.Attribute exports

See the API reference for a complete list of attributes. In the case you need to define your own attributes/properties/events, Flame provides the combinators

HA.createAttibute

HA.createProperty

HA.createEvent
HA.createRawEvent

Elements

The module Flame.HTML.Element exports HTML elements, such as div, body, etc, following the convention

HE.div [HA.id "my-div"] [HE.text "text content"] --renders <div id="my-div">text content</div>
HE.div_ [HE.text "text content"] --renders <div>text content</div>
HE.div' [HA.id "my-div"] --renders <div id="my-div"></div>

(a few elements that usually have no children like br or input have element behave as element')

Attributes and children elements are passed as arrays

HE.div [HA.id "my-div", HA.disabled False, HA.title "div tite"] [
        HE.span' [HA.id "special-span"],
        HE.br,
        HE.span_ [HE.text "I am regular"],
]
{- renders
<div id="my-div" title="div title">
        <span id="special-span"></span>
        <br>
        <span>I am regular</span>
</div>
-}

But for some common cases, the markup DSL also defines convenience type classes so we can write

See the API reference for a complete list of elements. In the case you need to define your own elements, Flame provides a few combinators as well

HE.createElement
HE.createElement_
HE.createElement'
HE.createEmptyElement

View logic

A view is just a regular PureScript function, meaning we can compose it or pass it around as any other value. For example, we can use the model in attributes

newtype Model = Model { done :: Int, enabled :: Boolean }

type Message = Do

view :: Model -> Html Message
view model = HE.div [HA.class' { usefulClass: model.enabled }] $ HE.input [HA.type' "button", HA.value "Do thing number " <> show $ model.done, HA.onClick Do]

or to selective alter the markup

type Name = String

type Model = Just Name

type Message = Update Name | Greet

view :: Model -> Html Message
view = case _ of
        Nothing -> HE.div' [
                HE.input [HA.type' "text", HA.onInput Update],
                HE.input [HA.type' "button", HA.value "Greet!", HA.onClick Greet]
        ]
        Just name -> "Greetings, " <> name <> "!"

but perhaps more interesting is the ability to create “partial views” without any special syntax support. We will talk more about how to structure applications in the next section, Handling events, but creating reusable views is remarkably simple.

header :: forall model message. model -> Html message
header = ...

footer :: forall model message. model -> Html message
footer = ...

view :: Model -> Html Message
view model = HE.content' [
        header,
        ...
        footer
]

See the counters test application for more examples of how to compose views.

Next: Handling events