2

I want to modify the visibility of a table when a button is clicked, utilizing clojurescript/javascript interop.

I've tried

{:on-click #(-> js/document                                               
 (.getElementById "db-search-result-tables")                                               
 (.-style)
 (.-display "block"))}

This is the div tag I'm calling it on.

[:div {:style {
       :display "none"}
       :id "db-search-result-tables"
        :class "db-search-results-table"}
[table-to-display]

I've also tried

(-> js/document                                               
 (.getElementById "db-search-result-tables")                                                
 (.-style)
 (.-display)
  (set! ""))

but it only displays the table momentarily, and then sets display to none again.

CambodianCoder
  • 467
  • 4
  • 14
  • Are you using Reagent and/or re-frame? (Reagent has an example of handling state here: https://github.com/reagent-project/reagent#examples) – Not so Veteran Jul 07 '19 at 21:15
  • I'm using reagent and re-frame. Thanks for linking to some examples. – CambodianCoder Jul 10 '19 at 19:10
  • I added an answer specific to re-frame, hopefully it's of some use – Not so Veteran Jul 11 '19 at 07:49
  • 1
    Yes, I found that the re-frame specific answer helped me to do exactly what I was hoping for. Putting the desired table into a "current-table-view" in the app-db and resetting it each time I clicked on a new button to show a different table was really helpful. @NotsoVeteran – CambodianCoder Jul 24 '19 at 12:22

2 Answers2

0

EDIT: This solution doesn't assume any library, based on the reading that the problem statement didn't explicitly mention any library/framework, just JS interop, modifying the DOM directly a la jQuery. Don't use this answer if you use any library or any React wrapper such as reagent.


Maybe it would be easier to create a helper function, say toggle that hides/shows the display of a given element by its ID?

(ns myproject.core)

(defn ^:export toggle [elem-id]
  (let [elem        (js/document.getElementById elem-id)
        style       (.-style elem)
        display     (.-display style)
        new-display (if (= "none" display) "block" "none")]
    (set! (.-display style) new-display)))

We find the element by its id, use a var to get the current style, get the display out of the style and compute the new value for the display attribute, then we set! it back into the display.

I used the ^:export metadata tag so that the function could be called directly from the document, like this:

    <div>
      <button onClick="myproject.core.toggle('details')">Toggle details</button>
    </div>

    <div id="details" style="display: none">
      Some details here. Some details here. Some details here. Some details here. 
    </div>

Denis Fuenzalida
  • 3,271
  • 1
  • 17
  • 22
0

This is a solution specific to re-frame. I'd suggest utilising the app-db to store the state, with a handler to change the state and a sub to retrieve the current value. Re-frame's README is a great resource for learning about this setup: https://github.com/Day8/re-frame

Direct changes to the DOM will be overridden by re-frame when it sees fit (which is why your original code was being reverted to the original component definition).

Set up subs / handlers

You could create a handler like this:

(re-frame.core/reg-event-fx
  :handlers/enable-search-results
  (fn [{:keys [db]} _]
    {:db (assoc db :show-search-results? true})

And a sub to retrieve the value:

(re-frame.core/reg-sub
  :subs/show-search-results?
  (fn [db]
    (:show-search-results? db))

Update code to use subs / handlers

Now, update your search button to dispatch to the handler:

[:button
  {:on-click #(re-frame.core/dispatch [:handlers/enable-search-results])}
  "Click to show search results"]

And update your search results div to be visible / hidden based on the sub:

(let [show-search-results? @(re-frame.core/subscribe [:subs/show-search-results?])]
  [:div {:style {:display (if show-search-results? "visible" "none"}
         :id "db-search-result-tables"
         :class "db-search-results-table"}
    [table-to-display]])

Alternatively:

(let [show-search-results? @(re-frame.core/subscribe [:subs/show-search-results?])]
  (when show-search-results?
    [:div {:id "db-search-result-tables"
           :class "db-search-results-table"}
      [table-to-display]]))

Because the app-db state is persistent, this is exactly where "mutations" like this can be controlled safely.

Not so Veteran
  • 318
  • 3
  • 9