Reputation: 144
I want to create a list of html elements (which include the results of a query) which are hidden by default but the user can toggle that state. I have tried a couple different ways below as toy examples but can't get either to work.
This code correctly creates three buttons, which were alter the exps state correctly but which do not ever hide content.
(:require [reagent.core :as r] )
(def exps (r/atom [true true true]))
(defn like-component []
[:div
(for [ [i r] (map-indexed vector ["A" "B" "C"])]
[:div
[:button {:on-click #(swap! exps update-in [i] not)}]
(when (nth @exps i)
[:pre (str i r)])])])
(r/render [like-component]
(js/document.getElementById "app"))
On the other hand, the code below will create only one element but it works correctly.
(defn expandable-view [e bool]
(let [expanded (r/atom bool)]
(fn []
[:li
[:div.expandable
[:div.header {:on-click #(swap! expanded not)}
"Click me to expand and collapse"]
(if @expanded
[:div.body (allow-html :pre e)])]])))
(defn like-component []
[:ul
(vec
(for [ e ["A" "B" "C"]]
(expandable-view e true ))) ])
(r/render [like-component]
(js/document.getElementById "app"))
Edit: Possibly related: https://github.com/reagent-project/reagent/wiki/Beware-Event-Handlers-Returning-False
Upvotes: 3
Views: 1614
Reputation: 446
for
is lazy, so reagent
can't tell you're dereferencing exps
in the first code snippet.
We can workaround it by explicitly dereferencing atoms.
(defn like-component []
(apply str @exps) ;; because @exps is a vector, and reagent
;; treat vectors as hiccup component
;; we can't just put `@exps` here.
[:div
(for [ [i r] (map-indexed vector ["A" "B" "C"])]
[:div
[:button {:on-click #(swap! exps update-in [i] not)}]
(when (nth @exps i)
[:pre (str i r)])])])
Or just wrap the lazy sequence in doall
.
(defn like-component []
[:div
(doall (for [ [i r] (map-indexed vector ["A" "B" "C"])]
[:div
[:button {:on-click #(swap! exps update-in [i] not)}]
(when (nth @exps i)
[:pre (str i r)])]))])
FYI, related discussions.
any idea why the second block I posted only creates one element?
Vectors are special citizens for Reagent, they're treated as hiccup/React components.
For a working example
(defn like-component []
[:ul
(doall
(for [ e ["A" "B" "C"]]
[expandable-view e true]))])
Also notice that we are using [expandable-view e true]
to properly construct a reagent component.
For more informations, I'd strongly suggest reading Using [] instead of () and Creating Reagent Components.
Upvotes: 5
Reputation: 4235
I achieved this type of behaviour by using bootstrap. I have a list of records in a state atom which are all hash maps. I add a :visible key to each map which is used to set the appropriate bootstrap classes on the record. There is a function which toggles the :visible setting in the has map. The component renders the record with a button which toggles visibility by changing the :visible value in the map, causing the component to re-render.
(defn toggle-visibility [k h]
(let [new-v (if (= "show" (:visible h))
"hidden"
"show")]
(state/set-value-in! [(state/this-page) :host-list k :visible] new-v)))
(defn host-component [k]
(let [host (state/value-in [(state/this-page) :host-list k])]
^{:key k} [:div.panel.panel-default
[:div {:class "panel-heading show"}
[:div {:class (condp = (:status host)
"Active" "text-success"
"Inactive" "text-info"
"Unknown" "text-warning"
:else "text-danger")}
[:button {:type "button" :class "btn btn-default"
:aria-label "Expand"
:on-click #(toggle-visibility k host)}
[:span {:class (str "glyphicon "
(if (= "show" (:visible host))
"glyphicon-minus"
"glyphicon-plus"))}]]
[:strong " IPv4 Address: "] (:ipv4 host)
[:strong " Hostname: "] (:hostname host)
[:div.pull-right (str "Host ID: " (:host-id host))]]]
[:div {:class (str "panel-body " (:visible host))}
[:ul.list-group
[:li.list-group-item
[:strong "Host Status: "] (:status host)]
[:li.list-group-item
[:strong "MAC Address: "] (:mac host)]
[:li.list-group-item
[:strong "IPv6 Address: "] (:ipv6 host)]
[:li.list-group-item
[:strong "Operating System: "] (:os host)]
[:li.list-group-item
[:strong "DHCP Client: "] (:dhcp host)
[:strong " DNS Entry: "] (:dns host)
[:strong " Revers DNS Entry: "] (:reverse-dns host)]
[:li.list-group-item
[:strong "Host Type: "] (:host-type host)]
[:li.list-group-item
[:strong "Network Group: "]
(str (:network-group host) " / " (:subgroup-name host))]
[:li.list-group-item
[:strong "Managed By: "] (:management-group host)]
[:li.list-group-item
[:strong "Creation Date: "] (:created-dt host)]
[:li.list-group-item
[:strong "Last Modified Date: "] (:last-modified-dt host)]
[:li.list-group-item
[:strong "Last Seen Date: "] (:last-seen-dt host)]]]]))
Essentially, letting bootstrap handle the showing/hiding of the content, leaving the code to just toggle the visible/invisible state. The full code is on my github page at theophilusx/Arcis
Upvotes: 0