Fork me on GitHub

@jrychter can you share? 🙂


@martinklepsch I'm considering it in the future — though it will be a lot of work to document it and polish the edges. For now I think I'll keep using it for a while, as I find that each new edge case makes it slightly better.


It's a very different approach from what you have, though. Here's an example, a simple but realistic use:

(rum/defc edit-price-break < rum/static [{:keys [:quantity :price] :as data} submit-fn]
  (forms/form data
              (merge (xforms/pos-int :quantity)
                     (xforms/pos-number :price))
              [[:quantity fields/text]
               [:price fields/text]]
              :layout-fn (fn [{:keys [quantity price]}]
                            [:div.field quantity]
                            [:div.field price]])
              :buttons-fn (fn [correct? ok-fn cancel-fn]
                            [:div.ui.right.floated.tiny.primary.button {:class (when-not correct? "disabled")
                                                                        :on-click ok-fn}
                             [:i.arrow.up.icon] "Add"])))


The :layout-fn and :buttons-fn are optional — you get a sequential layout and a default OK button if you don't provide them. The form does instant validation (highlighting the error fields, and disabling buttons) according to validators defined in xforms (second argument to form). Example of an xform:

(defn integer [k]
  {k {:display (comp tools/or-empty k)
      :edit #(str (or (get % k) ""))
      :parse (comp tools/to-int k)
      :validate (comp integer? k)}})


A pos-int adds another validator:

(defn pos-int [k]
  (-> (integer k)
      (add-validator k (fn [{n k}] (> n 0)))))


All this provides declarative forms (and tables, too), with lots of code reuse, and yet is flexible enough to do even dynamic forms which change depending on what is selected in one of the fields.