Git Product home page Git Product logo

sablono's Introduction

ŜABLONO

https://img.shields.io/clojars/v/sablono.svg https://github.com/r0man/sablono/workflows/Clojure%20CI/badge.svg https://versions.deps.co/r0man/sablono/status.svg https://versions.deps.co/r0man/sablono/downloads.svg

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

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:

[cljsjs/react "16.6.0-0"]
[cljsjs/react-dom "16.6.0-0"]

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:

[cljsjs/react-dom-server "16.6.0-0"]

Usage

Most functions from Hiccup are provided in the sablono.core namespace. The library can be used with Om like this:

(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"))})

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:

[:div {}
 ^:inline (function-that-returns-react-component)]

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 Tags and Attributes section in the React documentation.

HTML Attributes

HTML attributes in 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 Hiccup and naming conventions used in Clojure.

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

(html [:input
       {:auto-complete "off"
        :class "autocomplete"
        :on-change #(on-change %1)
        :on-key-down #(on-key-down %1)
        :type "text"}])

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.

(html [:div {:dangerouslySetInnerHTML {:__html "<div>hello world</div>" }}])

You can read more at React’s special attributes.

Benchmark

Benchmark results can be found here.

FAQ

How to run the tests?

You need to have Node.js and PhantomJS installed for the ClojureScript test.

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

lein deps

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

lein ci

For development the ClojureScript tests can be run with lein-doo. To run the tests on Node.js run the following command:

lein doo node nodejs auto

To run the tests on PhantomJS use this command:

lein doo phantom none auto

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 Practical Common Lisp:

Thanks

This library is based on James Reeves excellent Hiccup library. The server side rendering code has been taken from om.next.

License

Copyright © 2013-2020 r0man

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

sablono's People

Contributors

aamedina avatar ajchemist avatar akhudek avatar asmala avatar elliot42 avatar eslick avatar gacelita avatar jenanwise avatar joelash avatar jwhitlark avatar kennyjwilli avatar kidpollo avatar lynaghk avatar mikekap avatar nha avatar ostronom avatar r0man avatar sgrove avatar the-kenny avatar tonsky avatar totakke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sablono's Issues

Should select boxes be wrapped with `wrap-form-element`?

Hi!

I noticed a bug in Om where select form elements would flash different values on Safari when a value was selected.

The solution was to wrap the select box with wrap-form-element like the option, input and textarea.

I know that Sablono used Om's code for making inputs behave (with the wrap-form-element function) so I thought I would post this here.

Here's the link to the Om issue (With a video example of the problem. A fix was created and merged in): omcljs/om#508

What do you think?

Support class/id shorthand

First of all, thank you very much for sablono, I'm enjoying working with it and Om a lot.

I do have a small feature request:

Hiccup lets you define id class shorthands as follows:

[:div#div-id.class1.class2 ...]

But that doesn't seem to be possible in sablono.

Ideally you would be able to mix this shorthand as well as map based attributes:

[div#div-id.class1 {:class "class2 class3"} ...] ; class="class1 class2 class3"

Would it make sense to add this?

Cannot render cursors

I'm not sure whether this is a bug or out of scope, but it did surprise me: if a cursor points to a valid html vector, it looks like Sablono cannot render it. This was much less obvious in the original use-case, but here is a minimal example:

(ns sablono-bug.core
  (:require [om.core :as om :include-macros true]
            [sablono.core :as html :refer-macros [html]]))

(def app-state
  (atom {:a [:pre "hello"]}))

(defn main []
  (om/root
   (fn [app owner]
     (reify
       om/IRender
       (render [_]
               (html
                [:div {}
                 [:pre (pr-str (:a app))]
                 [:div {} (:a app)]
                 [:div {} (into [] (:a app))]
                 [:div {} (:a @app-state)]]))))
   app-state
   {:target (. js/document (getElementById "app"))}))

I run that in a chestnut project, with Om 0.8.0-rc1 and Sablono 0.3.4. I can provide the full project if needed.

Expected output:

[:pre "hello"]
hello
hello
hello

Actual output:

[:pre "hello"]
25prepre211845686921537751054096hello16766851181961aa-21234075862153775105409625prepre211845686921537751054096hello167668511819616647951819618G__1G__1-21431073252154168321409616123663819664552961638615aa-212340758621537751054096167668511819621804244798192
hello
hello

Generic support for input fields

We want to create input fields with type such as type="number". In order to do this we came up with something along the lines of

(defelem input
  ([type name]
     (input type name nil))
  ([type name value]
     (html/input-field type name value)))

Would you accept a pull request for:

  • rename input-field to input-field*
  • (defelem input-field ...).

Problem when combining keyword-classes with attr-map

Hi

In sablono 0.2.6 the following worked.
Upgrading to om 0.5, react 0.9 and sablono 0.2.10 (and .11) I am seeing the following error in the console:

Uncaught TypeError: Object helloworld has no method 'call'

I have boiled it down the following example:

(defn style-it [placeholder]
 {:placeholder (str placeholder)
  :type "text"} )

(defn app-root [app owner]
  (reify
    om/IRender
    (render [this]
      (html
       [:input.helloworld
        (style-it "dinosaurs")]))))

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

I was hoping it would still be possible to combine keyword-classes with attr-maps from functions.

Use standard keys for :class and :for

Given that the HTML attributes get passed via sablono.compiler/compile-attrs, is there a reason to stick to the React convention of using :htmlFor and :className, instead of the standard :for and :class? These could be easily rewritten on the fly within compile-attr to follow React conventions.

Sticking to the standard key names would allow sablono to more readily share code with hiccup and related libraries (see also this hiccup issue), and would seem more intuitive to the average Clojure user. What do you think?

Something like this:

(def ^:private react-attrs
  {:for :htmlFor
   :class :className})

(defn compile-attrs
  "Compile a HTML attribute map."
  [attrs]
  (->> (seq (rename-keys attrs react-attrs))
       (map #(apply compile-attr %1))
       (apply merge)
       (to-js)))

Mi

Something about the way Sablono is rendering makes it extremely difficult know when a key is required or not to avoid the warning: Each child in an array should have a unique "key" prop. Check the render method of undefined., and of course the massive performance penalty that goes along with it.

For example, in the following:

[:li.file_item {:key (:src media)}
     [:a
      {:key "a" ;; If this key isn't here, we get the warning
       :href "#"
       :on-click (constantly false)
       :target "_blank"}
      [:img {:key "img" :src (str "/assets/images/" (get icon-map extension "file") "_icon.png")}]
      [:span {:key "span"} (:name media)]]]

The [:a] tag has no siblings, but React is seeing the children of [:li] as an array with one element:

d6

The current solution is to litter :key and :react-key entries everywhere (on sablono-tags and om-components, respectively) in a very-difficult-to-track-down way. Not 100% sure of the causes yet.

support nobr aka nbsp?

formative emits this tag under some conditions. It's supported by pretty much every browser. However, React.DOM contains no such tag, and the w3c discourages its use.

I would suggest against hacking something together - this should be implemented in React, not here. Still, it's probably better not to throw a show-stopping exception - rather, I'd suggest doing something like emitting a [:span {:style {white-space "nowrap"}}]

Change the default widget target to not be document body

Hi,

I had a frustrating problem that took me quite a while to figure out. My brepl kept hanging after evaluating once...

Turns out it was because I had the widget set to the document's body like the example and it removed all of the brepl connection stuff.

This is something that should be changed or documented so other people using a brepl won't rip out their hair.

[:tag {:style style-map} ...] causes malformed style tag

The following code:

(defn interaction-row [data owner chans]
  (let [[date people subject role] data]
    (om/component
     (html/html
      [:div.interaction-row {:style {:position "relative"}}]))))

produces this html:

<div class="interaction-row"
     style="cnt:1px;arr::position,relative;cljs$lang$protocol_mask$partition1$:4px;cljs$lang$protocol_mask$partition0$:16123663px;" data-reactid=".r[oq0o].[2].[0].[0][0]">
</div>

Edit to add:

Trying to specify the style attribute as a string:

(defn interaction-row [data owner chans]
  (let [[date people subject role] data]
    (om/component
     (html/html
      [:div.interaction-row {:style (str "position:relative;")}]))))

Causes an Invariant Violation in React, presumably it expects a javascript object.

Finally, using a js-obj produces the desired output:

(defn interaction-row [data owner chans]
  (let [[date people subject role] data]
    (om/component
     (html/html
      [:div.interaction-row {:style #js {:position "relative" :width "950px;"}}]))))

Perhaps something Sablono should smooth over, similar to its treatment of :className vs .className?

Om next compatibility

I'm very new to the project, but I will use it in the upcoming project since Om next does provide some exciting features. I would love to know if sablono would work fine with Om next, if not, will it does ?

Thanks

data-* attributes that work via om.dom don't work via Ŝablono

Using these versions:

[om "0.2.3"]
[sablono "0.2.1"]

The following om.dom results in the two data-* attributes appearing in the DOM:

(dom/div #js {:data-toggle "modal"
              :data-target "#myModal"}
         "A modal target")

But neither of the following two uses of data* causes the attributes to appear on the DOM:

[:div {:data-toggle "modal" :data-target "#myModal"}
 "A modal target"]

[:div {:dataToggle "modal" :dataTarget "#myModal"}
 "A modal target"]

Screenshot of all three variants side-by-side in the Elements panel:

screen shot 2014-01-21 at 3 08 35 pm

Utility macro for CSS maps

The sablono.core/html macro internally uses sablono.compiler/compile-map-attr and sablono.util/camel-case-keys to convert ClojureScript maps containing CSS attributes to JavaScript maps usable by React, at compile time.

It would be convenient if a similar sablono.core/css macro or some such were created that did the same conversion at compile time by itself, so that style maps could be conveniently be created in other places, and without having to convert ClojureScript map to JavaScript object at every component render:

(css {:font-family "Comic Sans"})
; would expand to #js {:fontFamily "Comic Sans"} at compile time

Classes get rendered separated with "," instead of " "

Using [sablono "0.2.12"]: dom-nodes with multiple classes, get incorrectly rendered to the dom.

For instance: (html [:div.c1.c2 "text"]) gets rendered to <div class="c1,c2">text</div>, which should have been <div class="c1 c2">...

keywords in map literals inside callback functions are camel cased.

[:button
 {:on-click (fn [e]
        (let [kw :a-b] (println kw (pr-str {kw "asb"}) (pr-str {:a-b "asb"}))))} "Click"]

 => :a-b {:a-b "asb"} {:aB "asb"}

This is happening in a fairly large component, but not all of my components. I wasn't able to isolate it. Defining the callback outside of the html data works around the issue.

I can try to reproduce this in a smaller example if it isn't obvious what the issue is. It looks like the html macro is sometimes camel casing keywords defined in callback functions.

Please make sure Austin does not leak as dependency

I don't exactly understand what the issue is but somehow Austin seems to get around the :dev profile and ends up in a lot of pom.xml files of Clojurescript projects. I don't know how to fix this so I'll leave it here for now. Will get back if/when I figure out what's going on.

dev dependencies pulled in?

Not sure if this is an issue, or not, but for the 0.1.1 version, I somehow get some of the dev dependencies pulled in.

lein deps :tree

reveals

[sablono "0.1.1"]
   [com.cemerick/clojurescript.test "0.2.1"]
   [com.keminglabs/cljx "0.3.1"]
     [com.cemerick/piggieback "0.1.0"]
     [org.clojars.trptcolin/sjacket "0.1.0.3"]
       [net.cgrand/parsley "0.9.1"]
       [net.cgrand/regex "1.1.0"]
     [org.clojure/core.match "0.2.0"]
     [watchtower "0.1.1"]

and:

WARNING!!! version ranges found for:
[sablono "0.1.1"] -> [com.keminglabs/cljx "0.3.1"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [org.clojure/clojure "[1.3.0,)"]
Consider using [sablono "0.1.1" :exclusions [org.clojure/clojure]].
[sablono "0.1.1"] -> [com.keminglabs/cljx "0.3.1"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [net.cgrand/regex "1.1.0"] -> [org.clojure/clojure "[1.2.0,)"]
Consider using [sablono "0.1.1" :exclusions [org.clojure/clojure]].
[sablono "0.1.1"] -> [com.keminglabs/cljx "0.3.1"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [net.cgrand/parsley "0.9.1"] -> [org.clojure/clojure "[1.2.0,)"]
Consider using [sablono "0.1.1" :exclusions [org.clojure/clojure]].
[sablono "0.1.1"] -> [com.keminglabs/cljx "0.3.1"] -> [org.clojars.trptcolin/sjacket "0.1.0.3"] -> [net.cgrand/parsley "0.9.1"] -> [net.cgrand/regex "1.1.0"] -> [org.clojure/clojure "[1.2.0,)"]
Consider using [sablono "0.1.1" :exclusions [org.clojure/clojure]].

I can fix it with:

[sablono "0.1.1" :exclusions [org.clojure/clojure
  com.keminglabs/cljx com.cemerick/clojurescript.test]]

in my :dependencies block in project.clj.

I noticed this mainly because clojurescript test stuff was included in my uberjar (not that it takes up that much space).

Is this something you fix on your end? Pulling in cljs-ajax doesn't pull in the test dependency, but pulling in dommy 0.1.2 does pull in crate and cljs-test.

Super glad you're doing this library, by the way.

Odd Nesting behaviour

This [:li "298" [:span "Things"]]
outputs: <li><span>298</span><span>Things</span></li>

notice the number came out wrapped in a span.

While [:li "298"]
outputs: <li>298</li>

Raw HTML content

There doesn't seem to be a way to embed raw HTML into element's content. Say I want to render markdown text and put that somewhere into sablono generated content.

likely incorrectly parenthesized tests

in core-test you have a lot (all?) your tests defined as:

(is (= (html-str [:div])) "<div></div>")

This will always pass. I believe that you intended this to look more like:

(is (= (html-str [:div]) "<div></div>"))

Sugar for creating Om components

Using Sablono, I always find myself writing

(defn foo-widget [data owner]
  (om/component
    (html [:div#foo
           ... all the contents ... ])))

How about this?

(defcomponent foo-widget [data owner]
  :div#foo
  ... contents ...)

Less indentation and parens would make me happy, and it could just expand to the former representation — in the more complex situations, like where you want a will-mount method or similar, you'd continue to use (html ...).

Using Sablono with React with Add-Ons

As of Sablono 0.3.3 and cljsjs/react-with-addons 0.12.2-4, if a clean ClojureScript project includes both cljsjs/react-with-addons and sablono in its dependencies and is then compiled in optimizations-none mode, then the compiler will use React without Add-Ons and not use React with Add-Ons, making React.addons completely unavailable in any of the project’s code.

Removing Sablono from the project’s dependencies, cleaning compiled files, and then recompiling causes React with Add-Ons to be used.

It should be possible to use React with Add-Ons along with Sablono in the same project, though I’m unsure whether this is at all possible with the compiler’s current behavior.

id after class NPE

[:div.well#setup] throws NPE
[:div#setup.well] works fine.

Caused by: java.lang.NullPointerException: 
               core.clj:1505 clojure.core/name
                 util.clj:84 sablono.util/react-symbol
                 util.clj:89 sablono.util/react-fn
            compiler.clj:165 sablono.compiler/eval4650[fn]
            MultiFn.java:227 clojure.lang.MultiFn.invoke
            compiler.clj:170 sablono.compiler/eval4657[fn]
            MultiFn.java:227 clojure.lang.MultiFn.invoke
            compiler.clj:199 sablono.compiler/compile-seq[fn]
             LazySeq.java:42 clojure.lang.LazySeq.sval
             LazySeq.java:60 clojure.lang.LazySeq.seq
                 RT.java:484 clojure.lang.RT.seq
                core.clj:133 clojure.core/seq
               core.clj:2780 clojure.core/dorun
               core.clj:2796 clojure.core/doall
            compiler.clj:197 sablono.compiler/compile-seq
            compiler.clj:209 sablono.compiler/compile-html
             RestFn.java:137 clojure.lang.RestFn.applyTo
                core.clj:619 clojure.core/apply
                 core.clj:13 sablono.core/html
             RestFn.java:445 clojure.lang.RestFn.invoke
                AFn.java:167 clojure.lang.AFn.applyToHelper
             RestFn.java:132 clojure.lang.RestFn.applyTo
           AFunction.java:29 clojure.lang.AFunction$1.doInvoke
             RestFn.java:137 clojure.lang.RestFn.applyTo
                core.clj:621 clojure.core/apply
           analyzer.clj:1377 cljs.analyzer/macroexpand-1
           analyzer.clj:1413 cljs.analyzer/analyze-seq
           analyzer.clj:1506 cljs.analyzer/analyze[fn]
           analyzer.clj:1499 cljs.analyzer/analyze
                 [org.clojure/clojurescript "0.0-2173"]
                 [om "0.5.1"]
                 [sablono "0.2.7"]

Dependency Cycle

Cursive can not load file to repl, which contains sablono.core as a requirement:
2014-11-03 8 31 11

Is there something wrong in sablono?

Compile warning about `number` when using `count`

I'm using sablono "0.2.16" with om "0.6.2" and clojurescript "0.0-2202".
Using plain cljs count within an html macro within an om render gives a compiler warning about undefined var number.
I'm not sure if this is a problem with Sablono or ClojureScript's compiler.
I cannot expand the macro within plain Clojure because trying to require Sablono returns a cljs.tagged_literals.JSValue class not found error.

Here's some code demonstrating the issue.
(Note that the code actually runs fine; the problem is just the unnecessary compiler warning)

(ns scratch
  (:require [om.core :as om :include-macros true]
            [sablono.core :as html :refer-macros [html]]))


(html [:div (count [1 2 3])])
;;Compiles fine, no warnings

(reify
  om/IRender
  (render [_]
    (html [:div (count [1 2 3])])))

;;Gives compile warning
;;WARNING: Use of undeclared Var scratch/number at line...

Uncaught TypeError: Cannot read property 'call' of undefined

When trying to render an input I get the error:

Uncaught TypeError: Cannot read property 'call' of undefined

which in the browser console points to the html macro call

(html [:div [:input]]) ;; doesn't work
(html [:div [:select]]) ;; works

using version 0.3.4 with om 0.8.0-rc1

Doesn't work in advanced optimization mode

sablono generated <div href="jumbotron"/> from [:div.jumbotron] in advanced mode for me. No problems in normal mode though. Not sure what caused this, but it's quite inconvenient. :)

No such namespace: cljsjs.react.dom.server with latest Om Next

Caused by: clojure.lang.ExceptionInfo: No such namespace: cljsjs.react.dom.server, could not locate cljsjs/react/dom/server.cljs, cljsjs/react/dom/server.cljc, or Closure namespace "cljsjs.react.dom.server" {:tag :cljs/analysis-error}

Trying to use sablono 0.5.0 with Om Next gives the error above.

Thanks.

React warning

React complains about missing "key" prop in the array, this happens only for dynamic children.

The exact message is: "Each child in an array should have a unique "key" prop. Check the render method of undefined. See http://fb.me/react-warning-keys for more information."

(html [:div 
             [:h2 "Menu"]
             [:ul (for [component (om/build-all menu-item (:items data) {:init-state {:channel channel}})]
                    component)]])

Same with build

(html [:div 
             [:h2 "Menu"]
             [:ul (for [item (:items data)] 
                  (om/build menu-item item {:init-state {:channel channel}})]])

No such message when using default notation:

(dom/div nil
               (dom/h2 nil "Menu")
               (apply dom/ul nil
                      (om/build-all menu-item (:items data) {:init-state {:channel channel}}))))))

How to work around React errors (calling direct / unique key)

I'm building a select field, using Sablono I get React errors. I'm struggling to see how to avoid them short of completely switching to using pure om controls.

This is my problem code:

(defn Select [props owner]
  (om/component
    (let [{:keys [label value on-change help placeholder disabled
                  options default-option default-value]} props
          default-value (or default-value "")
          default-option (or default-option "Please select")]
      (html [:div.form-group
             (if label [:label label])
             [:select.form-control {:value       (or value default-value)
                                    :placeholder placeholder
                                    :disabled    disabled
                                    :on-change   on-change}
              (if (and options)
                [:option {:key default-value :value default-value :disabled true} default-option])
              (om/build-all Option options)]
             (if help [:p.help-block help])]))))

I get three react warnings:

  1. Warning: Something is calling a React component directly. Use a factory or JSX instead. See: http://fb.me/react-legacyfactory
  2. Each child in an array should have a unique "key" prop. Check the renderComponent call using . See http://fb.me/react-warning-keys for more information.
  3. Warning: transferPropsTo is deprecated. See http://fb.me/react-transferpropsto for more information.

And this is what I ended up with to avoid the problem, note use of (apply dom/select...).

(defn Select [props owner]
  (om/component
    (let [{:keys [label value on-change help placeholder disabled
                  options default-option default-value]} props
          default-value (or default-value "")
          default-option (or default-option "Please select")]
      (html [:div.form-group
             (if label [:label label])
             (apply dom/select #js {:className   "form-control"
                                    :value       (or value default-value)
                                    :placeholder placeholder
                                    :disabled    disabled
                                    :onChange   on-change}
                    (if options
                      (dom/option #js {:value default-value :disabled true} default-option))
                    (om/build-all Option options))
             (if help [:p.help-block help])]))))

My feeling is that I'm missing some standard sablono tricks for handling the case of many children in a vector.

Can't mix .className and {:className "class"} syntax

We have code that looks like this using Sablono:

[:div {:className (string/join " " [(if healthy? "healthy" "unhealthy" "account-card"])}...]

Whereas with dommy, we can have the static className declared with the haml-style and the dynamic class name computed

[:div.account-card {:className (if healthy? "healthy" "unhealthy")])}...]

And they'll be merged together. It makes the code considerably shorter and easier to read.

Also, as an aside, why :className instead of :class?

Finally, thank you for Sablono! It's fantastic for us porting dommy/hiccup code over!

WARNING: No such namespace: clojure.lang.PersistentHashMap

This isn't really a bug report, because I am still not certain where the bug actually is. I'm hoping you will either have an idea, or perhaps some pointers.

I've spent the better part of the last days trying to figure out why I'm getting warnings like:

WARNING: No such namespace: clojure.lang.PersistentHashMap, could not locate clojure/lang/PersistentHashMap.cljs, clojure/lang/PersistentHashMap.cljc, or Closure namespace "" at line 147

I get these warnings only in a certain environment: when compiling using figwheel, but not when launching via lein figwheel, but starting figwheel directly from the application, as the chestnut template does.

I managed to narrow it down to:

(rum/defc test-component-1 [params]
  (let [{:keys [a b]} params]
    ;; The above line causes a 'WARNING: No such namespace: clojure.lang.PersistentHashMap, could not locate
    ;; clojure/lang/PersistentHashMap.cljs, clojure/lang/PersistentHashMap.cljc, or Closure namespace ""' to be
    ;; generated, with line number pointing to the line with the defc declaration.
    [a b]))

(rum/defc test-component-2 [params]
  (let [a (:a params) ;; This component does not generate any warnings.
        b (:b params)]
    [a b]))

In other words, if a component does map destructuring inside a let form, the warnings appear.

I checked and Rum doesn't really do much with the body, passing it to sablono's compile-html.

Do you have any idea what could be causing these warnings?

how to render to html string directly ?

I want to render a component to html string directly, so I can attach to dom element directly, for transient animation purpose.

I saw in test code we have html-str ? do we have this function in core ?

(is (= (html-str [:div [:p] [:br]]) "


")

Sequences and om/react keys

I'm rendering table headers with om and sablono using a for loop:

(html [:table
       [:tbody
        [:tr (for [k [:last-name :first-name :date]]
               [:th (name k)])]]])

This generates a react warning, "Each child in an array should have a unique \"key\" prop."

But in this case, I think I just want a TR with some TH children, not a react "array". Is there a way to not generate react components for each element of a for loop, in the case of static data such as above?

Or should I be assigning a unique key to each TH, even in the case of an unchanging sequence?

Adding attributes to parent component yields completely new child components

When a parent component has a completely new attribute added, completely new child components are created. This causes child components to lose state and for init-state to be called multiple times.

This only happens when a completely new attribute is added, e.g. adding class="some-class" when no class attribute existed. Rendering works as expected if updating an existing attribute, e.g. changing class="some-class" to class="some-class some-new-class".

This is with om 0.6.2, sablono 0.2.16, and clojurescript 0.0-2173.

Here's the smallest case I could show with a broken example, one sablono working example, and an om/dom working example:

(ns test-sablano.core
  (:require [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]
            [sablono.core :as html :refer-macros [html]]))

(enable-console-print!)

(def sablono-broken-counter (atom 0))
(def sablono-working-counter (atom 0))
(def dom-counter (atom 0))

(defn sablono-broken-widget-with-state [data]
  (reify
    om/IInitState
    (init-state [_]
      (swap! sablono-broken-counter inc)
      {:text (str "Sablono broken count " @sablono-broken-counter)})
    om/IRenderState
    (render-state [_ {:keys [text]}]
      (html [:div text]))))

(defn sablono-working-widget-with-state [data]
  (reify
    om/IInitState
    (init-state [_]
      (swap! sablono-working-counter inc)
      {:text (str "Sablono working count " @sablono-working-counter)})
    om/IRenderState
    (render-state [_ {:keys [text]}]
      (html [:div text]))))

(defn dom-widget-with-state [data]
  (reify
    om/IInitState
    (init-state [_]
      (swap! dom-counter inc)
      {:text (str "Dom count " @dom-counter)})
    om/IRenderState
    (render-state [_ {:keys [text]}]
      (dom/div nil text))))

(defn sablono-parent-broken [data]
  (om/component
   (html
    [:div (when (:toggle data) {:style {:display "block"}})
     (om/build sablono-broken-widget-with-state data)])))

(defn sablono-parent-working [data]
  (om/component
   (html
    [:div {:style {:display (when (:toggle data) "block")}}
     (om/build sablono-working-widget-with-state data)])))

(defn dom-parent [data]
  (om/component
   (html
    (dom/div (when (:toggle data) (clj->js {:style {:display "block"}}))
               (om/build dom-widget-with-state data)))))

(defn widget [data]
  (reify
   om/IRender
   (render [_]
     (html [:div
            (om/build sablono-parent-broken data)
            (om/build sablono-parent-working data)
            (om/build dom-parent data)
            [:button {:onClick #(om/transact! data [:toggle] not)}
             "Toggle class"]]))))

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

Re-evaluating html macro in LT & browser REPL

The code indeed looks much better with sablono.core html macro. One issue I have with it: when using om/dom, I was able to re-evaluate om/root call, and have the browser updated via LT-2-browser websocket. It is a very handy feature and makes a very speedy development. With sablono, I can no longer do this, because the component function gets injected to browser sans macro, or unexpanded or w'ever, I'm a Clojure(Script) noob. Perhaps this is relevant? https://groups.google.com/forum/#!topic/light-table-discussion/SeArJCwXeT4

Compilation error on 0.3.4

I'm trying to compile the following form:

(html
             (if (empty? page-state)
               [:div.main.container
                [:div.spinner
                 [:div.double-bounce1] [:div.double-bounce2]]]
               [:div.race-page
                (u/client-side (race-cover page-state route state owner))
                [:div.container
                 {:class (if (:nav-open? @state)
                           "main down"
                           "main")}
                 (case section
                   "startlist" (startlist-section page-state)
                   "results" (results-section page-state)
                   ("general" "schedule" "course" "directions")
                   (render-content section page-state)
                   [:div.404
                    [:div.page-section.well "That race page section does not exist!"]])]
                (render-sponsors page-state)]))

And I see this error:

aused by: java.lang.IllegalArgumentException: Don't know how to create ISeq from: java.lang.Long
                 RT.java:505 clojure.lang.RT.seqFrom
                 RT.java:486 clojure.lang.RT.seq
                core.clj:135 clojure.core/seq
               core.clj:2539 clojure.core/every?
            analyzer.clj:587 cljs.analyzer/eval1269[fn]
            MultiFn.java:251 clojure.lang.MultiFn.invoke
           analyzer.clj:1665 cljs.analyzer/analyze-seq

The error's coming from the second branch of the "if" statement. (It goes away if I duplicate the first branch.)

Any advice on how I might restructure this?

transferPropsTo is now deprecated in React

Hi!

transferPropsTo is now deprecated in React: https://gist.github.com/sebmarkbage/a6e220b7097eb3c79ab7

It’s used in interpreter/wrap-form-element
https://github.com/r0man/sablono/blob/master/src/sablono/interpreter.cljx#L35

It generates warning every time form element is used:

Warning: transferPropsTo is deprecated. See http://fb.me/react-transferpropsto for more information.

BTW may I ask, what is the motivation behind wrapping React input elements with wrapper? Why don’t just use raw React inputs?

Custom Tags

I know it explicitly says NOT to do this in the README... BUT, let me make the pitch.

I've written a bunch of components in Om-Bootstrap that could easily be hooked into Sablono's renderer, if I were able to extend specific (namespaced) keywords in the markup to call out to my om-bootstrap components.

For example,

[:div
  [::b/button {:bs-style "primary"}
    [:a "Anchor tag"]]]

::b/button would pass the options and children into om-bootstrap.button/button.

What do you think? Is this a feature you guys would be interested in?

Support JS Dates in Content

System:

  • ClojureScript 0.0-2138
  • Om 0.3.0
  • Sablono 0.2.3

Thank you for the excellent library.

If Sablono encounters a JavaScript Date object/literal as the content of an element, it compiles out to an element with no content. I believe a better way to support Date literals, which both Clojure and ClojureScript have, would be to use its default string value at a minimum.

Spurious React key warnings if html macro chooses to interpret

In this diff, the red version generates React key warnings, and the green version does not.

image

The html macro compile path does not generate warnings, but if the macro takes the interpreter path, it passes react an array rather than applying children as varargs.

html doesn't exists warning

to reproduce:
lein new mies-om ommm

add [sablono "0.2.5"] and update clojurescript to 0.0-2156 to project.clj
and copy from example

(ns ommm.core
  (:require [om.core :as om :include-macros true]
            [om.dom :as dom :include-macros true]
            [sablono.core :as html :refer [html] :include-macros true]))

(enable-console-print!)

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

(om/root {} widget js/document.body)
lein cljsbuild once
Compiling ClojureScript.
Compiling "ommm.js" from ["src"]...
WARNING: Referred :var sablono.core/html does not exist at line 1 src/ommm/core.cljs

<input> defaults to being a controlled component

I noticed this issue when I was working on my toy project.

Specifically, upgrading to the latest version of sablono (version 0.2.5) made it impossible to enter text into the "What needs to be done" input shown in the image below:

I'm running on the latest Mac OSX (10.9.1), with the project dependencies specified in this file -- https://github.com/TanYewWei/pedestal-om-todo/blob/master/project.clj


Everything works with sablono 0.2.3 and below.

It seems like inputs are given a value attribute by default, and React then treats that input as a controlled component.

The problem for my app above was that adding a defaultValue attribute did not change my input into an uncontrolled component (as I wanted).

I think this behaviour was introduced in the following commit -- 8bcbef9

Maybe a possible fix would be to introduce an arity-4 version of sablono.core/input-field function, which allows specifying a defaultValue, and then using that defaultValue in place of value if present?

Any ideas on what could be the problem, and how best to go about fixing it?

Thanks.

support the obscure nobr html tag?

formative emits this tag under some conditions. It's supported by pretty much every browser. However, React.DOM contains no such tag, and the w3c discourages its use.

I would suggest against hacking something together - this should be implemented in React, not here. Still, it's probably better not to throw a show-stopping exception - rather, I'd suggest doing something like emitting a [:span {:style {white-space "nowrap"}}]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.