Open njj opened 5 years ago
You can handle computed values via re-frame events, e.g:
(ns example.core
(:require
[reagent.core :as r]
[re-frame.core :as re-frame]
[reagent-forms.core :refer [bind-fields]]))
(re-frame/reg-event-db
:init
(fn [_ _]
{:doc {}}))
(re-frame/reg-sub
:doc
(fn [db _]
(:doc db)))
(re-frame/reg-sub
:value
:<- [:doc]
(fn [doc [_ path]]
(get-in doc path)))
(defmulti rule (fn [_ path _] path))
(defn bmi [{:keys [weight height] :as doc}]
(assoc doc :bmi (/ weight (* height height))))
(defmethod rule [:height] [doc path value]
(bmi doc))
(defmethod rule [:weight] [doc path value]
(bmi doc))
(defmethod rule :default [doc path value]
doc)
(re-frame/reg-event-db
:set-value
(fn [{:keys [doc] :as db} [_ path value]]
(-> db
(assoc-in (into [:doc] path) value)
(update :doc rule path value))))
(def events
{:get (fn [path] @(re-frame/subscribe [:value path]))
:save! (fn [path value] (re-frame/dispatch [:set-value path value]))
:doc (fn [] @(re-frame/subscribe [:doc]))})
(defn row [label input]
[:div
[:div [:label label]]
[:div input]])
(def form-template
[:div
[:h3 "BMI Calculator"]
(row "Height" [:input {:field :numeric :id :height}])
(row "Weight" [:input {:field :numeric :id :weight}])
(row "BMI" [:label {:field :label :id :bmi}])])
(defn home-page []
[:div [:h2 "Welcome to Reagent"]
[bind-fields form-template events]])
@yogthos Thanks this is super helpful! It might benefit others to have your re-frame documentation in the readme reflect this.
The only issue I have w/ this implementation is it assumes the only computed value is the BMI, and I may have several forms with varying computed values. Is there a recommended way to refactor and abstract this such that you can pass in a variable rule
? I tried doing so on my own but ran into a ton of issues. Something a long the lines of:
(re-frame/reg-event-db
:set-value
(fn [{:keys [doc] :as db} [_ path value rule]]
(-> db
(assoc-in (into [:doc] path) value)
(when rule
(update :doc rule path value)))))
Note that the is actually a multimethod that's keyed on the path with the default behavior of doing nothing. So, the pattern I would suggest would be to create rule defmethods for the paths that should trigger business rules to recalculate values.
With the re-framing example, everything is handled through the
events
function:This works fine for initializing and updating values but what if we wanted to calculate the value of a non-editable field (similar to the BMI example). I have a field that comes from the backend that is non-editable and I want to show the calculation of that field to the user before sending it back over. Is there a way to update a specific field in the
doc
outside of the theevents
function setup?