Reputation: 1536
I need to create a form containing a select field where the options depend on the currently available mail accounts. I retrieve these mail accounts in a API request after the page has loaded.
The following function creates the form with the select element.
(defn create-options [mail-accounts]
(for [m mail-accounts]
[:option {:key (keyword (str (:id m)))
:value (:id m)}
(:name m)]))
(defn render-mail-account-select [mail-accounts]
(let [form-state (r/atom {})]
(fn [mail-accounts]
(let [form [:form.mailing-form.form-horizontal
(into [:select.form-control {:field :list :id :mail-account}]
(create-options mail-accounts))]]
(pprint form)
[bind-fields form form-state]))))
The pprint
gives me the following output:
;; Before the mail-accounts are loaded
[:select.form-control {:field :list, :id :mail-account}]
;; After the state update containing the mail accounts
[:select.form-control
{:field :list, :id :mail-account}
[:option {:key :24, :value 24} "First mail account name"]
[:option {:key :25, :value 25} "Second mail account name"]]
My problem is that the select on my page stays empty as if the select does not get re-rendered.
Addendum
I think I forgot to add some code:
I am wrapping this form in a KIOO component where I dereference my state
atom.
(defsnippet choose-account-panel "html/static-panel.html" [:div.panel]
[]
{[:h4.panel-title] (content "3. Mail Account wählen")
[:div.panel-body] (content [render-mail-account-select (:mail-accounts @state)])})
This component then calls my render-mail-account-select
function and should re-render the form properly.
Upvotes: 1
Views: 1799
Reputation: 4235
As already pointed out, your render function must reference something in your state atom in order to know when it needs to be re-rendered. However, if you also want your displayed form to have a 'default' value selected, you also need to set your state atom.
The bind-fields call will associate your form with the state atom. If there is nothing in the state atom, the form will be 'blank' i.e. none of the elements selected. If this is what you want, then that should be fine. However, if you want a value selected, then you should put that value in the atom. For example, if you wanted the first item to be already selected, then put :24 into the state atom i.e.
(swap! form-state assoc :mail-account :24)
Note that I'm not sure your option definition is correct. The docs seem to indicate that you only need the :key attribute with the value (as a keyword) in that attribute. I've not looked at the source to confirm this, but none of the examples use a :value attribute as well.
If you do the swap at the beginning of your render function, then the state atom will have been referenced and your component should re-render each time it is called with new arguments. When I say 'render function', in this case, I mean the anonymous function, not the outer one which creates the state atom.
Upvotes: 0
Reputation: 14559
You need to dereference a Ratom for Reagent to know which functions need to be called again when that Ratom changes. That dereferencing needs to happen within the anonymous function inside render-mail-account-select
, or one of the functions that it calls. I can’t see any dereferencing happening here?
See the docs here for more details on Reagent’s rendering.
Upvotes: 5