Hoplon

ClojureScript Web Library

Get Started

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

  • Custom Elements icon

    Custom HTML elements are regular functions and can be created and composed functionally with HLisp

  • Spreadsheet Icon

    Spreadsheet-like dataflow with the Javelin ClojureScript library. Intuitively model reactive behavior using cells and formulas.

  • Backend Icon

    Interact with the server using whatever backend you want.

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")
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: 0

  1. Seconds Elapsed: 1
  2. Seconds Elapsed: 2
  3. 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")

TODO List