Runtime selectors?

70 views
Skip to first unread message

Robert Campbell

unread,
Dec 26, 2009, 2:50:30 PM12/26/09
to enliv...@googlegroups.com
Is it possible to specify runtime selectors? For example, let's say I
have an HTML template:

<form>
<select id="x">
<option></option>
</select>
<select id="y">
<option></option>
</select>
<select id="z">
<option></option>
</select>
<input type="checkbox" id="chk" />
</form>

Then I have a map {:x [1 2 3] :y [\a \b] :chk true}

How might I
1) remove the "z" select since it is not represented in the map
2) populate the "x" and "y" selects with their respective values
without hard coding the selectors [:#x], [:#y]. [#z], [:#chk] at compile time.

Imagine the HTML and the map are both dynamic/user generated and any
union between the HTML and map should be populated at runtime with the
map values.

Hard coding is easy:

[:#x :option] (clone-for [op (:x some-map)]
(do-> (set-attr :value op)
(content op))))

But it's dependent on the [:#x] selector being written at compile time.

I thought about writing a macro to accept the map and expand it to a
defsnippet/template with the proper selector/transform pairs, but I'm
not sure if this is the right path.

Rob

Christophe Grand

unread,
Dec 28, 2009, 2:58:12 PM12/28/09
to enliv...@googlegroups.com
Hi Rob,

On Sat, Dec 26, 2009 at 8:50 PM, Robert Campbell <rrc...@gmail.com> wrote:
> Is it possible to specify runtime selectors?

All selectors are dynamic -- Enlive doesn't perform any optimisation
on static selectors (yet).

> For example, let's say I have an HTML template:

> <form>
>  <select id="x">
>    <option></option>
>  </select>
>  <select id="y">
>    <option></option>
>  </select>
>  <select id="z">
>    <option></option>
>  </select>
>  <input type="checkbox" id="chk" />
> </form>
>
> Then I have a map {:x [1 2 3] :y [\a \b] :chk true}
>
> How might I
> 1) remove the "z" select since it is not represented in the map

Here you must be more precise: do you want to remove all elements with
an id which isn't a key of the map?

> 2) populate the "x" and "y" selects with their respective values

user=> (compile-selector '[:#x])
(net.cgrand.enlive-html.state-machine/chain
net.cgrand.enlive-html.state-machine/descendants-or-self
(net.cgrand.enlive-html/id= "x"))

The interesting part is that :#x is backed by id=
user=> (compile-selector `[(id= "x")])
(net.cgrand.enlive-html.state-machine/chain
net.cgrand.enlive-html.state-machine/descendants-or-self
(net.cgrand.enlive-html/id= "x"))

So, the code below populates all fields whose ids are keys of the map.
(reduce (fn [html [k v]]
(transform html [(selector [(id= (name k))]) (populate-field v)]))
(html-resource "my.html")
{:x [1 2 3] :y [\a \b] :chk true})

or when you wrap everything up:
(def magic-snippet
(let [source (html-resource "my.html")]
(fn [m]
(reduce
(fn [source [k v]]
(transform source
[(selector [(id= (name k))]) (populate-field v)]))
source m))))

> I thought about writing a macro to accept the map and expand it to a
> defsnippet/template with the proper selector/transform pairs, but I'm
> not sure if this is the right path.

No, as I showed there's no need to use a macro.

I reckon that #'transform isn't easy to use, and #'at only transforms
one node. I'll fix that so one can write (using an hypothetic at+
macro):

(def magic-snippet
(let [source (html-resource "my.html")]
(fn [m]
(reduce
#(at+ %1 [(id= (name (key %2)))] (populate-field (val %2))))
source m))))

I'd like to add a facility for "automatic" (or nearly) templating but
haven't thought enough about it yet.

hth,

Christophe

Robert Campbell

unread,
Dec 29, 2009, 10:33:46 AM12/29/09
to enliv...@googlegroups.com
Hi Christophe,

Your solution works perfectly. I implemented the "populate-field"
function to correctly handle the various types of form inputs:

http://gist.github.com/265355

I'm going to be posting some template and usage examples in case
anyone else wants similar functionality.

> I'd like to add a facility for "automatic" (or nearly) templating but
> haven't thought enough about it yet.

It's a pretty powerful idea. I mean, why stop at form elements? The
idea could be expanded for dynamic injection of pretty much anything.
The application I'm building requires as much data-driven flexibility
as possible, so I might very well be expanding on this.

> Here you must be more precise: do you want to remove all elements with
> an id which isn't a key of the map?

Well, for now I need the ability to remove any form field (<input,
<select, <textarea) which does not appear in the map. I imagine a
function where you pass in an ancestor node (like the <form), a seq of
tag types (:input, :select, :textarea), and a seq of ids (:x, :y, :z)
and it will remove all tags of the specified types which do not appear
in the id seq.

I'm now trying to figure out this #1/filter part.

Rob

> --
>
> You received this message because you are subscribed to the Google Groups "Enlive" group.
> To post to this group, send email to enliv...@googlegroups.com.
> To unsubscribe from this group, send email to enlive-clj+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/enlive-clj?hl=en.
>
>
>

Robert Campbell

unread,
Feb 22, 2010, 3:44:04 AM2/22/10
to enliv...@googlegroups.com
I've generalized the code to support any number of tag population
mechanism via multimethods:

http://gist.github.com/310258

You can defmethod to add support for injecting model and reference
data/taxonomy values into particular tags.

Rob

Christophe Grand

unread,
Mar 3, 2010, 4:29:17 PM3/3/10
to enliv...@googlegroups.com
Hi Rob,


On Mon, Feb 22, 2010 at 9:44 AM, Robert Campbell <rrc...@gmail.com> wrote:
I've generalized the code to support any number of tag population
mechanism via multimethods:

http://gist.github.com/310258

You can defmethod to add support for injecting model and reference
data/taxonomy values into particular tags.

That's very nice and inspiring!

Some links that may be relevant to smart templates:
http://blog.ianbicking.org/on-form-libraries.html
http://formencode.org/htmlfill.html
http://beebole.com/pure/

Dear users, which usecases are you envisioning for smart templates? (Reporting, form filling, composition etc.)

Thanks,

Christophe

David Nolen

unread,
Mar 3, 2010, 5:02:49 PM3/3/10
to enliv...@googlegroups.com
On Mon, Feb 22, 2010 at 3:44 AM, Robert Campbell <rrc...@gmail.com> wrote:
I've generalized the code to support any number of tag population
mechanism via multimethods:

http://gist.github.com/310258

You can defmethod to add support for injecting model and reference
data/taxonomy values into particular tags.

Rob


This look interesting, do you have some examples of this in action?

David
 

Robert Campbell

unread,
Mar 4, 2010, 7:59:47 AM3/4/10
to enliv...@googlegroups.com
> Some links that may be relevant to smart templates:
Thanks Christophe, I wasn't aware this had a name. I'm very happy to
have additional references for how other people have solved this
problem.

> This look interesting, do you have some examples of this in action?

The only live example I can offer is my own, and I'm struggling to
explain it concisely.

I have a user-editable form, whose input elements represent possible
coordinates in a product configuration matrix. Some other page
elements - lists, spans, etc. - need to provide information on the
configuration selected from the matrix. The code has to take any
matrix, of variable size and values, and wire them up at runtime to
any view. In other words, it takes an ever changing model and a view
and does its best to wire them up at runtime.

I've posted two additional pieces of my solution:

1) http://gist.github.com/321663 - this takes a nested map,
representing the configuration matrix, and flattens it so that:

{:size "16x16"
:insert "Feather/Down"
:price 2990
:fabric {
:id "A123"
:note "This fabric made from 100% cotton"
}}

becomes

{:size "16x16"
:insert "Feather/Down"
:price 2990
:fabric__id "A123"
:fabric__note "This fabric made from 100% cotton"
}}

so that it maps to UI elements: <span id="fabric__note" />, <select
id="size" />, etc.

2) http://gist.github.com/321662 - this takes UI form selections
(coordinates) and locates the best match in the configuration matrix.
It also includes one ugly hack in the "points" function. Essentially I
need the ability to weigh field matches (size, fabric, etc)
differently. To keep it completely general, I'd like to store this
weighting in the database and attach it to each field. For now,
however, you can see I give massive preference to the :id fields.

A practical example is that a user selects: size=14x14,
insert=PolyFill, fabric__id=D321 and I need to find the closest valid
match.

3) http://gist.github.com/310258 - this takes that "best match" and
renders it - with the taxonomy - to the UI.

The result of all this is that you can support the selection of
arbitrarily complex configurations by only changing views and records,
both of which are dynamic, user modifiable data stored in by db
(Couch).

The "smart template" concept is about 1/3 of this implementation, but
I believe it will be growing much more important once I start adding
clients to the platform and the need for custom UIs and product
configurations increases.

Rob

Meikel Brandmeyer

unread,
Mar 6, 2010, 1:58:10 PM3/6/10
to enliv...@googlegroups.com
Hi,

> 2) http://gist.github.com/321662 - this takes UI form selections
> (coordinates) and locates the best match in the configuration matrix.
> It also includes one ugly hack in the "points" function. Essentially I
> need the ability to weigh field matches (size, fabric, etc)
> differently. To keep it completely general, I'd like to store this
> weighting in the database and attach it to each field. For now,
> however, you can see I give massive preference to the :id fields.

Ah. I remember you asking on #clojure about splitting the keys. I did
something similar for Swing (http://bitbucket.org/kotarak/jazz), but
it's quite hacky. I'll definitively look into your solution.

Sincerely
Meikel

Robert Campbell

unread,
Mar 7, 2010, 4:08:57 AM3/7/10
to enliv...@googlegroups.com
Hi Meikel,

Jazz - and many of the "smart templating" systems Christophe mentioned
- support building/generating the actual form. I've thought about this
in the past and decided that it's simply too complex trying to
anticipate every customization, layout, omittance, etc. and instead
put my effort into a sort of fuzzy-wiring, where the application does
its best to wire up the model to whatever form the client happens to
implement. Since my forms (views) are user-editable and stored in a
document database, this method works well for me and maximizes user
control. Swing is, of course, a different story. I'd love to hear your
thoughts on dynamic form generation as your project progresses.

Rob

Reply all
Reply to author
Forward
0 new messages