Fork me on GitHub
#re-frame
<
2018-12-11
>
andrea.crotti09:12:00

Suppose you have filters in the page which would be nice to update the url automatically

andrea.crotti09:12:51

How are people doing that kind of things? Is there any helper library that just takes a bunch of filter subscriptions and makes sure the url is always in sync?

rgm00:12:46

@U0524T275 I don’t know if there’s an easier way, but I’ve got an example I struggled through up on github: https://github.com/rgm/bidi-pushy-example/

rgm00:12:33

The example is trying to keep clicked links (representing a search, eg. `?s=my+search), a change to an on-screen text input, and a full-page reload (ie. cold-starting your SPA from eg. an emailed search link) all lined up. I’m not sure I’ve done it all right; I’m finding it tricky.

andrea.crotti20:12:59

Thanks I'll have a look

restenb14:12:15

anybody sitting on a good example of file upload using the re-frame http client?

restenb14:12:22

additionally, is it possible to send the file contents as part of a map, like so {:file file :filename filename} ?

restenb14:12:38

i've been trying different things but either due to incorrect client setup or middleware what comes out of the request server-side is unusable

restenb14:12:54

there's data there but it seems to be sent as a string, not as a file object

manutter5114:12:47

You’re using a file input field to get the file contents?

restenb14:12:21

it's a file selector input yes

restenb14:12:32

`[:input {:type "file" :on-change #(dispatch [:upload-file (aget (.. % -target -files) 0)])}]`

restenb14:12:08

that ought to send the file itself through to the :upload-file event in my estimation

manutter5114:12:27

Ok, and in the event handler, if you js/console.log the incoming argument, it’s the file you’re expecting, correct?

restenb14:12:20

yes, at least I think so. it's a javascript object with keys like name type lastModified etc.

restenb14:12:07

might it be that I have to get the File out of there to put in the HTTP request?

manutter5114:12:52

I’m looking at the cljs-ajax docs at https://github.com/JulianBirch/cljs-ajax:

; Send file explicitly, ClojureScript specific
(let [form-data (doto
                    (js/FormData.)
                  (.append "id" "10")
                  (.append "file" js-file-value "filename.txt"))]
  (POST "/send-file" {:body form-data
                      :response-format (raw-response-format)
                      :timeout 100}))

restenb14:12:13

yes, i've been trying that

manutter5114:12:23

:thinking_face:

restenb14:12:52

and it does send something, but it's not a java.File anymore, just raw data

manutter5114:12:33

I haven’t done file uploads in re-frame, but I can play with it and see if I can get a working version.

restenb14:12:01

doing it that way seems to just insert raw data on a nil key in the params map

restenb14:12:22

it might be that some ring middleware is interfering also, but I don't really see how

restenb14:12:12

and I'm also confused by the method of putting file contents in the body directly, as other examples talk about using multipart-params for this (which I've also tried, but then the request isn't even sent, seemingly due to incorrect request format)

restenb14:12:19

that's my current request setup for reference

manutter5114:12:49

Ah, I would guess that :request-format (ajax/json-request-format) is not right for this situation.

manutter5114:12:04

That’s going to try to coerce your file data.

restenb14:12:53

i thought so too, but it turned out to not matter, at least not so far with whatever my problem is

restenb14:12:14

simply removing :request-format still leaves the same output from the handler

restenb15:12:39

i'm using compojure to handle the requests; (POST "/file" {body :body} (response (sql/insert-file! jdbc {:file body :filename ""})))

manutter5115:12:57

On the server side, are you including the ring multipart params middleware?

restenb15:12:14

but the body is just a #object[org.eclipse.jetty.server.HttpInputOverHTTP 0xdf9085 [email protected][c=12876,q=0,[0]=null,s=EOF]], with no apparent way to parse it

restenb15:12:32

yeah i've got a bunch of ring middleware, including multipart-params

manutter5115:12:04

Ok, that org.eclipse.jetty.server.HttpInputOverHTTP object is what the multipart-params middleware should be processing for you, so I’m going to guess that you actually have the client side working correctly, and just need to figure out why the server side isn’t pulling out the file.

restenb15:12:27

really? that's weird

manutter5115:12:26

If I were debugging this, I might try uploading a plain text file (so that when I decode it, it’s not a bunch of binary gibberish), and then use the (ring.util.request/body-string request) trick to see what’s coming down to the server.

restenb15:12:00

"This adds a file key to the :params map of the request where :tempfile is a java.io.File object that contains the uploaded data. You can use this to perform any further processing you want."

restenb15:12:46

as not a http wiz this has me confused. i put the file in :body in the outgoing request, but the parsed version is found in :params ?

restenb15:12:15

and should I use :multipart-params in the request itself instead?

restenb15:12:36

trying it on a simple text file first is probably a good idea 😛

manutter5115:12:03

The reference to the :params map is referring to middleware processing that Ring does on the server side. What’s supposed to happen is that the request arrives at the server with the uploaded file in the :body of the request, as an HttpInputOverHTTP object. The middleware then reads the file data out of that object and stores it in a temp file, and puts a key in the :params map, where the name of the key is (I think) the key that you sent your file under, and the value is an input stream for the temp file on the local filesystem. So by the time your request handler receives the request, all this has happened, and your handler should be able to just look for the temp file under the :params key in the request map.

restenb15:12:05

all I can guess then is that for some reason wrap-multipart-params is not kicking in, since I clearly still have the HttpInput object as :body after all the middleware

restenb15:12:25

which brings me back to; what could be wrong in my HTTP request config (after discarding json as :request-format)

restenb15:12:39

or, i mean, i don't have the parsed contents in :params. I guess :body will be left untouched

manutter5115:12:59

Yeah, my suspicion is that you need to set :request-format to something explicit that says you’re sending multipart form data

👆 4
restenb15:12:26

if so it must be a re-frame specific thing, because ring doesn't seem to require that

manutter5115:12:50

Hmm, I wonder what’s being passed as the Content-Type, can you check the :headers in the request?

restenb15:12:19

it's application/x-www-form-urlencoded;charset=utf-8

orestis17:12:21

I’m fairly certain you need to set multipart/form-data or similar for file uploads to work, but I’m on a phone and can’t double check :)

restenb15:12:47

but the funny part is, I have to remove wrap-params and wrap-multipart-params for that to work

manutter5115:12:20

Ok, everything looks to me like the browser side is doing everything it’s supposed to.

manutter5115:12:43

When you have the multipart params middleware in place, are you getting anything in the request’s :params key?

restenb16:12:53

no, it seems not

restenb16:12:12

i added a log to the last level of middleware, and there's no :params key in the request at all

manutter5116:12:45

Maybe the multipart middleware is in the wrong place? Too early or too late?

manutter5116:12:00

Seems like it has to be something on the server side.

restenb16:12:29

i already tried removing all other parsing middleware except wrap-multipart-params to no avail

manutter5116:12:45

Though just to double-check, you are doing the part where you encode the file data using

(doto
                    (js/FormData.)
                  (.append "id" "10")
                  (.append "file" js-file-value "filename.txt"))
correct?

restenb16:12:55

beyond that those I have should not interfere with eachother

restenb16:12:49

well, shit. 😛

restenb16:12:25

that's what I get for never really learning how to do this stuff in plain javascript

restenb16:12:11

but yeah sending it as js/FormData does the trick, at least in this specific case (with then the FormData as request body)

manutter5116:12:49

Awesome, alls well that ends well, I always say. (Mainly because I’ve been bitten by this sort of thing more than once myself)

restenb16:12:45

and now of course the middleware parses everything out to a handy map with :tempfile and :filename keys

restenb16:12:10

heck, even the :content-type is inferred automatically based on the file

manutter5116:12:10

Cool, glad to hear it because I think I’m eventually going to have to add file uploads to the project I’m working on.

manutter5116:12:19

So now I’ve got some experience working with it.

restenb16:12:35

hehe, cool you got something out of it at least. 🙂

restenb16:12:04

the FormData "trick" is a little buried if your web experience amounts to clojure

manutter5116:12:52

Yeah, it’s nice having a lot of documentation but there’s also the converse problem of knowing where to focus your attention.

restenb16:12:57

meaning if you're using the re-frameclient you really should read the cljs-ajax docs as well

manutter5116:12:17

Yeah. And in some cases, read source code too.

manutter5116:12:04

Though on the plus side I’ve generally found Clojure to have the most readable source code of any language I’ve ever worked in, due to the inherent simplicity.

restenb16:12:35

when done correctly, yes, it's very readable

manutter5116:12:27

Yeah, it’s not impossible to write obfuscated Clojure, but it does lend itself to writing clear code when you know what you’re doing.

restenb16:12:38

there's a very high ceiling for composing though, I just need to look at my own code from about a year ago when I first started learning clojure

restenb16:12:22

my favourite was trying to reimplement the standard java/c way of doing for loops because I just didn't know about loop/recur yet

manutter5116:12:28

Yeah, my early code was a long ways away from what I’d write now.

danielneal16:12:14

yeah it's funny that. There's been a few times recently when I've been struggling to read some Clojure source code, but then the fact that I'm actually reading the source code at all is pretty telling - in earlier non-clojure days I tended just to use libraries as they were because it was much harder to figure out what was going on especially when there were a tonne of classes involved

Whiskas17:12:36

Does someone here uses re-frame with shadow-cljs?

mikethompson23:12:21

REMINDER re-frame-10x allows you to do form-by-form execution tracing of event handlers. And all the trace data captured, is available in your REPL for further experimentation https://github.com/Day8/re-frame-10x/blob/master/docs/HyperlinkedInformation/EventCodeTracing.md

rgm00:12:33

The example is trying to keep clicked links (representing a search, eg. `?s=my+search), a change to an on-screen text input, and a full-page reload (ie. cold-starting your SPA from eg. an emailed search link) all lined up. I’m not sure I’ve done it all right; I’m finding it tricky.