reagent-project / reagent-forms

Bootstrap form components for Reagent
339 stars 78 forks source link

:list field, fake placeholder and empty value #109

Open lambdam opened 8 years ago

lambdam commented 8 years ago

Hello,

I would like to have the possibility to add a fake placeholder to select lists.

I found this : http://stackoverflow.com/questions/5805059/how-do-i-make-a-placeholder-for-a-select-box

So with reagent 0.5.1 and reagent-forms 0.5.22, this was working :

[:div
 [:select {:field :list :id :countries}
  (->> data ;; [{:value "germany" :text "Germany"} {...} ...]
       (map #(do [:option {:key (-> % :value)}
                  (:text %)]))
       doall
       (into [[:option {:key nil
                        :disabled true
                        :hidden true}
               "Select a country"]]) ;; Placeholder text
       seq)]]

But React/Reagent was throwing a warning: Warning: Every element in a seq should have a unique :key : ([:option {:key nil, :disabled true, :hidden true}...

After upgrading to reagent 0.6.0-rc, this doesn't work anymore. The first value of the sequence is displayed by default, no fake placeholder, and the value of the ratom (:countries here) is not selected when provided.

I tried this and removed the warning but it didn't work better as a bound form (with [bind-fields ... ...]):

[:div
 [:select {:field :list :id :countries}
  [:option {:key "placeholder-da39a3ee5e6b4b0d3255bfef95601890afd80709"
            :value nil
            :disabled true
            :hidden true}
   "Select a country"]
  (->> data
       (map #(do [:option {:key (-> % :value)}
                  (:text %)]))
       doall)]]

Is having the "key" attribute used for the React key and for the value a good thing? Here I really want and empty value so that null is returned to the API.

Thanks

yogthos commented 7 years ago

It might be an idea to add a separate attribute that would be preferred to the :key if present. This would preserve the existing behavior, but would allow decoupling the React key from the value.

noesis-software commented 6 years ago

I also had a related problem where I would end up with duplicate keys because the option values were the same. Here's the version I wrote which supports the :value attr on option elements in addition to supporting:key.

(defn- normalize-option-elt
  [[elt {:keys [key value] :as attrs} label]]
  [elt (assoc attrs :value (or value key)) label])

(defmethod reagent-forms.core/init-field :list
  [[type {:keys [field id] :as attrs} & options] {:keys [doc get save!]}]
  (let [options   (map normalize-option-elt (reagent-forms.core/extract-selectors options))
        selection (atom (or (get id)
                            (get-in (first options) [1 :value])))]
    (save! id @selection)
    (render-element attrs doc
      [type
       (merge
         (reagent-forms.core/clean-attrs attrs)
              {:default-value (reagent-forms.core/default-selection options @selection)
               :on-change #(save! id (reagent-forms.core/value-of %))})
       (doall
         (filter
           #(if-let [visible? (:visible? (second %))]
             (visible? @doc) true)
           options))])))
yogthos commented 6 years ago

I'd be open to a pr to allow providing an explicit :value attr for this.