如何在试剂中定义聊天输入字段?
How is a chat input field defined in reagent?
假设您有一个文本字段,它是用带有 reagent 的 cljs 编写的聊天程序的输入。它可能看起来像这样:
(defn chat-input []
(let [written-text (atom "")]
(fn []
[:textarea
{:value @written-text
:on-change #(reset! written-text (-> % .-target .-value))}])))
现在实现发送消息的简单方法是添加一个发送按钮。但是有一种交互对于聊天来说是如此不可或缺,以至于您真的离不开它:输入或 shift-enter 发送消息。但是我不知道如何实现它。
我的第一个尝试是简单地添加一个 :on-key-press 事件处理程序来发送消息并将状态重置为“”。此解决方案的灵感来自 .
(defn chat-input []
(let [written-text (atom "")]
(fn []
[:textarea
{:value @written-text
:on-change #(reset! written-text (-> % .-target .-value))
:on-key-press (fn [e]
(let [enter 13]
(println "Key press" (.-charCode e))
(if (= (.-charCode e) enter)
(reset! written-text "")
(println "Not enter."))))}])))
问题是 :on-key-press
中对 (reset! written-text "")
的调用没有效果,可能是因为它被 :on-change
事件处理程序覆盖了。
那么你对如何实现这个功能有什么想法吗?如果有,请分享!
你在正确的轨道上,但忘记了 js 事件模型:在你的情况下 onChange
和 onKeyPress
都被触发,因为目标是一个文本区域,其中输入键改变了输入.所以在 js 中 onKeyPress
首先被触发,然后如果键会改变某些东西,它会触发 onChange
。您需要的是使用 preventDefault
:
禁用 keyPress
的默认行为
(defn chat-input []
(let [written-text (atom "")]
(fn []
[:textarea
{:value @written-text
:on-change #(reset! written-text (.. % -target -value))
:on-key-press (fn [e]
(when (= (.-charCode e) 13)
(.preventDefault e)
(reset! written-text "")))}])))
应该可以解决问题。
clojurians slack 上的 mccraigmccraig 允许我与您分享更多高级解决方案。随着输入内容变大,它会扩展文本区域的高度,模拟聊天输入在 slack 中的工作方式。
但是这个问题的重要部分是 :on-key-press
包含一个 (.preventDefault e)
。
(defn update-rows
[row-count-atom max-rows dom-node value]
(let [field-height (.-clientHeight dom-node)
content-height (.-scrollHeight dom-node)]
(cond
(and (not-empty value)
(> content-height field-height)
(< @row-count-atom max-rows))
(swap! row-count-atom inc)
(empty? value)
(reset! row-count-atom 1))))
(defn expanding-textarea
"a textarea which expands up to max-rows as it's content expands"
[{:keys [max-rows] :as opts}]
(let [dom-node (atom nil)
row-count (atom 1)
written-text (atom "")
enter-keycode 13]
(reagent/create-class
{:display-name "expanding-textarea"
:component-did-mount
(fn [ref]
(reset! dom-node (reagent/dom-node ref))
(update-rows row-count max-rows @dom-node @written-text))
:component-did-update
(fn []
(update-rows row-count max-rows @dom-node @written-text))
:reagent-render
(fn [{:keys [on-change-fn] :as opts}]
(let [opts (dissoc opts :max-rows)]
[:textarea
(merge opts
{:rows @row-count
:value @written-text
:on-change (fn [e]
(reset! written-text (-> e .-target .-value)))
:on-key-down (fn [e]
(let [key-code (.-keyCode e)]
(when (and (= enter-keycode key-code)
(not (.-shiftKey e))
(not (.-altKey e))
(not (.-ctrlKey e))
(not (.-metaKey e)))
(do
(.preventDefault e)
(send-chat! @written-text)
(reset! written-text "")))))})]))})))
假设您有一个文本字段,它是用带有 reagent 的 cljs 编写的聊天程序的输入。它可能看起来像这样:
(defn chat-input []
(let [written-text (atom "")]
(fn []
[:textarea
{:value @written-text
:on-change #(reset! written-text (-> % .-target .-value))}])))
现在实现发送消息的简单方法是添加一个发送按钮。但是有一种交互对于聊天来说是如此不可或缺,以至于您真的离不开它:输入或 shift-enter 发送消息。但是我不知道如何实现它。
我的第一个尝试是简单地添加一个 :on-key-press 事件处理程序来发送消息并将状态重置为“”。此解决方案的灵感来自
(defn chat-input []
(let [written-text (atom "")]
(fn []
[:textarea
{:value @written-text
:on-change #(reset! written-text (-> % .-target .-value))
:on-key-press (fn [e]
(let [enter 13]
(println "Key press" (.-charCode e))
(if (= (.-charCode e) enter)
(reset! written-text "")
(println "Not enter."))))}])))
问题是 :on-key-press
中对 (reset! written-text "")
的调用没有效果,可能是因为它被 :on-change
事件处理程序覆盖了。
那么你对如何实现这个功能有什么想法吗?如果有,请分享!
你在正确的轨道上,但忘记了 js 事件模型:在你的情况下 onChange
和 onKeyPress
都被触发,因为目标是一个文本区域,其中输入键改变了输入.所以在 js 中 onKeyPress
首先被触发,然后如果键会改变某些东西,它会触发 onChange
。您需要的是使用 preventDefault
:
keyPress
的默认行为
(defn chat-input []
(let [written-text (atom "")]
(fn []
[:textarea
{:value @written-text
:on-change #(reset! written-text (.. % -target -value))
:on-key-press (fn [e]
(when (= (.-charCode e) 13)
(.preventDefault e)
(reset! written-text "")))}])))
应该可以解决问题。
clojurians slack 上的 mccraigmccraig 允许我与您分享更多高级解决方案。随着输入内容变大,它会扩展文本区域的高度,模拟聊天输入在 slack 中的工作方式。
但是这个问题的重要部分是 :on-key-press
包含一个 (.preventDefault e)
。
(defn update-rows
[row-count-atom max-rows dom-node value]
(let [field-height (.-clientHeight dom-node)
content-height (.-scrollHeight dom-node)]
(cond
(and (not-empty value)
(> content-height field-height)
(< @row-count-atom max-rows))
(swap! row-count-atom inc)
(empty? value)
(reset! row-count-atom 1))))
(defn expanding-textarea
"a textarea which expands up to max-rows as it's content expands"
[{:keys [max-rows] :as opts}]
(let [dom-node (atom nil)
row-count (atom 1)
written-text (atom "")
enter-keycode 13]
(reagent/create-class
{:display-name "expanding-textarea"
:component-did-mount
(fn [ref]
(reset! dom-node (reagent/dom-node ref))
(update-rows row-count max-rows @dom-node @written-text))
:component-did-update
(fn []
(update-rows row-count max-rows @dom-node @written-text))
:reagent-render
(fn [{:keys [on-change-fn] :as opts}]
(let [opts (dissoc opts :max-rows)]
[:textarea
(merge opts
{:rows @row-count
:value @written-text
:on-change (fn [e]
(reset! written-text (-> e .-target .-value)))
:on-key-down (fn [e]
(let [key-code (.-keyCode e)]
(when (and (= enter-keycode key-code)
(not (.-shiftKey e))
(not (.-altKey e))
(not (.-ctrlKey e))
(not (.-metaKey e)))
(do
(.preventDefault e)
(send-chat! @written-text)
(reset! written-text "")))))})]))})))