Библиотека Clojurescript для предоставления привязки данных формы для реагента, см. Здесь для живой демонстрации.
Библиотека использует атом реагента в качестве хранилища документов. Компоненты связаны с документом с использованием атрибута :field . Этот ключ будет использоваться, чтобы решить, как должен быть связан конкретный тип компонента. Компонент также должен предоставить уникальный атрибут :id , который используется для корреляции его с документом. В то время как библиотека ориентирована на использование с помощью начальной загрузки Twitter, она довольно агностична в отношении типов компонентов, которые вы создаете.
: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 }] В поле Typeadehead используется ключ :data-source связанный с функцией, которая принимает текущий вход и возвращает список результатов соответствующих. В управлении используется элемент ввода для обработки ввода пользователя и отображает список вариантов в качестве неупорядоченного элемента списка, содержащего один или несколько элементов элемента списка. Пользователи могут указать классы CSS, используемые для визуализации каждого из этих элементов, используя клавиши: класс ввода,: list-class и: класс элемента. Пользователи могут дополнительно указать класс CSS для обработки выделения текущего выбора с помощью ключа: класс Hight. Справочные классы CSS включены в файл ресурсов/public/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 " }]Поле Typeaead поддерживает выбор мыши и клавиатуры.
Вы можете сделать отображаемое значение входов отличаться от значения, хранящегося в документе. Вам нужно указать :out-fn , A :result-fn и, необязательно :in-fn . The :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 }]]] If :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 для создания входа диапазона 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 и отобразит значение в этом ключе. Крафы могут иметь необязательные :preamble и :postamble клавиши с текстом, который будет отображаться до и после значения соответственно. Значение также можно интерпретировать с использованием функции Formatter, назначенной :fmt -ключом. The :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 может быть предоставлен для управления, если должна быть оказана кнопка закрытия (по умолчанию TRUE).
Когда событие поставляется, то тело предупреждения отображается всякий раз, когда событие возвращает 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 " } The :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 }]Обратите внимание, что вам нужно вернуть новый объект Date в обновлениях для компонента, чтобы перекрасить.
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}] связывается со следующим путем в состоянии Atom {: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, то рекомендуется использовать события повторной рамы для запуска пересчетания полей в форме. Например, давайте посмотрим на рассчитанное поле ИМТ:
( 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 ключевые точки на функцию, которая принимает идентификатор и возвращает связанное с ним значение документа. save! Функция принимает идентификатор и значение, которое будет связано с ним. update! Функция принимает идентификатор, функцию, которая будет обрабатывать обновление и значение. Функция, обрабатывающая обновление, получит старые и новые значения.
Адаптеры могут быть предоставлены полям, чтобы создать пользовательские форматы хранения для значений поля. Это пара функций, передавающихся на поле через ключи :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 на 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.
Tailventplugin для React - это еще один вариант создания адаптивных событий, пока функциональность не станет доступной в самом React.
Этот проект использует Doo для запуска тестов. Вы должны установить одну из среды, поддерживаемой DOO, обратиться к документам для подробностей. Чтобы запустить тесты, например, с использованием Phantom, Do:
lein doo slimer test
Copyright © 2018 Dmitri Sotnikov
Распределено по общедоступной лицензии Eclipse либо версии 1.0, либо (по варианту) любой более поздней версии.