sablono icon indicating copy to clipboard operation
sablono copied to clipboard

Lisp/Hiccup style templating for Facebook's React in ClojureScript.

  • ŜABLONO

    [[https://clojars.org/sablono][https://img.shields.io/clojars/v/sablono.svg]] [[https://github.com/r0man/sablono/actions?query=workflow%3A%22Clojure+CI%22][https://github.com/r0man/sablono/workflows/Clojure%20CI/badge.svg]] [[https://versions.deps.co/r0man/sablono][https://versions.deps.co/r0man/sablono/status.svg]] [[https://versions.deps.co/r0man/sablono][https://versions.deps.co/r0man/sablono/downloads.svg]]

    Lisp/Hiccup style templating for Facebook's [[http://facebook.github.io/react][React]] in [[https://github.com/clojure/clojurescript][ClojureScript]].

    [[https://xkcd.com/1144][http://imgs.xkcd.com/comics/tags.png]]

** Dependencies

/Ŝablono/ doesn't declare a dependency on React anymore. Use the React dependencies from one of the ClojureScript wrappers or provide the dependencies yourself like this:

#+BEGIN_SRC clojure :exports code :results silent [cljsjs/react "16.6.0-0"] [cljsjs/react-dom "16.6.0-0"] #+END_SRC

If you want to do server rendering and use the =render= or =render-static= functions from the =sablono.server= namespace you need to add the following dependency as well:

#+BEGIN_SRC clojure :exports code :results silent [cljsjs/react-dom-server "16.6.0-0"] #+END_SRC

** Usage

Most functions from [[https://github.com/weavejester/hiccup][Hiccup]] are provided in the =sablono.core= namespace. The library can be used with [[https://github.com/swannodette/om][Om]] like this:

#+BEGIN_SRC clojure :exports code :results silent (ns example (:require [om.core :as om :include-macros true] [sablono.core :as html :refer-macros [html]]))

(defn widget [data]
  (om/component
   (html [:div "Hello world!"
          [:ul (for [n (range 1 10)]
                 [:li {:key n} n])]
          (html/submit-button "React!")])))

(om/root widget {} {:target (. js/document (getElementById "my-app"))})

#+END_SRC

By default, /Ŝablono/ will wrap any forms in the body of hiccup with a call to =sablono.interpreter/interpret=. If your code returns a React element, then you can skip the call to this function by marking the s-expression with the metadata tag =:inline=. For example:

#+BEGIN_SRC clojure :exports code :results silent [:div {} ^:inline (function-that-returns-react-component)] #+END_SRC

** HTML Tags

/Ŝablono/ only supports tags and attributes that can be handled by React. This means you can't have your own custom tags and attributes at the moment. For more details take a look at the [[http://facebook.github.io/react/docs/tags-and-attributes.html][Tags and Attributes]] section in the React documentation.

** HTML Attributes

HTML attributes in [[http://facebook.github.io/react/docs/tags-and-attributes.html#html-attributes][React]] are camel-cased and the =class= and =for= attributes are treated special. /Ŝablono/ renames attributes with dashes in their name to the camel-cased version and handles the =class= and =for= special case. This is more consistent with [[https://github.com/weavejester/hiccup][Hiccup]] and naming conventions used in Clojure.

An =input= element with event listeners attached to it would look like this in /Ŝablono/:

#+BEGIN_SRC clojure :exports code :results silent (html [:input {:auto-complete "off" :class "autocomplete" :on-change #(on-change %1) :on-key-down #(on-key-down %1) :type "text"}]) #+END_SRC

** Setting innerHTML of a DOM node

It is not recommended to directly set the innerHTML of DOM nodes, but in some cases it is necessary. i.e. injecting a HTML string that was generated from Markdown.

#+BEGIN_SRC clojure :exports code :results silent (html [:div {:dangerouslySetInnerHTML {:__html "

hello world
" }}]) #+END_SRC

You can read more at [[http://facebook.github.io/react/docs/special-non-dom-attributes.html][React's special attributes]].

** Benchmark

Benchmark results can be found [[https://r0man.github.io/sablono/dev/bench/][here]].

** FAQ

*** How to run the tests?

You need to have [[https://nodejs.org/en/][Node.js]] and [[http://phantomjs.org/][PhantomJS]] installed for the
ClojureScript test.

Make sure you have all dependencies installed. The following
command installs the Maven and Node.js dependencies.

#+BEGIN_SRC sh :exports code :results silent
  lein deps
#+END_SRC

To run all Clojure and ClojureScript tests run the following
command:

#+BEGIN_SRC sh :exports code :results silent
  lein ci
#+END_SRC

For development the ClojureScript tests can be run with
[[https://github.com/bensu/doo][lein-doo]]. To run the tests on Node.js run the following command:

#+BEGIN_SRC sh :exports code :results silent
  lein doo node nodejs auto
#+END_SRC

To run the tests on PhantomJS use this command:

#+BEGIN_SRC sh :exports code :results silent
  lein doo phantom none auto
#+END_SRC

*** Why is there a compiler and an interpreter?

The interpreter is executed at *run time*, and it's job is to
evaluate Hiccup forms and produce React elements. The compiler on
the other hand, is executed at *compile time* and can optimize
certain Hiccup forms. It's job is to evaluate Hiccup forms and
produce executable code.

A good introduction to this topic can be found in Peter Seibel's
[[http://www.gigamonkeys.com/book][Practical Common Lisp]]:

- [[http://www.gigamonkeys.com/book/practical-an-html-generation-library-the-interpreter.html][An HTML Generation Library, the Interpreter]]
- [[http://www.gigamonkeys.com/book/practical-an-html-generation-library-the-compiler.html][An HTML Generation Library, the Compiler]]

** Thanks

This library is based on James Reeves excellent [[https://github.com/weavejester/hiccup][Hiccup]] library. The server side rendering code has been taken from [[https://github.com/omcljs/om][om.next]].

** License

Copyright © 2013-2020 [[https://github.com/r0man][r0man]]

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.