Hi all,

I've recently released Clidget v0.1.0, a new lightweight CLJS state utility
that allows you to build UIs through small, composable widgets.

Quick links:

   - GitHub <https://github.com/james-henderson/clidget> (README, Rationale
   and Getting Started)
   - Sample 'counter'
application<https://github.com/james-henderson/clidget/tree/master/clidget-sample>
   - TodoMVC 
implementation<https://github.com/james-henderson/clidget/tree/master/todomvc>

The fundamental idea behind Clidget is to ensure that you can write widgets
declaratively, in traditional Clojure style, as a function that *takes in
immutable values and returns a DOM element*. Then, rather than calling that
function with values, you* call it with atoms*, and Clidget figures out
when each widget needs to be re-rendered.

So, a counter widget would look something like this (using Dommy to create
the DOM element and core.async for event handling - you're free to choose
whatever you like):

(:require [cljs.core.async :as a]
          [dommy.core :as d]
          [clidget.widget :refer [defwidget] :include-macros true])
(:require-macros [dommy.macros :refer [node]]
                 [cljs.core.async.macros :refer [go-loop]]))

(defwidget counter-widget [{:keys [counter]} events-ch]
  (node
   [:div
    [:h2 "counter is now: " counter]
    [:p
     (doto (node [:button "Increment counter"])
       (d/listen! :click #(a/put! events-ch :inc-counter)))]]))



To include a widget in the page, call it (it’s just a function!), but
provide it with the *atoms* that it needs to watch:

(set! (.-onload js/window)
      (fn []
        (let [!counter (atom 0)
              events-ch (doto (a/chan)
                          ;; 'watch-events!' implemented below
                          (watch-events! !counter))]

          (d/replace-contents! (.-body js/document)
                               (counter-widget {:!counter !counter}
events-ch)]))))

(I'm prefixing the atom with '!', to easily differentiate between
atoms and values)



Finally, we implement watch-events! (no Clidget here):


(defn watch-events! [events-ch !counter]
  (go-loop []
    (when-let [event (a/<! events-ch)]
      (when (= :inc-counter event)
        (swap! !counter inc))
      (recur))))

(We could do this inside the widget, given it's only a counter, but
it's probably better to separate it!)



*Why?!*

The main design decision behind Clidget was to favour simplicity over
performance (obviously within reason). Clidget does one thing (I hope it
does it well!) - figuring out which widgets to re-render and when. How you
update the state, render the widgets, pass the events, handle the events
etc is completely up to you.

There's no 'magic' in Clidget - you pass in atoms, it behaves pretty much
like Clojure's add-watch function. I've found this to be a really useful
quality when reasoning about applications - it makes the debugging headache
a lot more manageable.

Finally, I wanted to preserve Clojure's composability. One of the benefits
I really enjoy about working with Clojure is the ability to use many small
libraries without worrying about how they'll fit together. If you're a
Hiccup person, you can use your favourite Hiccup-like library; if you're an
Enlive/Mustache person, likewise! In Clidget, this also extends to JS
libraries - when you're working with the DOM elements that will end up on
the page, you can pass them to other JS libraries easily.


I've received a number of requests for a comparison with
Om/Cloact/React - it didn't fit into 140 characters, so it's here
<https://github.com/james-henderson/clidget/blob/master/comparison.org>.



*Feedback*


The idea behind Clidget is still very much experimental - I'd be very
interested to hear thoughts and feedback (good, bad, and 'what on
earth were you thinking?!') on the approach and/or implementation,
please let me know! GitHub, Twitter (@jarohen
<https://twitter.com/jarohen>) or through here are all good.



If you've got this far, thanks for reading!


James

-- 
-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to