مكتبة ClojureScript لتوفير روابط بيانات النموذج للكاشف ، انظر هنا للحصول على عرض تجريبي مباشر.
تستخدم المكتبة ذرة كاشف كمتجر المستندات. المكونات مرتبطة بالوثيقة باستخدام سمة :field :. سيتم استخدام هذا المفتاح لتحديد كيفية ربط النوع المحدد للمكون. يجب أن يوفر المكون أيضًا سمة فريدة :id يتم استخدامه لربطه بالوثيقة. في حين أن المكتبة موجهة نحو الاستخدام مع Twitter Bootstrap ، إلا أنها غير ملائمة إلى حد ما حول أنواع المكونات التي تنشئها.
يمكن أن يكون :id كلمة رئيسية ، على سبيل المثال: {:id :foo} ، أو مسارًا مفتاحًا للمخطط {:id :foo.bar} الذي سيخطط إلى {:foo {:bar "value"}} . بدلاً من ذلك ، يمكنك تحديد مسار المتجه بشكل صريح [:foo 0 :bar] .
بشكل افتراضي ، فإن قيمة المكون هي قيمة حقل المستند ، ومع ذلك تدعم جميع المكونات AN :in-fn و :out-fn سمات الوظيفة. :in-fn قيمة المستند الحالية ويُرجع ما سيتم عرضه في المكون. :out-fn قيمة المكون ويعيد ما سيتم تخزينه في المستند.
يتم دعم الأنواع التالية من الحقول خارج الصندوق:
يمكن أن يكون حقل الإدخال من النوع :text ، :numeric ، :range ، :password ، :email ، و :textarea . تتصرف المدخلات تمامًا مثل مدخلات HTML العادية وتحديث حالة المستند عندما يتم تشغيل حدث :on-change .
[ :input.form-control { :field :text :id :first-name }]
[ :input.form-control { :field :numeric :id :age }] يمكن أن يكون لحقول الإدخال اختيارية :fmt التي يمكن أن توفر سلسلة تنسيق للقيمة:
[ :input.form-control
{ :field :numeric :fmt " %.2f " :id :bmi :disabled true }]تدعم المدخلات الرقمية سمات مدخلات الرقم HTML 5:
[ :input
{ :field :numeric
:id :volume
:fmt " %.2f "
:step " 0.1 "
:min 0
:max 10 }] يستخدم حقل TypeAhead A :data-source المرتبط بوظيفة تأخذ الإدخال الحالي وإرجاع قائمة نتائج المطابقة. يستخدم عنصر التحكم عنصر إدخال للتعامل مع إدخال المستخدم وجعل قائمة الخيارات كعنصر قائمة غير مرتبة يحتوي على عنصر عنصر أو أكثر. يمكن للمستخدمين تحديد فئات CSS المستخدمة لتقديم كل عنصر من هذه العناصر باستخدام المفاتيح: فئة الإدخال ،: فئة القوائم و: فئة العناصر. يمكن للمستخدمين أيضًا تحديد فئة CSS للتعامل مع تسليط الضوء على التحديد الحالي باستخدام مفتاح: Aightly-Class. يتم تضمين فئات CSS المرجعية في الموارد/ملف/css/congent-forms.css.
( 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 " }]يدعم حقل TypeAhead اختيار كل من الماوس ولوحة المفاتيح.
يمكنك جعل القيمة المعروضة للمدخلات مختلفة عن القيمة المخزنة في المستند. تحتاج إلى تحديد :out-fn ، a :result-fn واختياري :in-fn . يحتاج :data-source إلى إرجاع المتجه [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 }]]] إذا :data-source مع قائمة الخيارات الكاملة عند تمرير الكلمة الرئيسية :all ذلك ، سيعرض مفتاح السهم لأسفل القائمة.
يمكن تحديد سمة :selections لتمرير ذرة تستخدم للاحتفاظ بالتحديدات. يعطي هذا الخيار لجلب القائمة باستخدام TypeAhead Text - إذا قام معالج استجابة Ajax بتعيين الذرة التي ستظهر القائمة.
إذا تم توفيرها ، فإن وظيفة :get-index ستضمن تمييز العنصر المحدد عند انخفاض القائمة.
يتوفر مثال كامل في الكود المصدري لصفحة العرض التوضيحي.
يقوم حقل مربع الاختيار بإنشاء عنصر مربع الاختيار:
[ :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 . عند تعيين مربع الاختيار ، سيتم تحديد مسار المستند الذي يتم توجيهه بواسطة مفتاح :id سيتم تعيينه على true .
[ :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 }]]] يستخدم التحكم في المدى :min و :max Keys لإنشاء إدخال نطاق HTML:
[ :input.form-control
{ :field :range :min 10 :max 100 :id :some-range }] لا تستخدم أزرار الراديو مفتاح :id لأنه يجب أن يكون فريدًا ويتم تجميعه بدلاً من ذلك باستخدام سمة :name . يتم استخدام سمة :value للإشارة إلى القيمة التي يتم حفظها في المستند:
[ :input { :field :radio :value :a :name :radioselection }]
[ :input { :field :radio :value :b :name :radioselection }]
[ :input { :field :radio :value :c :name :radioselection }] يقبل زر الراديو سمة اختيارية :checked . عند تعيين مربع الاختيار ، سيتم تحديد مسار المستند الذي تم توجيهه بواسطة مفتاح :name سيتم تعيينه على true .
[ :input { :field :radio :value :a :name :radioselection }]
[ :input { :field :radio :value :b :name :radioselection :checked true }]
[ :input { :field :radio :value :c :name :radioselection }] يربط حقل الملف كائن File الخاص بـ <input type="file"/> .
[ :input { :field :file :type :file }] نفس الملف ، باستثناء أنه يعمل مع <input type="file" multiple/> ويربط الكائن FileList بأكمله.
[ :input { :field :files :type :file :multiple true }] تحتوي حقول القائمة على عناصر طفل يتم ملؤها في المستند عند اختيارها. يجب أن يكون لكل عناصر الطفل :key تشير إلى القيمة التي سيتم حفظها في المستند. يجب أن تكون قيمة العنصر كلمة رئيسية.
يمكن أن يكون للعناصر اختيارية :visible? الكلمة الرئيسية التي تشير إلى وظيفة مسند. يجب أن تقبل الوظيفة المستند وإرجاع قيمة منطقية تشير إلى ما إذا كان ينبغي عرض الحقل.
يتم استخدام حقل :list لإنشاء عناصر HTML select التي تحتوي على عناصر option :
[ :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])]يتصرف الحقل المفرد مثل القائمة ، لكنه يدعم أنواعًا مختلفة من العناصر ويسمح بإلغاء تحديد الحقول:
[ :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 " ]]يتيح حقل التجديد متعدد القيم تحديد قيم متعددة وتعيينها في المستند:
[ :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 " ]] يمكن أن ترتبط الملصقات بمفتاح في المستند باستخدام سمة :id وستعرض القيمة في هذا المفتاح. يمكن أن تحتوي Lables على اختياري :preamble و :postamble مع النص الذي سيتم تقديمه قبل وبعد القيمة على التوالي. يمكن أيضًا تفسير القيمة باستخدام دالة تنسيق مخصصة لمفتاح :fmt . يمكن استخدام مفتاح :placeholder لتوفير نص سيتم عرضه في حالة عدم وجود قيمة:
[ :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 " )}] ترتبط التنبيهات بمعرف حقل يؤدي إلى تنبيه ويمكن أن يكون له اختياري :event . يجب أن يشير مفتاح الحدث إلى وظيفة تُرجع قيمة منطقية.
اختياري :closeable? true/false يمكن توفير :closeable? true/false للتحكم إذا كان يجب تقديم زر إغلاق (الافتراضيات إلى TRUE).
عندما يتم توفير حدث ما ، يتم تقديم جسم التنبيه عندما يعود الحدث بشكل صحيح:
[ :input { :field :text :id :first-name }]
[ :div.alert.alert-success { :field :alert :id :last-name :event empty?} " first name is empty! " ]عندما لا يتم توفير أي حدث ، يتم عرض التنبيه عندما تكون القيمة في المعرف غير فارغة وتعرض القيمة:
( 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 }]يتم تخزين التاريخ في المستند باستخدام التنسيق التالي:
{ :year 2014 :month 11 :day 24 } يمكن أن يأخذ DatePicker أيضًا اختياريًا :auto-close? مفتاح الإشارة إلى أنه يجب إغلاقه عند النقر فوق اليوم. هذا الافتراضات إلى false .
يمكن تعيين تنسيق التاريخ باستخدام مفتاح :date-format :
{ :field :datepicker :id :date :date-format " yyyy/mm/dd " } يمكن أن يشير :date-format إلى وظيفة تُرجع تاريخ تنسيق:
{ :field :datepicker
:id :date
:date-format ( fn [{ :keys [year month day]}] ( str year " / " month " / " day))} ما ورد أعلاه مفيد بالاقتران مع :save-fn Key الذي يسمح لك بتوفير وظيفة مخصصة لحفظ القيمة. على سبيل المثال ، إذا كنت ترغب في استخدام كائن تاريخ JavaScript ، فيمكنك القيام بما يلي:
[ :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 }]لاحظ أنك تحتاج إلى إرجاع كائن تاريخ جديد في تحديثات للمكون لإعادة إعادة الطلاء.
يأخذ DatePicker اختياريًا :lang الذي يمكنك استخدامه لتعيين لغة DatePicker. يوجد حاليًا الإنجليزية والروسية والألمانية والفرنسية والإسبانية والبرتغالية والفنلندية والهولندية في ترجمات. لاستخدام تمريرة لغة مدمجة في :lang مع كلمة رئيسية كما في الجدول التالي:
| لغة | الكلمة الرئيسية |
|---|---|
| إنجليزي | :en-US (افتراضي) |
| الروسية | :ru-RU |
| الألمانية | :de-DE |
| فرنسي | :fr-FR |
| الأسبانية | :es-ES |
| البرتغالية | :pt-PT |
| الفنلندية | :fi-FI |
| هولندي | :nl-NL |
مثال على استخدام لغة اللغة المدمجة:
{ :field :datepicker :id :date :date-format " yyyy/mm/dd " :inline true :lang :ru-RU } يمكنك أيضًا توفير خريطة تجزئة محلية مخصصة إلى DatePicker. :first-day في اليوم الأول من الأسبوع بدءًا من يوم الأحد على أنه 0. يجب تحديد جميع المفاتيح.
مثال على استخدام خريطة التجزئة المخصصة:
{ :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 }} يتطلب DatePicker CSS إضافية من أجل تقديمها بشكل صحيح. يتم توفير CSS الافتراضي في reagent-forms.css في مسار الموارد. ببساطة تأكد من تضمينه في الصفحة. يمكن قراءة الملف باستخدام:
( -> " reagent-forms.css " clojure.java.io/resource slurp)يمكن استخدام عنصر الحاوية لتجميع عنصر مختلف. يمكن استخدام الحاوية لتعيين رؤية عناصر متعددة.
[ :div.form-group
{ :field :container
:visible? #( :show-name? %)}
[ :input { :field :text :id :first-name }]
[ :input { :field :text :id :last-name }]] يمكن إرفاق دالة المدقق بمكون باستخدام الكلمة الرئيسية :validator . تقبل هذه الوظيفة الوضع الحالي للوثيقة ، وإرجاع مجموعة من الفئات التي سيتم إلحاقها بالعنصر:
[ :input
{ :field :text
:id :person.name.first
:validator ( fn [doc]
( when ( -> doc :person :name :first empty?)
[ " error " ]))}] قد توفر المكونات اختيارية :visible? المفتاح في سماتهم التي تشير إلى وظيفة القرار. من المتوقع أن تأخذ الوظيفة القيمة الحالية للوثيقة وتنتج قيمة الحقيقة التي سيتم استخدامها لتحديد ما إذا كان ينبغي تقديم المكون ، على سبيل المثال:
( def form
[ :div
[ :input { :field :text
:id :foo }]
[ :input { :field :text
:visible? ( fn [doc] ( empty? ( :foo doc)))
:id :bar }]]) يمكن استخدام مفتاح :set-attributes في الحالات التي تحتاج فيها إلى إجراء تحديث تعسفي لسمات المكون. يجب أن يشير المفتاح إلى وظيفة تقبل القيمة الحالية للمستند وخريطة السمات للمكون. يجب أن تُرجع الوظيفة خريطة السمة المحدثة:
[ :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 ))))}]]مثال أعلاه يعطل إدخال الاسم الأخير عندما تكون قيمة إدخال الاسم الأول هي "بوب".
تتصرف مكونات الميدان تمامًا مثل أي مكونات كاشف أخرى ويمكن خلطها معها بحرية. يمكن رؤية مثال كامل النموذج أدناه.
يمكن أن تكون عناصر النماذج مرتبطة ببنية متداخلة باستخدام . كفاصل مسار. على سبيل المثال ، يرتبط المكون التالي [:input {:field :text :id :person.first-name}] بالمسار التالي في ذرة الحالة {: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 }])])ملاحظة مهمة
يتم تقييم القوالب بفارغ الصبر ، ويجب عليك دائمًا استدعاء وظائف المساعد كما في المثال أعلاه بدلاً من وضعها في ناقل. سيتم استبدالها بمكونات كاشف عندما يتم استدعاء bind-fields لتجميع القالب.
بمجرد إنشاء قالب النماذج ، يمكن ربط مستند باستخدام دالة bind-fields :
( 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 " ))يمكن تهيئة النموذج باستخدام وثيقة مأهولة ، وسيتم تهيئة الحقول بالقيم الموجودة هناك:
( 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)]]))) تقبل دالة bind-fields الأحداث الاختيارية. يتم تشغيل الأحداث كلما تم تحديث المستند ، وسيتم تنفيذها بالترتيب المدرجة. يرى كل حدث الوثيقة المعدلة من قبل سابقتها.
يجب أن يأخذ الحدث 3 معلمات ، وهي id path value document . يطابق id :id الحقل ، path هو مسار الحقل في المستند ، ويمثل value القيمة التي تم تغييرها في النموذج ، ويحتوي المستند على حالة النموذج. يمكن للحدث إما إرجاع مستند محدث أو nil ، عندما يتم إرجاع nil ثم تكون حالة المستند غير معدلة.
فيما يلي مثال على حدث لحساب قيمة مفتاح :bmi عندما يتم ملء مفاتيح :weight و :height :
( 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)))))] يمكنك توفير خريطة مخصصة لوظائف الحدث bind-fields لاستخدام أشكال الكاشف مع مكتبة مثل re-frame . في هذه الحالة ، لن تحتفظ أشكال الكاشف بأي حالة داخلية وسيتم استخدام وظائف توفرها للحصول على قيمة الحقل وحفظها وتحديثها. هذا مثال:
( 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])يمكن تعيين رؤية العناصر إما عن طريق توفير المعرف في وثيقة سيتم التعامل معها كقيمة الحقيقة ، أو وظيفة:
( 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 " ]])إذا كنت تستخدم إعادة الإطار ، فمن المستحسن استخدام أحداث إعادة الإطار لإعادة حساب الحقول في النموذج. على سبيل المثال ، دعونا نلقي نظرة على حقل مؤشر كتلة الجسم المحسوب:
( 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]]) سيتم تشغيل rule MultiEmthod عندما يتم استدعاء حدث :set-value ، وسيقوم بحساب مؤشر كتلة الجسم في أي وقت يتم فيه تحديث الطول أو الوزن.
يمكن إضافة الحقول المخصصة عن طريق تنفيذ reagent-forms.core/init-field . يجب أن تأخذ الطريقة معلمتين ، حيث تكون المعلمة الأولى هي مكون الحقل والثاني هو الخيارات.
بشكل افتراضي ، سوف تحتوي الخيارات على get و save! update! مفاتيح. get Key Points to function تقبل المعرف ويعيد قيمة المستند المرتبطة به. save! تقبل الوظيفة معرفًا وقيمة مرتبطة به. update! تقبل الدالة معرف ، وظيفة ستتعامل مع التحديث ، والقيمة. ستتلقى الوظيفة معالجة التحديث القيم القديمة والجديدة.
يمكن توفير المحولات إلى الحقول لإنشاء تنسيقات تخزين مخصصة لقيم الحقل. هذه هي زوج من الوظائف التي تم تمريرها إلى الحقل من خلال المفاتيح :in-fn و :out-fn . :in-fn يعدل العنصر المخزن بحيث يمكن للحقل الاستفادة منه بينما :out-fn إخراج الحقل قبل تخزينه. على سبيل المثال ، من أجل استخدام كائن js/Date الأصلي كتنسيق التخزين ، يمكن تهيئة DatePicker وبالتالي:
[ :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 %)))}]قد يتم تمرير المحولات الخالية لذلك يجب أن تكون قادرة على التعامل معها.
سيكون لـ Safari on iOS تأخير 300 مللي ثانية :on-click الأحداث ، من الممكن تعيين حدث مشغل مخصص باستخدام مفتاح :touch-event . انظر هنا للحصول على قائمة الأحداث المتاحة في React. على سبيل المثال ، إذا أردنا استخدامه :on-touch-start بدلاً من :on-click لإطلاق الحدث ، فيمكننا القيام بما يلي:
[ :input.form-control { :field :text :id :first-name :touch-event :on-touch-start }] لاحظ أنه سيتعين عليك أيضًا تعيين نمط cursor: pointer لأي عناصر أخرى غير الأزرار من أجل أن تعمل الأحداث على iOS.
يعد TapeventPlugin for React خيارًا آخر لإنشاء أحداث مستجيبة ، حتى تصبح الوظيفة متوفرة في React نفسها.
يستخدم هذا المشروع Doo لتشغيل الاختبارات. يجب عليك تثبيت واحدة من البيئات المدعومة من DOO ، راجع المستندات للحصول على التفاصيل. لتشغيل الاختبارات ، على سبيل المثال باستخدام فانتوم ، افعل:
lein doo slimer test
حقوق الطبع والنشر © 2018 Dmitri Sotnikov
تم توزيعه ضمن ترخيص Eclipse Public إما الإصدار 1.0 أو (في خيارك) أي إصدار لاحق.