Luminus/Reagent Clojurescript forms and POST data

921 views
Skip to first unread message

Webdev Tory Anderson

unread,
Nov 3, 2015, 6:13:35 PM11/3/15
to Luminus

I have a simple form for updating user data, but I'm having some embarrassing difficulties.


Goals

  1. Server receives data sent by the form and makes necessary changes
  2. Response is handled by a function and the client does NOT redirect, but just updates in Reagent as I change my primary Atom

Attempts

  1. Avoid the default form action and use cljs.AJAX to send form data This would have the benefit of letting me easily set my handler for the response, which I've done on other components of this app. The problem here is that, though cljs.ajax supports submission of js/FormData contents as the :body and is supposed to automagically set headers etc, something else in Luminus seems to be intercepting these and in the end, my request just has :form-params {}, :content-type application/transit+json; and :body #object[java.io.ByteArrayInputStream 0x675ca573 java.io.ByteArrayInputStream@675ca573], regardless of how I try to change these manually.
  2. Use the default form submit When a regular form submit button is used to POST to the action path, the server receives the data just fine. However, the browser then proceeds to try to GET the same URL, when I want it to just stay put and handle the (transit-data) response. I've tried any number of combinations of returning false, .preventDefault, and other tricks, but nothing seems to be doing what I want.

Code

(defn submit-update-form []
  (let [form (.getElementById js/document "userform")
        url "/update-user"
        ;_ (.log js/console form-data)
        csrf-token (.-value (.getElementById js/document "token"))]
    (POST url {;;:handler #(js/alert (str "Submission!" %))               
               ;:format "application/x-www-form-urlencoded"
               :body (js/FormData. form)
               :response-format (raw-response-format) ; Doesn't seem to be heeded
               :headers {:x-csrf-token csrf-token
                         ;:content-type "application/x-www-form-urlencoded"
                         }
               })))

(defn user-page []
    [:div.container.user-account
     [:h1 (ttd/get-user-detail :username)]
     [:form.container.user-account {:id "userform"
                                    ;:on-submit #(submit-update-form)
                                    :method "post"
                                    :action "/update-user"
                                    }
      [:input {:type "hidden" :name "__anti-forgery-token" :value (.-value (.getElementById js/document "token"))}]
      [:fieldset.email
       [:legend "Contact Information"]
       [:span.email (label-classes) "Email"]
       [:input.email {:name "email" :type "text" :defaultValue (ttd/get-user-detail :email)}]]
      [:p.bottom-gap]
      [:input.btn.btn-info {:type "submit" :value "Update"}]
      [:a.btn.btn-danger {:href "/"} "Cancel"]
      [:button.btn.btn-warning {:on-click #(js/alert "Logging out is not supported at this time")} "Log Out"]]
     ])

Webdev Tory Anderson

unread,
Nov 3, 2015, 6:35:50 PM11/3/15
to Luminus
Related post on StackOverflow

Dmitri

unread,
Nov 3, 2015, 11:52:37 PM11/3/15
to Luminus
If you're using ajax then you don't want to use the form submit. Instead, you should collect the fields and then call the Ajax function in the on-click event. If you're using Reagent then you can take a look here for an example https://github.com/yogthos/yuggoth/blob/master/src-cljs/yuggoth/components/login.cljs

Webdev Tory Anderson

unread,
Nov 4, 2015, 4:19:18 PM11/4/15
to Luminus
So, I have captured the form and converted it to Transit, looking like this:

["^ ","~:__anti-forgery-token","+ZDkcZMEYG5G7dnp4tr7v/pQP2RqZc00fJu4cYzEfHz3Hr5MVxDfFGl/YzLr6GdhCyzIfxXM8B5IKMAS","~:username","adrf","~:email","","~:app-type","professional"]

But once it hits the server it is STILL received as a :body like this:

#object[java.io.ByteArrayInputStream 0x16bcd64 java.io.ByteArrayInputStream@16bcd64]

Despite all the content-types being set at application/transit+json.

Do I need to change settings from default in middleware in order to receive transit sensibly? Or am I supposed to do something with the ByteArrayInputStream?

Webdev Tory Anderson

unread,
Nov 4, 2015, 4:47:10 PM11/4/15
to Luminus
I have this working now by performing a transit read on the input stream. But did I misunderstand that the Luminus setup should have handled this out of the box? Is there something I can add to middle-ware to automatically parse transit bodies?

Dmitri

unread,
Nov 4, 2015, 5:48:28 PM11/4/15
to Luminus
You should be able to use the ring-middleware-format to deserialize the request automatically https://github.com/metosin/ring-middleware-format that should be enabled by default. The middleware relies on you specifying the correct header as shown in the docs, e.g:

(POST "/send-message"
       
{:headers {"Accept" "application/transit+json"}
         
:params {:message "Hello World"
                 
:user    "Bob"}

         
:handler handler
         
:error-handler error-handler}
)

Webdev Tory Anderson

unread,
Nov 4, 2015, 6:39:45 PM11/4/15
to Luminus
Ah... that's what I was missing. I wasunder the impression that the accept header was an instruction for the response-type, without effect upon the request type. Thanks!
Reply all
Reply to author
Forward
0 new messages