【问题标题】:How is a chat input field defined in reagent?试剂中如何定义聊天输入字段?
【发布时间】:2017-01-19 19:59:59
【问题描述】:

假设您有一个文本字段,它是用 cljs 编写的带有试剂的聊天程序的输入。它可能看起来像这样:

(defn chat-input []
  (let [written-text (atom "")]
    (fn []
      [:textarea
       {:value     @written-text
        :on-change #(reset! written-text (-> % .-target .-value))}])))

现在实现发送消息的简单方法是添加发送按钮。但是有一种交互对于聊天来说是如此不可或缺,以至于你不能没有它:enter 或 shift-enter 发送消息。但我不知道如何实现它。

我的第一次尝试是简单地添加一个 :on-key-press 事件处理程序来发送消息并将状态重置为“”。此解决方案的灵感来自How to detect enter key press in reagent

(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 事件处理程序覆盖了。

那么您对如何实现此功能有任何想法吗?如果有,请分享!

【问题讨论】:

    标签: textarea chat clojurescript reagent


    【解决方案1】:

    您在正确的轨道上,但忘记了 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 "")))}])))
    

    这应该可以解决问题。

    【讨论】:

      【解决方案2】:

      这里有很多更高级的解决方案,mccraigmccraig on the clojurians slack 非常友好地允许我与您分享。当输入内容变大时,它会扩展文本区域的高度,从而模拟聊天输入在 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/atom 而不是atom。也许在提供的解决方案中就是这种情况,但由于不涉及标头,我有一个疑问。
      猜你喜欢
      • 2021-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-30
      • 1970-01-01
      • 1970-01-01
      • 2019-12-08
      • 1970-01-01
      相关资源
      最近更新 更多