Frage

I'm brand new to clojure and the web development stack. I'm trying to use enlive to set values in an HTML template:

(en/deftemplate project-main-page
  (en/xml-resource "project-main.html")
  [id]
  [:#project-name] (en/content (str "Name: " ((get-project id) :name)))
  [:#project-desc] (en/content (str "Desc: " ((get-project id) :desc))))

This works fine to set my two HTML elements, but it involves a repeated call to my function get-project. At the moment this just reads from a local map, but eventually it will involve some external storage access, so I'd prefer to just perform it once in this function.

I was thinking of using let:

(en/deftemplate project-main-page
  (en/xml-resource "project-main.html")
  [id]
  (let [project (get-project id)]
    [:#project-name] (en/content (str "Name: " (project :name)))
    [:#project-desc] (en/content (str "Desc: " (project :desc)))))

But this only affects the description element and ignores the name forms.

What is the best way to bind a local var within deftemplate?

War es hilfreich?

Lösung

Enlive's deftemplate macro expects a series of tag/content pairs after the args vector (the args vector is [id] in your example). You can't just stick a let in there because the macro isn't expecting a let form, so when it does its splicing everything gets messed up and results in the behavior you described above.

One way you could fix this would be to write your own deftemplate macro that allows binding definitions using the identifiers in the args vector. Example:

(alt/deftemplate project-main-page
  (en/xml-resource "project-main.html")
  [id]
  [project (get-project id)]
  [:#project-name] (en/content (str "Name: " (project :name)))
  [:#project-desc] (en/content (str "Desc: " (project :desc))))

The deftemplate macro is a simple wrapper around template, which uses snippet* and this is probably where you'd need to insert your changes:

(defmacro snippet* [nodes args & forms]
  `(let [nodes# (map annotate ~nodes)]
     (fn ~args
       ; You could add let bindings here since args are in scope
       (doall (flatmap (transformation ~@forms) nodes#)))))

The other option—which might be simpler since you don't have to muck around in the library code—would be to add a level of indirection to your get-project function to cache results. You might try the core.cache library.

Andere Tipps

If I have understood what you are trying to achieve; you could also try using the transformation macro provided by enlive.

(defn main-page [{:keys [name desc] :as project}]
 (en/transformation
   [:#project-name] (en/content (str "Name: " name)
   [:#project-desc] (en/content (str "Desc: " desc))))


(en/deftemplate project-main-page
  (en/xml-resource "project-main.html")
  [id]
   (main-page (get-project id)))

The code is untested, but I hope it conveys a different way to do what you need

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top