Have you tried NOT using a macro at all? This code does not need to be a
macro at all if you ask me.
Just a little sketch but things could look just about the same without any
macros at all:
(let [form {:editing? true
:values form-values
:validation validation-report
:on-change handle-form-change}]
(form/tag form
(form/text form :name)
(form/number form :age)))
;; in-ns 'form
(defn text [form field]
[text-component {:id field
:value (get-in form [:values field])
...}])
(defn tag
[{:keys [editing?] :as form} & children]
(into [:div.form.horizontal
{:class (if editing? "editing" "editable")}]
children))
Use macros very sparingly, most of the time data and functions are just
better.
Just my 2 cents,
/thomas
On Wednesday, September 30, 2015 at 10:29:30 PM UTC+2, Colin Yates wrote:
>
> Hi all,
>
> I am banging my head against the wall - I think it is obvious but I have
> started too long:
>
> The use-case is that I want a form which takes a set of children. The form
> also takes in some form-wide state, like the form-wide validation, the
> values for each item etc. I want the macro, for each child, to decorate
> that child by extracting the validation errors and value from the form-wide
> state.
>
> So, assuming:
> - validation looks like {:name "Duplicate name" :age "You must be at
> least 0"}
> - form-values looks like {:name "a-duplicate-user" :age -1}
>
> then my form might look like:
>
> (form {:editing? true :values form-values :validation validation-report
> :on-change handle-form-change}
> [form/text {:id :name}]
> [form/number {:id :age}])
>
> After the macro I want the following code:
>
> [:div.form.horizontal
> {:class "editing"}
> [form/text {:id :name :value "a-duplicate-user" :errors "Duplicate name"
> :on-click (fn [e] (handle-form-change :name (-> e .target .value])]
> [form/number {:id :age :value "-1" :errors "You must be at least 0"
> :on-click (fn [e] (handle-form-change :age (-> e .target .value))]]
>
> However, ideally the macro would _not_ emit the contents of the input as
> literals but would emit code that inspects the provided parameters at
> run-time (i.e. rather than :value "a-duplicate-user" I would much prefer
> :value (-> state values :name) as that will allow me to pass in an atom for
> example.
>
> I have tried so many variations and evaluating the state (e.g. (:editing?
> state)) works fine as the emitted code has the destructured values, but
> that doesn't work for an atom.
>
> Here is my attempt at trying to emit code that interrogates the provided
> parameter.
>
> (defmacro form [state & elements]
> (let [state# state]
> `[:div.form.horizontal
> {:class (if (:editing? state#) "editing" "editable")}
> ~@(map (fn [[_ {:keys [id]} :as child]]
> (update child 1 assoc
> :editing? (:editing? state#)
> :value `(-> (:values state#) 'deref (get ~id))
> :on-change `(fn [e#]
> (js/console.log "E: "
> (cljs.core/clj->js e#))
> ((:on-change state#) ~id (-> e#
> .-target .-value)))))
> elements)]))
>
> The error I am getting is that there is such var as the gen-sym's state#
> in the namespace.
>
> The generic thing I am trying to do is remove the boilerplate from each of
> the items in the form.
>
> Any and all suggestions are welcome.
>
> Thanks!
>
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
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 [email protected].
For more options, visit https://groups.google.com/d/optout.