Fork me on GitHub
#reagent
<
2017-11-01
>
cmal05:11:14

Hi, how should I do form submit programmatically in reagent? I have the following code but do not know how to submit this form

(defn logger-iframe [target data]
  (r/create-class
   {:display-name "logger-iframe"
    :component-did-mount (fn []
                           ;; do submit
                           )
    :reagent-render
    (fn [target data]
      [:iframe
       {:style {:position "absolute"
                :left     "-10000px"
                :top      "-10000px"}}
       [:form#f {:action logger-ext-url
                 :method "POST"}
        [:input {:type  "hidden"
                 :name  "target"
                 :value (encode-uri target)}]
        [:input {:type  "hidden"
                 :name  "userid"
                 :value ""}]
        [:input {:type  "hidden"
                 :name  "Uid"
                 :value (get-logger-uid)}]
        [:input {:type  "hidden"
                 :name  "ref"
                 :value (get-ref)}]
        [:input {:type  "hidden"
                 :name  "data"
                 :value (->> data
                             json-stringify
                             encode-uri)}]]])}))

cmal05:11:54

do nothing but to sumit this form

p-himik07:11:59

@cmal Why do you need the invisible form? I think you could just make an AJAX POST request.

cmal07:11:46

for ajax post to another domain name

cmal07:11:03

build an iframe to make a CORS post request.

p-himik07:11:08

Hmm, I thought you could just add or check some headers, and switching to an invisible form shouldn't be any different from a request from JS. But I may be wrong. In any case, you can add :ref attribute to the form, store the passed node in an atom, and call submit on that node in :component-did-mount handler. Although I'm not sure whether :iframe will interfere or not.

cmal07:11:26

@p-himik Does iframe makes send POST request to another host domain possible? Our frontend code already write this to request for another host. And it works. Direct request will be blocked by browser.

cmal07:11:26

If I add a :ref attribute, how to get the component by ref in :component-did-mount?

p-himik07:11:42

I have no idea what to answer on your first question, I have a very limited experience with CORS. Regarding your second question, take a look at this example: https://gist.github.com/pesterhazy/4d9df2edc303e5706d547aeabe0e17e1

cmal07:11:47

Thank you! @p-himik That really helps.

cmal08:11:23

Can ref be used in a :on-click method? It seems the !ref does not have any content util render completes. Or will it make the :reagent-render function to run a second time?

p-himik08:11:49

ref can also be called multiple times - make sure to consult React documentation on the matter. ref should be called before component-did-mount.

p-himik08:11:02

I mean, React will call it.

cmal08:11:53

Do you mean that if I use !ref in a :on-click that will not cause :reagent-render to run again?

cmal08:11:06

Thank you.

cmal08:11:28

Hi, @p-himik Why does this example do the @!ref in the :component-did-update method? Does that have a special meaning? Does that mean only when the :reagent-render function returned can the !ref get a correct value? If so, I doubt that I use the @!ref in a :on-click method will fail because the :reagent-render have not returned, or because the :reagent-render will run twice due to deref the !ref.

p-himik08:11:38

@cmal I don't understand what :on-click method you're talking about - there's none in your example.

p-himik08:11:31

You've specified in that example that you want to submit the form in :component-did-mount. :ref will work there.

cmal08:11:41

Sorry I am thinking of another :input example, the :on-click is in a component under :reagent-render.

p-himik08:11:08

So what's the problem? How exactly this :on-click will be called before the component is rendered?

cmal08:11:30

(defn doumi-pay-helper []
  (let [hide-pay-tip (fn [e] (rf/dispatch [:doumi-use-vip-set-show-help false]))
        show-tip     (rf/subscribe [:doumi-use-vip-rmb-show-help])]
    (r/create-class
     {:display-name "doumi-pay-helper"
      :component-did-mount
      (fn [el]
        (-> js/document
            .-body
            (.addEventListener "click" hide-pay-tip false))
        (-> js/document
            .-body
            (.addEventListener "touchend" hide-pay-tip false)))
      :component-will-unmount
      (fn []
        (-> js/document
            .-body
            (.removeEventListener "click" hide-pay-tip false))
        (-> js/document
            .-body
            (.removeEventListener "touchend" hide-pay-tip false)))
      :reagent-render
      (fn []
        [:span
         {:style    {:position    "relative"
                     :margin-left (base 8)}
          :color    "#000"
          :on-click (fn [e]
                      (let [element (.getElementById js/document "icon-question")]
                        (rf/dispatch [:doumi-use-vip-set-show-help (not @show-tip)])
                        (rf/dispatch [:doumi-style-set-pay-helper-qm-offsetleft
                                      (-> element get-element-left)])
                        (rf/dispatch [:doumi-style-set-pay-helper-qm-offsetwidth
                                      (-> element .-offsetWidth)])
                        (.stopPropagation e)))}
         [:i#icon-question.icon.icon-question]
         (when @show-tip [pay-tip-triangle])
         (when @show-tip [pay-tip])])})))

cmal08:11:50

I want to change the getElementById to a ref

p-himik08:11:35

It will work fine since you can't click on that :span before it's rendered.

cmal08:11:40

I doubt if I use ref in :on-click, it will fail.

p-himik08:11:26

If used according to the React documentation (remember that I said that :ref can be called many times), it won't fail where getElementById works.

cmal08:11:07

OK. Thank you for your explanation. I will try this.

p-himik08:11:25

@cmal By the way, your code looks like it does something similar to what's already implemented in re-com library from the authors of re-frame. You may want to check it out.

cmal08:11:57

OK. Thanks you.

cmal09:11:19

The ref works well. re-com popover.cljs also give me good ideas. Thanks for all that!

cmal09:11:25

(defn doumi-pay-helper []
  (r/with-let [hide-pay-tip (fn [e] (rf/dispatch [:doumi-use-vip-set-show-help false]))
               show-tip     (rf/subscribe [:doumi-use-vip-rmb-show-help])
               icon-question (atom nil)
               _ (-> js/document
                     .-body
                     (.addEventListener "touchend" hide-pay-tip false))]
    [:span
     {:style    {:position    "relative"
                 :margin-left (base 8)}
      :color    "#000"
      :on-click (fn [e]
                  (let [element (r/dom-node @icon-question)]
                    (rf/dispatch [:doumi-use-vip-set-show-help (not @show-tip)])
                    (rf/dispatch [:doumi-style-set-pay-helper-qm-offsetleft
                                  (-> element get-element-left)])
                    (rf/dispatch [:doumi-style-set-pay-helper-qm-offsetwidth
                                  (-> element .-offsetWidth)])
                    (.stopPropagation e)))}
     [:i#icon-question.icon.icon-question
      {:ref (fn [com] (reset! icon-question com))}]
     (when @show-tip [pay-tip-triangle])
     (when @show-tip [pay-tip])]
    (finally
      (-> js/document
          .-body
          (.removeEventListener "touchend" hide-pay-tip false)))))

cmal11:11:17

Hi, how can I prevent the (.submit some-form) method to redirect my page to the form action url and keep the page still in the place I submitted the form? Just like in JS I can add a return false in the function passed to the submit method?

cmal11:11:28

(defn logger-iframe []
  (let [!ref (atom nil)]
    (r/create-class
     {:display-name        "logger-iframe"
      :component-did-mount (fn [] (some-> @!ref .submit))
      :reagent-render
      (fn []
        [:form#logger-form
         {:action logger-ext-url
          :method "POST"
          :ref    (fn [com] (reset! !ref com))}
         [:input {:type  "hidden"
                  :name  "userid"
                  :value ""}]])})))

cmal11:11:33

When the :component-did-mount get called, the page is redirected to logger-ext-url.

cmal11:11:41

How can I prevent this?

p-himik11:11:37

I think that cmal needs submission, but doesn't need redirection.

p-himik11:11:02

If that's the case, forms have target attribute that can reference an iframe. Although I've read somewhere that it was deprecated.

cmal11:11:04

Hi, there are not a on-submit, the submit is called by :component-did-mount

cmal11:11:29

@p-himik Thank you so much.

pesterhazy11:11:41

Note that submitting on component-did-mount looks like an anti-pattern to me. You should cause side effects based on user actions or browser events, not component lifecycles.

cmal11:11:47

Are there any suggestions that can create an iframe and POST to another hostname and then dispose this iframe, when the user click on somewhere on the page? We have a backend service running at our server(another hostname) to record the user actions.

p-himik11:11:36

@cmal I still think that it's something that could be done without iframes and forms, especially if you have the control of that server. E.g. I use Rollbar for basically the same task as you want to implement, and it doesn't create any forms.

cmal11:11:50

The backend is not controlled by me, and the service is not used only by this page. In short, the backend service is not possible to be changed. I tried to proxy this host to my node service but my boss tell me not to do this, he want me to post to another hostname so the browser will not block the request when there are many. (It is said the browser can only deal with 8 request at the same time.) What is your suggestion? @p-himik

p-himik11:11:12

Doesn't something like https://www.html5rocks.com/en/tutorials/cors/ work in your case?

cmal12:11:15

Awesome. Thank you! @p-himik

brunex15:11:32

heya, trying to run reagent 0.7.0 on nodejs and allways getting ReferenceError: React is not defined

swlabrtyr15:11:58

hello friends, I’m a bit of a n00b here, and I’m having trouble with a toggle function in this event listener https://gist.github.com/swlabrtyr/29d0fcd6a740a3807f4a6587e59955df if anyone can assist me in what I’m doing wrong I will be eternally grateful

swlabrtyr15:11:03

I am trying to build a step sequencer with the webaudio api

gadfly36115:11:39

@swlabrtyr it isn't working because you are recreating the local atom on every re-render https://gist.github.com/swlabrtyr/29d0fcd6a740a3807f4a6587e59955df#file-cljs-L14

swlabrtyr16:11:50

I moved the let statement out of the function but the click handler still doesn’t do anything

swlabrtyr16:11:05

at least, it doesn’t change the component’s class

swlabrtyr16:11:52

the handler itself runs, as I am getting “test” statements in my dev console, but my note component’s local-state atom does not change

gadfly36116:11:45

@swlabrtyr The code below should work and introduced the following changes: 1) persisting the local ratom (which we covered before 2) duplicate the arguments to render-note in both the outher and inner functions 3) calling render-note as a regent component (not as a function) 4) adding key metadata to prevent react warning

(defn toggle-class [a k class1 class2]
  (println "test")
  (if (= (@A K) class1)
      (swap! a assoc k class2)
      (swap! a assoc k class1)))


(defn render-note [text key]
  (let [local-state (reagent/atom {:class "note note-off"})]
    (fn [text key]
      [:div {:key      key
             :on-click (fn [_]
                         (toggle-class local-state
                                       :class
                                       "note note-off"
                                       "note note-on"))
             :class    (@local-state :class)}
       (str text (@local-state :class))])))

(defn render-notes []
  [:div {:id "notes-container" :class "notes-container"}
   (doall
    (for [n (range 1 65)]
      ^{:key n}
      [render-note n n]))])

swlabrtyr16:11:24

I included your code but it’s still not working… I think I should do some more reading, I didn’t know you could render hiccup with functions [render-note n n] thanks for the help! I am probably missing something fundamental

swlabrtyr16:11:08

got it working! I just had to invoke the render-note func (render-note) and removed the arguments from the outer func

swlabrtyr16:11:12

thanks again!

swlabrtyr16:11:42

I’m not entirely sure what fixed my issue ¯\(ツ)