(我对OM和React有点陌生,所以如果我还没见过这方面的文档,我很抱歉。)
我正在尝试制作一个文本框,它可以在编辑过程中验证其输入并设置其格式,类似于autoNumeric JS库。
我的应用程序是用于验证美元货币(例如,$1,234.56形式的字符串)。具体地说,文本框应防止用户在文本框中键入无效字符(如字母),并应动态格式化其内容,以包括美元符号($)和标记数千个位置的逗号。该组件应防止删除这些格式字符。JS toLocaleString函数已经很好地处理了格式化,但我在UI和输入后光标跳跃的位置方面遇到了问题。
我找到了一个相关的react github issue,它描述了如何防止光标在应用程序状态更新后跳来跳去。从该线程捕获的解决方案是a JS Bin,但是它是使用React用javascript编写的。
我正在尝试实现一个使用ClojureScript和Om的版本,但是遇到了麻烦。如果我只使用组件本地状态实现组件,而不使用全局应用程序状态,那么光标将执行预期的行为,并且不会跳来跳去(类似于autoNumeric库)。最终,我希望将该值传播到全局应用程序状态。但是,当我让组件更新全局应用程序状态时,每次更改输入字段后,光标都会跳转。
我尝试了许多变体,包括不使用本地状态(只使用全局应用程序状态),以及混合使用全局和本地。每当我包含全局应用程序状态时,光标都会跳动。
这是我当前的实现,它还没有更新全局应用程序状态,但没有跳转光标。(请注意,我使用的是om-tools,因此其语法可能与普通om稍有不同。)
(defn raw-number [s]
(let [s (if (empty? s "0" (str s)))]
(js/parseFloat (str/replace s #"[^-0-9.]" ""))))
(defn format-currency [s]
(.toLocaleString (raw-number s) "en-US" #js {:style "currency" :currency "USD"}))
(defcomponentk validated-input
"Component that prevents the cursor from jumping after updates."
[data owner opts]
(will-receive-props [_ new-state]
(when-let [dom-node (om/get-node owner "validated-input")]
(let [old-length (-> dom-node .-value count)
old-idx (.-selectionStart dom-node)
_ (set! (.-value dom-node) new-state)
new-len (count new-state)
new-idx (max 0 (-> new-len (- old-length) (+ old-idx)))]
(om/update! data :cursor-pos new-idx))))
(did-update [_ _ prev-state]
(let [new-idx (:cursor-pos data)
dom-node (om/get-node owner "validated-input")]
(set! (.-selectionStart dom-node) new-idx)
(set! (.-selectionEnd dom-node) new-idx)))
(render [_]
(dom/input
(merge
opts
{:type "text"
:ref "validated-input"}))))
(defcomponentk formatted-input
"Formats the text using the provided formatter before rendering it."
[data owner [:opts formatter :as opts]]
(init-state [_]
{:text (formatter "")})
(render [_]
(let [text (om/get-state owner :text)]
(validated-input
text ;; << I recognize this to be shady, passing local state down. but it works to prevent the cursor jumping.
{:opts
(merge
opts
{:value text
:on-change (fn [e]
(let [new-val (formatter (str (.. e -target -value)))]
(om/set-state! owner :text new-val)))})}))))发布于 2016-05-14 13:40:43
我想我被绊倒了,因为对本地状态和全局状态的更改都触发了重新呈现。我最终使用了一种有点老套的变通方法,在本地状态下使用一个原子,以避免重新触发重新渲染。
该解决方案在台式机上运行良好,但由于它依赖于on-click或on-key-down事件,我不确定它是否能在移动设备上运行良好。
无论如何,它都适合我的需求。欢迎评论!
(defn save-position! [owner event]
(let [found-node (om/get-node owner "validated-input")
idx-atom (om/get-state owner :idx-atom)
new-idx (if (nil? found-node) @idx-atom (.-selectionStart found-node))]
(reset! idx-atom new-idx)))
(defcomponentk formatted-input
"Formats the text using the provided formatter before rendering it."
[data owner [:opts formatter :as opts]]
(init-state [_]
{:idx-atom (atom 0)})
(did-update [_ prev-props prev-state]
(let [dom-node (om/get-node owner "validated-input")
old-text (:text prev-props)
new-text (:text data)
old-len (count old-text)
new-len (count new-text)
delta-len (- new-len old-len)
old-idx @(:idx-atom prev-state)
new-idx (max 0 (+ delta-len old-idx))]
(set! (.-selectionStart dom-node) new-idx)
(set! (.-selectionEnd dom-node) new-idx)))
(render [_]
(dom/input
(merge
opts
{:value (formatter (str (:text data)))
:type "text"
:ref "validated-input"
:on-click (fn [dom-node] (save-position! owner dom-node))
:on-key-down (fn [dom-node] (save-position! owner dom-node))
:on-change (fn [dom-node]
(let [new-val (formatter (str (.. dom-node -target -value)))
old-idx @(om/get-state owner :idx-atom)
found-node (om/get-node owner "validated-input")]
(when (not= new-val (.-value found-node))
(set! (.-value found-node) new-val)
(set! (.-selectionStart found-node) old-idx)
(set! (.-selectionEnd found-node) old-idx))
(om/update! data :text new-val)))}))))https://stackoverflow.com/questions/37154609
复制相似问题