Eine ClojureScript -Bibliothek zur Bereitstellung von Formdatenbindungen für Reagenzien finden Sie hier für eine Live -Demo.
Die Bibliothek verwendet ein Reagenzatom als Dokumentgeschäft. Die Komponenten sind mit dem :field " an das Dokument gebunden. Dieser Schlüssel wird verwendet, um zu entscheiden, wie der spezifische Komponententyp gebunden werden sollte. Die Komponente muss auch ein eindeutiges :id -Attribut für ein eindeutiges ID bereitstellen, mit dem es mit dem Dokument korreliert. Während die Bibliothek mit Twitter Bootstrap auf die Nutzung ausgerichtet ist, ist sie ziemlich agnostisch in Bezug auf die Arten von Komponenten, die Sie erstellen.
Die :id kann ein Schlüsselwort sein, z. B. {:id :foo} oder ein Schlüsselwort -Pfad {:id :foo.bar} , der {:foo {:bar "value"}} zugeordnet wird. Alternativ können Sie einen Vektorpfad explizit angeben [:foo 0 :bar] .
Standardmäßig ist der Komponentenwert der des Dokumentfelds, alle Komponenten unterstützen jedoch :in-fn und :out-fn Funktionsattribute. :in-fn akzeptiert den aktuellen Dokumentwert und gibt das zurück, was in der Komponente angezeigt werden soll. :out-fn akzeptiert den Komponentenwert und gibt das zurück, was im Dokument gespeichert werden soll.
Die folgenden Arten von Feldern werden aus der Box unterstützt:
Ein Eingabefeld kann vom Typ sein :text ,: :numeric , :range ,: :password ,: :email und :textarea . Die Eingaben verhalten sich genau wie reguläre HTML-Eingaben und aktualisieren den Dokumentstatus, wenn das :on-change -Ereignis ausgelöst wird.
[ :input.form-control { :field :text :id :first-name }]
[ :input.form-control { :field :numeric :id :age }] Die Eingabefelder können ein optionales :fmt -Attribut haben, das einen Format -Zeichenfolge für den Wert bereitstellen kann:
[ :input.form-control
{ :field :numeric :fmt " %.2f " :id :bmi :disabled true }]Numerische Eingänge unterstützen Attribute für die HTML 5 -Zahleneingabe:
[ :input
{ :field :numeric
:id :volume
:fmt " %.2f "
:step " 0.1 "
:min 0
:max 10 }] Das Feld TypeAhehead verwendet A :data-source -Schlüssel, die an eine Funktion gebunden sind, die die aktuelle Eingabe nimmt und eine Liste der übereinstimmenden Ergebnisse zurückgibt. Das Steuerelement verwendet ein Eingabeelement, um die Benutzereingabe zu behandeln, und rendert die Auswahlliste als nicht ordnungsgemäßes Listenelement, das ein oder mehrere Listenelementelemente enthält. Benutzer können die CSS-Klassen angeben, mit denen jedes dieser Elemente mithilfe der Tasten: Eingabeklasse,: Listenklasse und: Element-Klasse verwendet wird. Benutzer können zusätzlich eine CSS-Klasse angeben, um das Hervorhebung der aktuellen Auswahl mit der Taste der Highlight-Klasse zu verarbeiten. Referenz-CSS-Klassen sind in der Datei Ressourcen/öffentlich/CSS/Reagent-Forms.css.CSS enthalten.
( defn friend-source [text]
( filter
#( -> % ( .toLowerCase %) ( .indexOf text) ( > -1 ))
[ " Alice " " Alan " " Bob " " Beth " " Jim " " Jane " " Kim " " Rob " " Zoe " ]))
[ :div { :field :typeahead
:id :ta
:input-placeholder " pick a friend "
:data-source friend-source
:input-class " form-control "
:list-class " typeahead-list "
:item-class " typeahead-item "
:highlight-class " highlighted " }]Das Feld TypeAhehead unterstützt sowohl die Maus- als auch die Tastaturauswahl.
Sie können den angezeigten Wert der Eingabe von dem im Dokument gespeicherten Wert unterscheiden. Sie müssen angeben :out-fn , a :result-fn und optional :in-fn . Die :data-source muss einen Vektor zurücksenden [display-value stored-value] .
( defn people-source [people]
( fn [text]
( ->> people
( filter #( -> ( :name %)
( .toLowerCase )
( .indexOf text)
( > -1 )))
( mapv #( vector ( :name %) ( :num %))))))
[ :div { :field :typeahead
:data-source ( people-source people)
:in-fn ( fn [num]
[( :name ( first ( filter #( = num ( :num %)) people))) num])
:out-fn ( fn [[name num]] num)
:result-fn ( fn [[name num]] name)
:id :author.num }]]] Wenn :data-source mit der vollständigen Optionsliste reagiert, wenn das Schlüsselwort übergeben wird :all , wird die Down-Arm-Taste die Liste angezeigt.
Das Attribut :selections kann angegeben werden, um ein Atom zu übergeben, mit dem die Auswahlen geführt werden. Dies gibt die Option, die Liste mit dem TypeAeAhead -Text abzurufen. Wenn ein AJAX -Antworthandler das Atom legt, wird die Liste angezeigt.
Wenn die Funktion :get-index -Funktion geliefert wird, wird sichergestellt, dass das ausgewählte Element beim Auftauchen der Liste hervorgehoben wird.
Ein vollständiges Beispiel ist im Quellcode für die Demonstrationsseite verfügbar.
Das Kontrollkästchenfeld erstellt ein Kontrollkästchen -Element:
[ :div.row
[ :div.col-md-2 " does data binding make you happy? " ]
[ :div.col-md-5
[ :input.form-control { :field :checkbox :id :happy-bindings }]]] Das Kontrollkästchen akzeptiert ein optionales :checked Attribut. Beim Einstellen wird das Kontrollkästchen ausgewählt und der Dokumentenpfad, auf den die :id -Taste auf true festgelegt wird.
[ :div.row
[ :div.col-md-2 " does data binding make you happy? " ]
[ :div.col-md-5
[ :input.form-control { :field :checkbox :id :happy-bindings :checked true }]]] Range Control verwendet :min und :max Schlüssel, um einen HTML -Bereicheingang zu erstellen:
[ :input.form-control
{ :field :range :min 10 :max 100 :id :some-range }] Optionsschaltflächen verwenden die :id -Taste nicht, da er eindeutig sein muss und stattdessen mit dem Attribut :name gruppiert werden. Das :value -Attribut wird verwendet, um den Wert anzugeben, der im Dokument gespeichert wird:
[ :input { :field :radio :value :a :name :radioselection }]
[ :input { :field :radio :value :b :name :radioselection }]
[ :input { :field :radio :value :c :name :radioselection }] Das Optionsfeld akzeptiert ein optionales :checked Attribut. Beim Einstellen wird das Kontrollkästchen ausgewählt und der Dokumentpfad mit der Taste der :name auf true festgelegt.
[ :input { :field :radio :value :a :name :radioselection }]
[ :input { :field :radio :value :b :name :radioselection :checked true }]
[ :input { :field :radio :value :c :name :radioselection }] Das Feld "Datei" bindet das File eines <input type="file"/> .
[ :input { :field :file :type :file }] Gleich wie die Datei, außer dass sie mit <input type="file" multiple/> funktioniert und das gesamte FileList Objekt bindet.
[ :input { :field :files :type :file :multiple true }] Listenfelder enthalten untergeordnete Elemente, deren Werte bei Auswahl im Dokument ausgefüllt sind. Die untergeordneten Elemente müssen jeweils ein :key Schlüsselattribut haben, das auf den Wert hinweist, der im Dokument gespeichert wird. Der Wert des Elements muss ein Schlüsselwort sein.
Die Elemente können optional sein :visible? Schlüsselwort, das auf eine Prädikatfunktion hinweist. Die Funktion sollte das Dokument akzeptieren und einen Booleschen Wert zurückgeben, der darauf hinweist, ob das Feld angezeigt werden soll.
Das Feld :list wird zum Erstellen von HTML select verwendet, die option Child Elements enthalten:
[ :select.form-control { :field :list :id :many-options }
[ :option { :key :foo } " foo " ]
[ :option { :key :bar } " bar " ]
[ :option { :key :baz } " baz " ]]
( def months
[ " January " " February " " March " " April " " May " " June "
" July " " August " " September " " October " " November " " December " ])
[ :select { :field :list :id :dob.day }
( for [i ( range 1 32 )]
[ :option
{ :key ( keyword ( str i))
:visible? #( let [month ( get-in % [ :dob :month ])]
( cond
( < i 29 ) true
( < i 31 ) ( not= month :February )
( = i 31 ) ( some #{month} [ :January :March :May :July :August :October :December ])
:else false ))}
i])]
[ :select { :field :list :id :dob.month }
( for [month months]
[ :option { :key ( keyword month)} month])]
[ :select { :field :list :id :dob.year }
( for [i ( range 1950 ( inc ( .getFullYear ( js/Date. ))))]
[ :option { :key ( keyword ( str i))} i])]Das Feld Einzelauswahl verhält sich wie die Liste, unterstützt jedoch verschiedene Arten von Elementen und ermöglicht es, die Felder zu deaktivieren:
[ :h3 " single-select buttons " ]
[ :div.btn-group { :field :single-select :id :unique-position }
[ :button.btn.btn-default { :key :left } " Left " ]
[ :button.btn.btn-default { :key :middle } " Middle " ]
[ :button.btn.btn-default { :key :right } " Right " ]]
[ :h3 " single-select list " ]
[ :ul.list-group { :field :single-select :id :pick-one }
[ :li.list-group-item { :key :foo } " foo " ]
[ :li.list-group-item { :key :bar } " bar " ]
[ :li.list-group-item { :key :baz } " baz " ]]Mit dem Feld Multi-Selekten können mehrere Werte ausgewählt und im Dokument festgelegt werden:
[ :h3 " multi-select list " ]
[ :div.btn-group { :field :multi-select :id :position }
[ :button.btn.btn-default { :key :left } " Left " ]
[ :button.btn.btn-default { :key :middle } " Middle " ]
[ :button.btn.btn-default { :key :right } " Right " ]] Etiketten können einem Schlüssel im Dokument mit dem Attribut :id zugeordnet werden und den Wert an diesem Schlüssel anzeigen. Die Labels können optional sein :preamble und :postamble -Tasten mit dem Text, der vor und nach dem Wert gerendert wird. Der Wert kann auch unter Verwendung einer Formatierfunktion interpretiert werden, die dem :fmt -Schlüssel zugewiesen ist. Der :placeholder kann verwendet werden, um Text bereitzustellen, die ohne Wert angezeigt werden:
[ :label { :field :label :id :volume }]
[ :label { :field :label :preamble " the value is: " :id :volume }]
[ :label { :field :label :preamble " the value is: " :postamble " ml " :id :volume }]
[ :label { :field :label :preamble " the value is: " :postamble " ml " :placeholder " N/A " :id :volume }]
[ :label { :field :label :preamble " the value is: " :id :volume :fmt ( fn [v] ( if v ( str v " ml " ) " unknown " )}] Warnungen sind an eine ID eines Feldes gebunden, das den Alarm auslöst und einen optionalen :event haben kann. Der Ereignisschlüssel sollte auf eine Funktion verweisen, die einen booleschen Wert zurückgibt.
Ein optional :closeable? true/false kann zur Steuerung bereitgestellt werden, wenn eine Schlusstaste gerendert werden sollte (Standardeinstellungen an True).
Wenn ein Ereignis geliefert wird, wird der Körper des Alarms wiedergegeben, wenn das Ereignis wahr zurückgibt:
[ :input { :field :text :id :first-name }]
[ :div.alert.alert-success { :field :alert :id :last-name :event empty?} " first name is empty! " ]Wenn kein Ereignis geliefert wird, wird der Alarm angezeigt, wenn der Wert an der ID nicht leer ist und den Wert anzeigt:
( def doc ( atom {}))
; ;define an alert that watches the `:errors.first-name` key for errors
[ :div.alert.alert-danger { :field :alert :id :errors.first-name }]
; ;trigger the alert by setting the error key
[ :button.btn.btn-default
{ :on-click
#( if ( empty? ( :first-name @doc))
( swap! doc assoc-in [ :errors :first-name ] " first name is empty! " ))}
" save " ][ :div { :field :datepicker :id :birthday :date-format " yyyy/mm/dd " :inline true }]Das Datum wird im Dokument mit dem folgenden Format gespeichert:
{ :year 2014 :month 11 :day 24 } Der DatePicker kann auch eine optionale :auto-close? Schlüssel, um anzuzeigen, dass es geschlossen werden sollte, wenn der Tag angeklickt wird. Dies ist standardmäßig false .
Das Datumsformat kann mit dem Taste :date-format festgelegt werden:
{ :field :datepicker :id :date :date-format " yyyy/mm/dd " } Das :date-format kann auch auf eine Funktion verweisen, die das formatierte Datum zurückgibt:
{ :field :datepicker
:id :date
:date-format ( fn [{ :keys [year month day]}] ( str year " / " month " / " day))} Das obige ist in Verbindung mit der Taste :save-fn Taste nützlich, mit der Sie eine benutzerdefinierte Funktion zum Speichern des Werts angeben können. Wenn Sie beispielsweise ein JavaScript -Datumsobjekt verwenden möchten, können Sie Folgendes tun:
[ :div.input-group.date.datepicker.clickable
{ :field :datepicker
:id :reminder
:date-format ( fn [date]
( str ( .getDate date) " / "
( inc ( .getMonth date)) " / "
( .getFullYear date)))
:save-fn ( fn [current-date { :keys [year month day]}]
( if current-date
( doto ( js/Date. )
( .setFullYear year)
( .setMonth ( dec month))
( .setDate day)
( .setHours ( .getHours current-date))
( .setMinutes ( .getMinutes current-date)))
( js/Date. year ( dec month) day)))
:auto-close? true }]Beachten Sie, dass Sie ein neues Datumsobjekt in Aktualisierungen für die Komponente zurückgeben müssen.
DatePicker nimmt eine optionale :lang Taste, mit der Sie das Gebietsschema des DatePickers festlegen können. Derzeit gibt es Englisch, Russisch, Deutsch, Französisch, Spanisch, Portugiesisch, Finnisch und niederländische Übersetzungen. Um einen eingebauten Sprachpass zu verwenden :lang mit einem Schlüsselwort wie in der folgenden Tabelle:
| Sprache | Stichwort |
|---|---|
| Englisch | :en-US (Standard) |
| Russisch | :ru-RU |
| Deutsch | :de-DE |
| Französisch | :fr-FR |
| Spanisch | :es-ES |
| Portugiesisch | :pt-PT |
| finnisch | :fi-FI |
| Niederländisch | :nl-NL |
Beispiel für die Verwendung eines integrierten Sprachgebiets:
{ :field :datepicker :id :date :date-format " yyyy/mm/dd " :inline true :lang :ru-RU } Sie können dem DatePicker auch eine benutzerdefinierte Hash-Map für den Gebietsschema zur Verfügung stellen. :first-day markiert am ersten Tag der Woche ab Sonntag als 0. Alle Schlüssel müssen angegeben werden.
Beispiel für die Verwendung eines benutzerdefinierten Gebietsschemas-Hash-Map:
{ :field :datepicker :id :date :date-format " yyyy/mm/dd " :inline true :lang
{ :days [ " First " " Second " " Third " " Fourth " " Fifth " " Sixth " " Seventh " ]
:days-short [ " 1st " " 2nd " " 3rd " " 4th " " 5th " " 6th " " 7th " ]
:months [ " Month-one " " Month-two " " Month-three " " Month-four " " Month-five " " Month-six "
" Month-seven " " Month-eight " " Month-nine " " Month-ten " " Month-eleven " " Month-twelve " ]
:months-short [ " M1 " " M2 " " M3 " " M4 " " M5 " " M6 " " M7 " " M8 " " M9 " " M10 " " M11 " " M12 " ]
:first-day 0 }} Der DatePicker benötigt zusätzliche CSS, um korrekt gerendert zu werden. Das Standard-CSS ist in reagent-forms.css im Ressourcenpfad bereitgestellt. Stellen Sie einfach sicher, dass es auf der Seite enthalten ist. Die Datei kann mit:
( -> " reagent-forms.css " clojure.java.io/resource slurp)Das Containerelement kann verwendet werden, um verschiedene Elemente zu gruppieren. Der Container kann verwendet werden, um die Sichtbarkeit mehrerer Elemente festzulegen.
[ :div.form-group
{ :field :container
:visible? #( :show-name? %)}
[ :input { :field :text :id :first-name }]
[ :input { :field :text :id :last-name }]] Eine Validator -Funktion kann mit dem Schlüsselwort :validator an eine Komponente angehängt werden. Diese Funktion akzeptiert den aktuellen Status des Dokuments und gibt eine Sammlung von Klassen zurück, die an das Element angehängt werden:
[ :input
{ :field :text
:id :person.name.first
:validator ( fn [doc]
( when ( -> doc :person :name :first empty?)
[ " error " ]))}] Die Komponenten können optional liefern :visible? Schlüssel in ihren Attributen, die auf eine Entscheidungsfunktion hinweisen. Es wird erwartet, dass die Funktion den aktuellen Wert des Dokuments übernimmt und einen wahren Wert erzeugt, der verwendet wird, um zu entscheiden, ob die Komponente gerendert werden soll, z. B.:
( def form
[ :div
[ :input { :field :text
:id :foo }]
[ :input { :field :text
:visible? ( fn [doc] ( empty? ( :foo doc)))
:id :bar }]]) Der :set-attributes -Schlüssel kann in Fällen verwendet werden, in denen Sie eine willkürliche Aktualisierung der Attribute der Komponente durchführen müssen. Der Schlüssel muss auf eine Funktion verweisen, die den aktuellen Wert des Dokuments und die Karte der Attribute für die Komponente akzeptiert. Die Funktion muss eine aktualisierte Attributkarte zurückgeben:
[ :div
[ :input { :field :text
:id :person.name.first
:validator ( fn [doc]
( when ( = " Bob " ( -> doc :person :name :first ))
[ " error " ]))}]
[ :input { :field :text
:id :person.name.last
:set-attributes ( fn [doc attrs]
( assoc attrs :disabled ( = " Bob " ( -> doc :person :name :first ))))}]]Das obige Beispiel deaktiviert die Nachname -Eingabe, wenn der Wert der Vornameneingabe "Bob" ist.
Die Feldkomponenten verhalten sich genauso wie alle anderen Reagenzienkomponenten und können frei mit ihnen gemischt werden. Ein vollständiges Formularbeispiel ist unten zu sehen.
Formelemente können an eine verschachtelte Struktur gebunden werden, indem die . als Pfadabscheider. Beispielsweise bindet die folgende Komponente [:input {:field :text :id :person.first-name}] an den folgenden Pfad im Statusatom {:person {:first-name <field-value>}}
( defn row [label input]
[ :div.row
[ :div.col-md-2 [ :label label]]
[ :div.col-md-5 input]])
( def form-template
[ :div
( row " first name " [ :input { :field :text :id :first-name }])
( row " last name " [ :input { :field :text :id :last-name }])
( row " age " [ :input { :field :numeric :id :age }])
( row " email " [ :input { :field :email :id :email }])
( row " comments " [ :textarea { :field :textarea :id :comments }])])wichtiger Hinweis
Die Vorlagen werden eifrig bewertet, und Sie sollten die Helferfunktionen immer wie im obigen Beispiel anrufen, anstatt sie in einen Vektor zu legen. Diese werden durch Reagenzkomponenten ersetzt, wenn die bind-fields aufgerufen werden, um die Vorlage zu kompilieren.
Sobald eine Formularvorlage erstellt wurde, kann sie unter Verwendung der bind-fields -Funktion an ein Dokument gebunden werden:
( ns myform.core
( :require [reagent-forms.core :refer [bind-fields]]
[reagent.core :as r]))
( defn form []
( let [doc ( r/atom {})]
( fn []
[ :div
[ :div.page-header [ :h1 " Reagent Form " ]]
[bind-fields form-template doc]
[ :label ( str @doc)]])))
( reagent/render-component [form] ( .getElementById js/document " container " ))Das Formular kann mit einem besiedelten Dokument initialisiert werden, und die Felder werden mit den dort gefundenen Werten initialisiert:
( def form-template
[ :div
( row " first name "
[ :input.form-control { :field :text :id :first-name }])
( row " last name "
[ :input.form-control { :field :text :id :last-name }])
( row " age "
[ :input.form-control { :field :numeric :id :age }])
( row " email "
[ :input.form-control { :field :email :id :email }])
( row " comments "
[ :textarea.form-control { :field :textarea :id :comments }])])
( defn form []
( let [doc ( atom { :first-name " John " :last-name " Doe " :age 35 })]
( fn []
[ :div
[ :div.page-header [ :h1 " Reagent Form " ]]
[bind-fields form-template doc]
[ :label ( str @doc)]]))) Die bind-fields -Funktion akzeptiert optionale Ereignisse. Ereignisse werden immer dann ausgelöst, wenn das Dokument aktualisiert wird, und werden in der Reihenfolge ausgeführt, in der sie aufgeführt sind. In jedem Ereignis wird das von seinem Vorgänger modifizierte Dokument verändert.
Das Ereignis muss 3 Parameter annehmen, nämlich die id , der path , der value und das document . Die id entspricht der :id des Feldes, der path ist der Pfad des Feldes im Dokument, der value repräsentiert den Wert, der im Formular geändert wurde, und das Dokument enthält den Status des Formulars. Das Ereignis kann entweder ein aktualisiertes Dokument oder nil zurückgeben, wenn nil zurückgegeben wird, ist der Status des Dokuments nicht modifiziert.
Das Folgende ist ein Beispiel für ein Ereignis zur Berechnung des Werts der :bmi -Schlüssel, wenn :weight und :height besiedelt sind:
( defn row [label input]
[ :div.row
[ :div.col-md-2 [ :label label]]
[ :div.col-md-5 input]])
( def form-template
[ :div
[ :h3 " BMI Calculator " ]
( row " Height " [ :input { :field :numeric :id :height }])
( row " Weight " [ :input { :field :numeric :id :weight }])
( row " BMI " [ :input { :field :numeric :id :bmi :disabled true }])])
[bind-fields
form-template
doc
( fn [id path value { :keys [weight height] :as doc}]
( when ( and ( some #{id} [ :height :weight ]) weight height)
( assoc-in doc [ :bmi ] ( / weight ( * height height)))))] Sie können bind-fields eine benutzerdefinierte Karte von Ereignisfunktionen zur Verfügung stellen, um Reagenzien mit einer Bibliothek wie re-frame zu verwenden. In diesem Fall werden Reagenz-Formen keinen internen Zustand und Funktionen, die von Ihnen bereitgestellt werden, verwendet, um den Wert des Feldes zu erhalten, zu speichern und zu aktualisieren. Hier ist ein Beispiel:
( ns foo.bar
( :require [re-frame.core :as re-frame]
[reagent-forms.core :refer [bind-fields]]))
; re-frame events
( 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)))
( re-frame/reg-event-db
:set-value
( fn [db [_ path value]]
( assoc-in db ( into [ :doc ] path) value)))
( re-frame/reg-event-db
:update-value
( fn [db [_ f path value]]
( update-in db ( into [ :doc ] path) f value)))
; Functions that will be called by each individual form field with an id and a value
( def events
{ :get ( fn [path] @( re-frame/subscribe [ :value path]))
:save! ( fn [path value] ( re-frame/dispatch [ :set-value path value]))
:update! ( fn [path save-fn value]
; save-fn should accept two arguments: old-value, new-value
( re-frame/dispatch [ :update-value save-fn path value]))
:doc ( fn [] @( re-frame/subscribe [ :doc ]))})
; bind-fields called with a form and a map of custom events
( defn foo
[]
[bind-fields
[ :div
[ :input { :field :text
:id :person.name.first
:valid? ( fn [doc]
( when ( = " Bob " ( -> doc :person :name :first ))
[ " error " ]))}]
[ :input { :field :text
:id :person.name.last }]]
events])Die Sichtbarkeit der Elemente kann festgelegt werden, indem entweder die ID in einem Dokument bereitgestellt wird, das als wahrheitsgemäßer Wert oder als Funktion behandelt wird:
( re-frame/reg-event-db
:toggle-foo
( fn [db _]
( update-in db [ :doc :foo ] not)))
( re-frame/reg-sub
:bar-visible?
( fn [db _]
( :bar db)))
( re-frame/reg-event-db
:toggle-bar
( fn [db _]
( update db :bar not)))
( def form
[ :div
[ :input { :field :text
:id :foo-input
:visible? :foo }]
[ :input { :field :text
:id :bar-input
:visible? ( fn [doc] @( re-frame/subscribe [ :bar-visible? ]))}]
( defn page
[]
[ :div
[bind-fields
[ :input { :field :text
:id :foo-input
:visible? :foo-input-visible? }]
event-fns]
[ :button
{ :on-click #( re-frame/dispatch [ :toggle-foo ])}
" toggle foo " ]
[ :button
{ :on-click #( re-frame/dispatch [ :toggle-bar ])}
" toggle bar " ]])Wenn Sie Re-Frame verwenden, wird empfohlen, dass Sie Re-Frame-Ereignisse verwenden, um die Neuberechnung von Feldern im Formular auszulösen. Schauen wir uns beispielsweise ein kalkuliertes BMI -Feld an:
( re-frame/reg-sub
:value
:<- [ :doc ]
( fn [doc [_ path]]
( get-in doc path)))
( defn bmi [{ :keys [weight height] :as doc}]
( assoc doc :bmi ( / weight ( * height height))))
( defmulti rule ( fn [_ path _] path))
( 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 " BMI example " ]
[bind-fields form-template events]]) Die rule MultiemThod wird ausgelöst, wenn das Ereignis :set-value aufgerufen wird und BMI jederzeit berechnet wird, wenn die Größe oder das Gewicht aktualisiert wird.
Benutzerdefinierte Felder können durch die Implementierung der reagent-forms.core/init-field -Multimethod hinzugefügt werden. Die Methode muss zwei Parameter annehmen, wobei der erste Parameter die Feldkomponente und die zweite Optionen ist.
Standardmäßig enthalten die Optionen das get und das save! und update! Schlüssel. Die get -Schlüsse zeigt auf eine Funktion, die eine ID akzeptiert und den damit verbundenen Dokumentwert zurückgibt. Das save! Funktion akzeptiert eine ID und einen Wert, der damit verbunden ist. Das update! Die Funktion akzeptiert eine ID, eine Funktion, die das Update und den Wert verwaltet. Die Funktionsbearbeitung des Updates empfängt die alten und die neuen Werte.
Adapter können Feldern zur Verfügung gestellt werden, um benutzerdefinierte Speicherformate für Feldwerte zu erstellen. Dies sind ein Paar von Funktionen, die durch die Schlüssel an das Feld übergeben wurden :in-fn und :out-fn . :in-fn ändert das gespeicherte Element so, dass das Feld es nutzen kann, während :out-fn die Ausgabe des Feldes vor dem Speichern verändert. Um beispielsweise ein nationales js/Date -Objekt als Speicherformat zu verwenden, kann der Datumspicker so initialisiert werden:
[ :div { :field :datepicker :id :birthday :date-format " yyyy/mm/dd " :inline true
:in-fn #( when % { :year ( .getFullYear %) :month ( .getMonth %) :day ( .getDate %)})
:out-fn #( when % ( js/Date ( :year %) ( :month %) ( :day %)))}]Adapter können Nulls übergeben werden, damit sie in der Lage sein müssen, diese zu handhaben.
Safari auf iOS hat eine Verzögerung von 300 ms für :on-click -Ereignisse. Es ist möglich, ein benutzerdefiniertes Triggerereignis mit dem Taste :touch-event Taste festzulegen. Hier finden Sie die Liste der in React verfügbaren Ereignisse. Wenn wir beispielsweise verwenden wollten :on-touch-start statt :on-click um das Ereignis auszulösen, könnten wir Folgendes tun:
[ :input.form-control { :field :text :id :first-name :touch-event :on-touch-start }] Beachten Sie, dass Sie auch den Cursorstil festlegen müssen cursor: pointer für andere Elemente als Schaltflächen, damit Ereignisse auf iOS arbeiten können.
Das TapeVentPlugin für React ist eine weitere Option zum Erstellen reaktionsschneller Ereignisse, bis die Funktionalität in React selbst verfügbar ist.
In diesem Projekt werden Doo zum Ausführen der Tests verwendet. Sie müssen eine der von DOO unterstützten Umgebungen installieren und finden Sie in den DOCs. Um die Tests beispielsweise mit Phantom auszuführen, tun Sie:
lein doo slimer test
Copyright © 2018 Dmitri Sotnikov
Verteilt unter der Eclipse Public Lizenz entweder Version 1.0 oder (nach Ihrer Option) jede spätere Version.