3

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.

4
  • Are you using Reagent and/or re-frame? (Reagent has an example of handling state here: github.com/reagent-project/reagent#examples) Commented Jul 7, 2019 at 21:15
  • I'm using reagent and re-frame. Thanks for linking to some examples. Commented Jul 10, 2019 at 19:10
  • I added an answer specific to re-frame, hopefully it's of some use Commented Jul 11, 2019 at 7: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 Commented Jul 24, 2019 at 12:22

2 Answers 2

1

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>

Sign up to request clarification or add additional context in comments.

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.