2

Say I have the following map:

(def m {"a" {"d" {}
             "e" {}}
        "b" {"f" {}
             "g" {"h" {}
                  "i" {}}}
        "c" {}})

I need to render it like this:

(om.dom/div #js {} "a" 
  (om.dom/div #js {} "d")
  (om.dom/div #js {} "e"))

(om.dom/div #js {} "b" 
  (om.dom/div #js {} "f")
  (om.dom/div #js {} "g"
    (om.dom/div #js {} "h")
    (om.dom/div #js {} "i")))

(om.dom/div #js {} "c")

How would I go about doing this? I have messed around with clojure.walk, but couldn't get it to call om.dom/div on the leaves first, then the direct parents, etc.

I am thinking that the solution might involve mapping a recursive function over the vals of a given sub-map. It would break the map apart until it sees a leaf and then bubble the om.dom/div calls back up the map.

So far I have this function:

(defn build-tree [data]
  (apply dom/div #js {}
         (->> (clojure.walk/postwalk
                #(cond (map? %) (vec %)
                       (vector? %) %
                       (string? %) %) data)
              (clojure.walk/postwalk
                #(if (and (vector? %) (string? (first %)))
                   (apply dom/div #js {} %) %)))))

Which results in:

With this in the inspector:


Bonus points for generating nested dom/ul and dom/li elements..

4 Answers4

2

When solving problems of this type, sometimes it's helpful to generate tree structure resembling the desired call tree at the REPL. Once the result looks all right, converting it to an actual call tree is generally straightforward.

For example, here's a function to generate the om.dom/div call tree for your example; to use in ClojureScript, tweak as indicated in the comment:

(defn div [m]
  (for [[k v] m]
    ;; replace with (apply om.dom/div #js {} k (div v)) in CLJS
    (list* 'om.dom/div {} k (div v))))

Example call:

(div {"a" {"d" {}
           "e" {}}
      "b" {"f" {}
           "g" {"h" {}
                "i" {}}}
      "c" {}})

Output from the above:

;; in CLJS, you'll want to use this as the ... in
;; (apply create-container-component initial-args ...)
((om.dom/div {} "a"
   (om.dom/div {} "d")
   (om.dom/div {} "e"))
 (om.dom/div {} "b"
   (om.dom/div {} "f")
   (om.dom/div {} "g"
     (om.dom/div {} "h")
     (om.dom/div {} "i")))
 (om.dom/div {} "c"))
Michał Marczyk
  • 83,634
  • 13
  • 201
  • 212
0

The DOM-structure generated by the following code looks to be quite close to what you are looking for with the exception that the child nodes (even if there aren't any) are wrapped in a div, this should be easy to fix:

(defn tree-component
  [tree]
  (apply dom/div nil
         (keep (fn [[k v]]
                 (when k
                   (dom/div nil k
                            (tree-component v))))
               tree)))

(defn app-view
  [data owner]
  (reify
    om/IRender
    (render [_]
      (tree-component
        {"a" {"d" {}
              "e" {}}
         "b" {"f" {}
              "g" {"h" {}
                   "i" {}}}
         "c" {}}))))
ponzao
  • 20,684
  • 3
  • 41
  • 58
0

clojure.walk is great but it sometimes make it harder. I've got this:

 (defn recursive-component [el]
   (cond
    (empty? el) nil
    (vector? el) (apply dom/div nil 
                      (first el)
                      (recursive-component (second el)))
    (map? el) (map recursive-component el)))


 (defn main-component [data owner]
   (om/component
    (apply dom/div nil "Main"
           (recursive-component m))))
sbensu
  • 1,491
  • 10
  • 9
0

For ul and li:

(defn recursive-component [el]
  (cond
   (and (vector? el) (empty? (second el)))
   (dom/li nil (first el))

   (vector? el) (apply dom/ul nil 
                     (first el)
                     (recursive-component (second el)))

   (map? el) (map recursive-component el)))

but it displays as li the {:c {}} element.

sbensu
  • 1,491
  • 10
  • 9