1

I have code that updates the DOM. new-recipe! calls an API to get a new recipe string. update-recipe-state next updates this state in the screen. Finally we have a call to update-transition-buttons.

(defn- add-listener-to-recipe-button! []
  "Listens to go button, creates a new recipe and displays it"
  (create-click-event-listener! (dommy/sel1 :#button-start)
                                #(go (new-recipe!)
                                     (<! (timeout 2000))
                                     (update-recipe-state!)
                                     (<! (timeout 2000))
                                     (update-transition-buttons! "onboarding"))))

;; define your app data so that it doesn't get over-written on reload
(defonce world
  (add-listener-to-recipe-button!))

The update-transition-buttons has some delays between the steps (using the timeout code here) looks as follows:

(defn- update-transition-buttons! [recipe-name]
  "Updates the buttons with the transition names"
  (go
    ;; Split response in list of actions by splitting on the comma
    (let [response (<! (http/get (get-recipe-transitions-url recipe-name)))
          transition-names (clojure.string/split (:body response) ",")]
      (go (update-buttons! transition-names)
          (<! (timeout 2000))
          (js/console.log transition-names)
          (set-button-event-handlers! transition-names)))))

So it splits the response to a string. updates-buttons changes state on the page by adding some buttons (this is visible). Again there is a timeout, and then I want to add the event handlers to the buttons. This is where it goes wrong.

The routine to create event listeners (which also contain a console.log) look as follows:

(defn- listen-to-transition-button! [name]
  "Creates click event listener on button (button HTML ID should be name)"
  (do (js/console.log (str "Listening to " name))
    (let [name-without-spaces (clojure.string/replace name " " "")
          button (dommy/sel1 (keyword (str "#" name-without-spaces)))
          action #(do (perform-action! name)
                      (update-recipe-state!))]
      (create-click-event-listener! button action))))

(defn- set-button-event-handlers! [names]
  "Creates click event listeners on the buttons (button ID should be name)"
  (map listen-to-transition-button! names))

Again you see a console.log message that should happen for each elements that is passed. The output I get in the Firefox console is:

[FIRST SERVICE CALLED]
[NEXT SERVICE CALLED]
[DISPLAY LIST OF STEPS]: ["Step1", "Step2", "Step3"]

What I expect is:

[FIRST SERVICE CALLED]
[NEXT SERVICE CALLED]
[DISPLAY LIST OF STEPS]: ["Step1", "Step2", "Step3"]
Listening to Step1
Listening to Step2
Listening to Step3

So the event handlers (which depend on the HTML generated in the step before) are not added for some reason and the console.log message is not displayed.

When I call the same code from the REPL I do see the output, i.e.:

repl=> (set-button-event-handlers! ["Step1","Step2", "Step3"])
(#object[Object [object Object]] #object[Object [object Object]] #object[Object [object Object]])

And the console output is:

Listening to Step1
Listening to Step2
Listening to Step3

Why can set-button-event-handlers! be called from the REPL, but not in the update-transition-buttons method after update-buttons?

4

1 回答 1

6

看起来问题出在这里:

(map listen-to-transition-button! names)

set-button-event-handlers!

它创建了一个惰性序列,直到在代码中的某个地方使用元素才会实现元素(这永远不会发生),但是当你在 repl 中调用它时,它完全实现了在输出中显示所有元素。尝试将此行更改为:

(doall (map listen-to-transition-button! names))
于 2016-04-19T11:53:45.127 回答