ไลบรารี Clojurescript เพื่อให้การเชื่อมโยงข้อมูลแบบฟอร์มสำหรับรีเอเจนต์ดูที่นี่สำหรับการสาธิตสด
ห้องสมุดใช้อะตอมรีเอเจนต์เป็นที่เก็บเอกสาร ส่วนประกอบถูกผูกไว้กับเอกสารโดยใช้แอตทริบิวต์ :field คีย์นี้จะถูกใช้เพื่อตัดสินใจว่าควรมีส่วนประกอบประเภทเฉพาะอย่างไร ส่วนประกอบจะต้องให้แอตทริบิวต์ :id ไม่ซ้ำกันที่ใช้เพื่อเชื่อมโยงกับเอกสาร ในขณะที่ห้องสมุดมุ่งเน้นไปที่การใช้งานด้วย twitter bootstrap มันค่อนข้างไม่เชื่อเรื่องพระเจ้าเกี่ยวกับประเภทของส่วนประกอบที่คุณสร้าง
:id สามารถเป็นคำหลักเช่น: {:id :foo} หรือเส้นทางคำสำคัญ {:id :foo.bar} ที่จะแมปกับ {:foo {:bar "value"}} หรือคุณสามารถระบุเส้นทางเวกเตอร์อย่างชัดเจน [:foo 0 :bar]
โดยค่าเริ่มต้นค่าส่วนประกอบคือของฟิลด์เอกสารอย่างไรก็ตามส่วนประกอบทั้งหมดรองรับแอตทริบิวต์ฟังก์ชั่น :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 ใช้คีย์ :data-source ที่ถูกผูกไว้กับฟังก์ชั่นที่ใช้อินพุตปัจจุบันและส่งคืนรายการผลลัพธ์การจับคู่ ตัวควบคุมใช้องค์ประกอบอินพุตเพื่อจัดการอินพุตของผู้ใช้และแสดงรายการตัวเลือกเป็นองค์ประกอบรายการที่ไม่ได้จัดเรียงที่มีองค์ประกอบรายการหนึ่งรายการขึ้นไป ผู้ใช้อาจระบุคลาส CSS ที่ใช้ในการแสดงผลแต่ละองค์ประกอบเหล่านี้โดยใช้คีย์: อินพุตคลาส: รายการคลาสและ: รายการรายการ ผู้ใช้อาจระบุคลาส CSS เพื่อจัดการกับการเน้นการเลือกปัจจุบันด้วย: ไฮไลท์คลาสคลาส คลาสอ้างอิง CSS รวมอยู่ในไฟล์ทรัพยากร/สาธารณะ/css/reagent-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 - หากตัวจัดการการตอบกลับ 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 }]]] Range Control ใช้ :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 " ]]ฟิลด์ Multi-select อนุญาตให้เลือกหลายค่าและตั้งค่าในเอกสาร:
[ :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 " )}] การแจ้งเตือนจะถูกผูกไว้กับ ID ของฟิลด์ที่ก่อให้เกิดการแจ้งเตือนและสามารถมีรหัสเพิ่มเติม :event คีย์เหตุการณ์ควรชี้ไปที่ฟังก์ชั่นที่ส่งคืนค่าบูลีน
ตัวเลือก :closeable? true/false สามารถให้ :closeable? true/false เพื่อควบคุมหากปุ่มปิดควรแสดงผล (ค่าเริ่มต้นเป็นจริง)
เมื่อมีการจัดหาเหตุการณ์แล้วร่างกายของการแจ้งเตือนจะถูกแสดงผลเมื่อใดก็ตามที่เหตุการณ์ส่งคืนจริง:
[ :input { :field :text :id :first-name }]
[ :div.alert.alert-success { :field :alert :id :last-name :event empty?} " first name is empty! " ]เมื่อไม่มีการจัดทำเหตุการณ์การแจ้งเตือนจะปรากฏขึ้นเมื่อใดก็ตามที่ค่าที่ ID ไม่ว่างเปล่าและแสดงค่า:
( 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 ที่ช่วยให้คุณสามารถจัดหาฟังก์ชั่นที่กำหนดเองสำหรับการบันทึกค่า ตัวอย่างเช่นหากคุณต้องการใช้วัตถุวันที่ 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 Key ซึ่งคุณสามารถใช้เพื่อตั้งค่าสถานที่ของ 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 } นอกจากนี้คุณยังสามารถจัดหา Hash-Map ที่กำหนดเองให้กับ Datepicker :first-day ทำเครื่องหมายวันแรกของสัปดาห์เริ่มตั้งแต่วันอาทิตย์ที่ 0. ต้องระบุกุญแจทั้งหมด
ตัวอย่างของการใช้ตำแหน่ง 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 }} 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 ))))}]]ตัวอย่างด้านบนปิดใช้งานอินพุตนามสกุลเมื่อค่าของอินพุตชื่อแรกคือ "Bob"
ส่วนประกอบของสนามทำตัวเหมือนส่วนประกอบรีเอเจนต์อื่น ๆ และสามารถผสมกับพวกเขาได้อย่างอิสระ ตัวอย่างแบบฟอร์มที่สมบูรณ์สามารถดูได้ด้านล่าง
องค์ประกอบแบบฟอร์มสามารถผูกพันกับโครงสร้างซ้อนกันโดย . เป็นตัวคั่นเส้นทาง ตัวอย่างเช่นองค์ประกอบต่อไปนี้ [: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 Key เมื่อ :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])การมองเห็นองค์ประกอบสามารถตั้งค่าได้โดยการจัดหา ID ในเอกสารที่จะถือว่าเป็นค่าความจริงหรือฟังก์ชั่น:
( 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 " ]])หากคุณใช้เฟรมใหม่ขอแนะนำให้คุณใช้เหตุการณ์ใหม่เพื่อเรียกการคำนวณฟิลด์ใหม่ในรูปแบบ ตัวอย่างเช่นลองดูที่ฟิลด์ BMI ที่คำนวณได้:
( 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 หลายข้อจะถูกทริกเกอร์เมื่อมีการเรียกเหตุการณ์ :set-value และจะคำนวณค่าดัชนีมวลกายได้ตลอดเวลาที่มีการปรับปรุงความสูงหรือน้ำหนัก
สามารถเพิ่มฟิลด์ที่กำหนดเองได้โดยใช้ reagent-forms.core/init-field หลายวิธี วิธีการจะต้องใช้สองพารามิเตอร์โดยที่พารามิเตอร์แรกคือองค์ประกอบของฟิลด์และที่สองคือตัวเลือก
โดยค่าเริ่มต้นตัวเลือกจะมีการ get และ save! และ update! กุญแจ get Key Points ไปยังฟังก์ชั่นที่ยอมรับ ID และส่งคืนค่าเอกสารที่เกี่ยวข้อง save! ฟังก์ชั่นยอมรับ ID และค่าที่จะเชื่อมโยงกับมัน update! ฟังก์ชั่นยอมรับ ID, ฟังก์ชั่นที่จะจัดการกับการอัปเดตและค่า ฟังก์ชั่นการจัดการการอัปเดตจะได้รับค่าเก่าและค่าใหม่
อะแดปเตอร์สามารถให้กับฟิลด์เพื่อสร้างรูปแบบการจัดเก็บที่กำหนดเองสำหรับค่าฟิลด์ เหล่านี้เป็นคู่ของฟังก์ชั่นที่ส่งผ่านไปยังฟิลด์ผ่านปุ่ม :in-fn และ :out-fn :in-fn ปรับเปลี่ยนรายการที่เก็บไว้เพื่อให้ฟิลด์สามารถใช้ประโยชน์ได้ในขณะที่ :out-fn ปรับเปลี่ยนผลลัพธ์ของฟิลด์ก่อนที่จะเก็บไว้ ตัวอย่างเช่นในการใช้วัตถุ js/Date ดั้งเดิมเป็นรูปแบบการจัดเก็บ
[ :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 %)))}]อะแดปเตอร์อาจผ่าน Nulls ดังนั้นพวกเขาจึงต้องสามารถจัดการได้
Safari on iOS จะมีความล่าช้า 300ms สำหรับ :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 สำหรับ React เป็นอีกทางเลือกหนึ่งสำหรับการสร้างเหตุการณ์ที่ตอบสนองได้จนกว่าฟังก์ชั่นจะพร้อมใช้งานใน React ตัวเอง
โครงการนี้ใช้ Doo สำหรับเรียกใช้การทดสอบ คุณต้องติดตั้งหนึ่งในสภาพแวดล้อมที่รองรับ DOO อ้างถึงเอกสารสำหรับรายละเอียด ในการเรียกใช้การทดสอบเช่นการใช้ Phantom ให้ทำ:
lein doo slimer test
ลิขสิทธิ์© 2018 Dmitri Sotnikov
แจกจ่ายภายใต้ใบอนุญาตสาธารณะ Eclipse ไม่ว่าจะเป็นเวอร์ชัน 1.0 หรือ (ตามตัวเลือกของคุณ) เวอร์ชันใด ๆ ในภายหลัง