I'm trying to play with Om (yes, I know om.next is coming, but it's not clear when it will be ready--and I think the basic idea will apply), but have run into a simple (or should be) problem.
I have a component with a select widget that populates from the "effects" member of my app-state, and a button to send that effect downstream (controlling LED strips):
(defn effect-view [data owner]
(reify
om/IInitState
(init-state [_]
{:selected-effect nil})
om/IRenderState
(render-state [this state]
(dom/div {}
(dom/select #js {:id "effect"
:value (:selected-effect state)
:onChange #(handle-change % owner state)}
(for [effect (:effects data)]
(dom/option {:react-key (:name effect)} (:name effect))))
(dom/button #js {:id "send-effect"
:onClick (fn [e]
(queue-command "effect" (:selected-effect state)))
:react-key "send-effect"}
(str "Send Effect"))))))
Good so far actually; this mostly works. When the app-state is populated with :effects (via a REST call), this does indeed fill in the options.
The problem is that pesky :selected-effect
state member. It's initially nil, but once the select element is populated, the selector displays the first element. So if the user clicks the "Send Effect" button, rather than getting the first effect it sends "nil"--because the "option" children are being filled in correctly but the view state hasn't changed.
When I change the effect list in the global state, I'd like the value of the selector (:selected-effect
) in the view to change to the first item in the list, if the current value isn't valid (in other words, it starts at nil
and I populate with effects A, B, and C--it should change the selected value to "A"... if I then populate with effects A, C, and D, it should stay A, but if I populated with B, C, D, it should then switch to B, if that makes sense.
How do I do this?