A simple way to build for the Web
Hoplon is a ClojureScript library that paves over the web's idiosyncrasies and present a simpler way to design and build single-page web applications. Learn more on our wiki
A simple custom element
Elements and custom elements are regular functions. The function below, loud
, accepts any number of children
. It constructs and returns a div
containing the uppercased children
.
(defn loud [& children]
(h/div :css {:text-transform "uppercase"} children))
(loud "Lisp Can Do It")
A stateful custom element
In this example, the `timer` function initializes a Javelin input cell and schedules it for periodic update using window.setInterval
. The text of the returned div
is attached reactively to seconds
through a formula cell created using cell=
.
h/defelem
is a convenience macro for creating functions that handle attribute arguments naturally.
(h/defelem timer [attrs children]
(let [start (or (:start attrs) 0)
seconds (cell start)]
(.setInterval js/window #(swap! seconds inc) 1000)
(h/div attrs (cell= (str "Seconds Elapsed: " seconds)))))
;; our first timer will start at 0 and count up
(h/p (timer :style "color: green;" :start 0))
;; start 3 more timers, each with different starting values
(apply h/ol (map h/li (for [r (range 1 4)] (timer :start r))))
- Seconds Elapsed: 1
- Seconds Elapsed: 2
- Seconds Elapsed: 3
An application
In this final example, todo-list
is a function that returns an instance of a simple todo list application. The application responds to user input by updating input cell values when DOM events occur.
The :change
event on the text input is attached to a
function that updates the new-item
input cell as keystrokes
occur.
The :click
event on the button is attached to a function that appends the value of new-item
to the end of todo-items
inside a dosync
. dosync
is a transactional construct that suspends the propagation of new values through the cell graph while updating multiple cells.
todo-items
are rendered as li
elements using the loop-tpl
macro. loop-tpl
efficiently maps dynamically-sized collections to DOM nodes.
(h/defelem todo-list [{:keys [title]} _]
(let [todo-items (cell [])
new-item (cell "")]
(h/div
(h/h3 (or title "TODO"))
(h/ul
(h/loop-tpl :bindings [todo todo-items]
(h/li todo)))
(h/input :type "text"
:value new-item
:change #(reset! new-item @%))
(h/button :click #(dosync
(swap! todo-items conj @new-item)
(reset! new-item ""))
(h/text "Add #~{(inc (count todo-items))}")))))
(todo-list :title "TODO List")