如何在试剂中定义聊天输入字段?

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 事件模型:在你的情况下 onChangeonKeyPress 都被触发,因为目标是一个文本区域,其中输入键改变了输入.所以在 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 "")))))})]))})))