Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to make an onClick handler in Om without using anonymous function?

I want to make a click handler function for an Om component. The docs and Stack Overflow examples I've found always declare anonymous functions like this

(defn main-view [_ owner]
  (reify
  om/IRender
   (render [_]
    (let [xs (items)]
      (dom/div nil
        (om/build sub-view {:title "View A"})
        (om/build sub-view {:title "View B"})
        (dom/button
          #js {:onClick
               (fn [e] (om/transact! xs #(assoc % 1 {:text "zebra"})))}
          "Switch To Zebra!"))))))

I think it's cleaner to declare click functions outside the jsx/template area, within the component, the way it's commonly done in regular React. Is there a way do this in Om within the component? I tried this, but it doesn't work because onClick is undefined:

(defn my-component []
  (reify
    om/IRender
    (render [this]
       ; Using Sablono syntax
       (html [:h1 "Here is a heading" {:on-click 'onClick} ]))
    onClick
    (onClick [this]
      ; this part never gets executed when you click
      (.log js/console "click"))))

I'd like to avoid defining a separate function outside the component if it's possible.

like image 778
lk135 Avatar asked Jan 09 '23 03:01

lk135


2 Answers

Your question is sensible and it's about handling scope of data.

It is possible but the problem with this approach in most cases you will need local scope data from the outside code block (in your case, it's an Om component).

I would explain in code. Let's say you want to move handler function out:

(anything
 (let [a 1 b 2]
   (on-event (fn my-handler [evt] (log (+ a b (.someAttr evt)))))))

You'll end up with this which is way longer:

(defn local-data->handler [a b]
  (fn [evt] (log (+ a b (.someAttr evt)))))

(anything
 (let [a 1 b 2]
   (on-event (local-data->handler a b))))

in case you just want to move around inside the component definition:

(anything
 (let [a 1
       b 2
       my-handler (fn [evt] (log (+ a b (.someAttr evt))))]
   (on-event my-handler)))

Please note: to keep event handler work, ensure your non-anonymous function (created with defn or let) is the same as the anonymous form, especially argument list.

onClick is undefined because you use it as if it's an Om protocol. Please consult Om lifecycle protocols for correct usage.

https://github.com/swannodette/om/wiki/Documentation

like image 52
myguidingstar Avatar answered Apr 29 '23 07:04

myguidingstar


Per your requirements, you should move the function definition out of the component.

You should then be able to pass the function's name to the event listener:

(defn foo [] (println "foo"))

(defn my-component [text owner]
  (reify
    om/IRender
    (render [_]
        (dom/button
          #js { :onClick foo }
          "Click Here"))))
like image 45
pdoherty926 Avatar answered Apr 29 '23 07:04

pdoherty926