How can I submit multipart/formdata in ClojureScript? Is there any library that supports this? I can fallback to e.g. jquery.form.js but would prefer a plain ClojureScript solution.
I recently made a pull request to cljs-http to support form-data. Util r0man merges it, you can see instruction in my version's README at https://github.com/myguidingstar/cljs-http
Edited: The pull request has been merged. See the original repository instead.
This is how I did it:
(defn generate-form-data [params]
(let [form-data (js/FormData.)]
(doseq [[k v] params]
(.append form-data (name k) v))
form-data))
(defn upload [file]
(go (let [response (<! (http/post "http://localhost/upload"
{:body (generate-form-data {:file file})}))]
(prn (:status response))
(prn (:body response)))))
;; some-dom-element is a single file upload input
;; <input type="file">
(upload (-> some-dom-element .-files first))
If you don't want to use cljs-http, see cljs-http.core/request in its source code for how to make a direct call to XhrIo
https://github.com/r0man/cljs-http/blob/master/src/cljs_http/core.cljs
Take a look at cljs-http:
;; Form parameters in a POST request (simple)
(http/post "http://example.com" {:form-params {:key1 "value1" :key2 "value2"}})
;; Form parameters in a POST request (array of values)
(http/post "http://example.com" {:form-params {:key1 [1 2 3] :key2 "value2"}})
====== UPDATE =======
You'll need some iframe hack. Read this and this:
;; Imports
(:require [goog.events :as gev])
(:import [goog.net IframeIo]
[goog.net EventType]
(defn upload []
(let [io (IframeIo.)]
(gev/listen io
(aget goog.net.EventType "SUCCESS")
#(js/alert "SUCCESS!"))
(gev/listen io
(aget goog.net.EventType "ERROR")
#(js/alert "ERROR!"))
(gev/listen io
(aget goog.net.EventType "COMPLETE")
#(js/alert "COMPLETE!"))
(.setErrorChecker io #(not= "ok" (.getResponseText io)))
(.sendFromForm io (dom/by-id "form") "/upload")))
Related
The question is how to send to a nodejs app the result of a go block
i found a solution with callback
but i need a solution with promise
Promise solution?
Clojurescript app
(defn foo []
(go 1))
;;how to change foo,wrap to promise?, so node app can await to get the 1
;;i used 1 for simplicity in my code i have something like
;;(go (let [x (<! ...)] x))
Node app
async function nodefoo() {
var x = await foo();
console.log(x); // i want to see 1
}
Callback solution (the one that works now)
So far i only found a way to pass a cb function, so this 1 goes back to node.js app
Clojurescript app
(defn foo1 [cb]
(take! (go 1)
(fn [r] (cb r))))
Node app
var cb=function () {....};
foo1(cb); //this way node defined cb function will be called with argument 1
But i dont want to pass a callback function, i want node.js to wait and get the value.
I want to return a promise.
This function takes a channel and returns a Javascript Promise that resolves with the first value the channel emits:
(defn wrap-as-promise
[chanl]
(new js/Promise (fn [resolve _]
(go (resolve (<! chanl))))))
Then to show usage:
(def chanl (chan 1))
(def p (wrap-as-promise chanl))
(go
(>! chanl "hello")
(.then p (fn [val] (println val))))
If you compile that and run it in your browser (assuming you called enable-console-print!) you'll see "hello".
It is also possible to extend the ManyToManyChannel type with extend-type.
Here's a naif implementation using a similar wrap-as-promise function
(require '[clojure.core.async.impl.channels :refer [ManyToManyChannel]])
(defn is-error? [val] (instance? js/Error val))
(defn wrap-as-promise
[channel callback]
(new js/Promise
(fn [resolve reject]
(go
(let [v (<! channel)]
(if (is-error? v)
(reject v)
(resolve (callback v))))))))
(extend-type ManyToManyChannel
Object
(then
[this f]
(wrap-as-promise this f)))
(def test-chan (chan 1))
(put! test-chan (new js/Error "ihihi"))
(put! test-chan :A)
(defn put-and-close! [port val]
(put! port val)
(async/close! port))
(-> test-chan
(.then (fn [value] (js/console.log "value:" value)))
(.catch (fn [e] (js/console.log "error" e)))
(.finally #(js/console.log "finally clause.")))
I am making an HTTP request:
(defn main-panel []
(def API-URL "https://api.chucknorris.io/jokes/random")
(defn getFileTree []
(go (let [response (<! (http/get API-URL
{:with-credentials? false
:headers {"Content-Type" "application/json"}}))]
(:status response)
(js/console.log (:body response))))) ; prints a very complex data structure
(go
(let [result (<! (getFileTree))]
(.log js/console (:body result)))) ; prints null
:reagent-render
(fn []
[:h1 "kjdfkjndfkjn"]))
But I can't get to the "joke" in the returned object, array item 13:
How do I assign this value to a let or def?
Also, why does the second console.log print null?
Update
I am now moving on from using reagent atoms to reframe.
This is my component that successfully GETs data, updates the re-frame 'database':
(defn main-panel []
(def API-URL "https://api.chucknorris.io/jokes/random")
(def request-opts {:with-credentials? false})
(defn getFileTree []
(go (let [response (<! (http/get API-URL request-opts))]
(re-frame/dispatch [:update-quote response]))))
(defn render-quote []
(println (re-frame/subscribe [::subs/quote])) ;successfully prints API data as in screenshot below
(fn []
(let [quote-data (re-frame/subscribe [::subs/quote])
quote-text (if quote-data (:value quote-data) "...loading...")]
[:div
[:h3 "Chuck quote of the day"]
[:em quote-text]])))
(fn []
(getFileTree)
[render-quote]))
But this is the object I get back from the re-frame database:
As you can see it comes wrapped in the Reaction tags and I can't access the body or value any more. How do I access those?
I have a small working version using the reagent template. Create a new project (assuming you have Leiningen installed) with: lein new reagent chuck. This will create a project with many dependencies, but it works out of the box.
Next, edit the file at src/cljs/chuck/core.cljs and edit it so it looks like the following:
(ns chuck.core
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [reagent.core :as reagent :refer [atom]]
[cljs-http.client :as http]
[cljs.core.async :refer [<!]]))
(def api-url "https://api.chucknorris.io/jokes/random")
(def request-opts {:with-credentials? false
:headers {"Content-Type" "application/json"}})
(def api-response (atom nil))
(defn get-quote []
(go
(let [response (<! (http/get api-url request-opts))]
(println response)
(reset! api-response response))))
(defn render-quote []
(fn []
(let [quote-data (:body #api-response)
quote-text (if quote-data (:value quote-data) "...loading...")]
[:div
[:h3 "Chuck quote of the day"]
[:em quote-text]])))
(defn quote-page []
(fn []
(do
(get-quote)
[:div
[:header
[render-quote]]
[:footer
[:p "footer here"]]])))
;; -------------------------
;; Initialize app
(defn mount-root []
(reagent/render [quote-page] (.getElementById js/document "app")))
(defn init! []
(mount-root))
I'll explain the relevant bits:
init will bootstrap the basics of the front-end, but in our case it's just calls mount-root which starts reagent telling it to call quote-page and placing the results in the DOM replacing the element with the ID of app.
quote-page calls get-quote which will call the API using the cljs-http library. I'm not checking for errors here, but basically when the request completes (either success or error) it will read the results from the channel (using <!) and place the response in response. The key is that response is a nested ClojureScript map that you can inspect to check if the result was successful or not. Note that I'm also printing the results with println instead of JS interop (.log js/console xxx) because console.log will show the inner details of how the nested map is implemented, which is not relevant for this case.
One the response is available, I store the results of the response in an atom called api-response. The key here is that the atom will contain nothing for a bit (while the request completes) and then the response will be inside it and reagent will take care of detecting the change and re-rendering.
Finally, quote-page calls render-quote which generates the markup for rendering the quote or a placeholder while it loads.
To run the whole thing, open a terminal and run lein run which will start a web server listening on port 3000 by default. In another terminal, run lein figwheel which will compile the ClojureScript code for you. One figwheel is ready it will start a REPL, and you can open the address http://0.0.0.0:3000/ in your computer to view the page.
Say I have a component which needs to request some data from server before rendering.
What I have now is something like with cljs-ajax library:
(def data (r/atom nil))
(defn component [id]
(r/create-class {:reagent-render simple-div
:component-did-mount (partial get-data id)}))
(defn get-data [id]
(GET (str "/api/" id)
{:handler init}))
(defn init [response]
(let [data1 (:body response)
data2 (compute data1)
data3 (compute2 data2)]
(reset! data (compute3 data1))
(.setup #data data1)
(.setup2 #data data2)
(.setup3 #data data3))
the setup functions are some foreign JS library functions with side effects.
This works but I don't feel like this is the correct way to do callback.
Not to mention if I need to GET other datas based on the first data I got, and then other datas based on that, it would be a very nasty chain of callbacks.
Is there a better, clean way of doing this kind of ajax request in reagent/clojurescript?
The most common way to make requests is cljs-http. Add [cljs-http "0.1.39"] to the dependencies in project.clj and restart the figwheel process in the terminal to pick up the new dependency.
(ns my.app
(:require
[cljs.core.async :refer [<!]] [cljs-http.client :as http])
(:require-macros [cljs.core.async.macros :refer [go]])
(go (let [response (<! (http/get "data.edn"))]
(prn (:status response))
(prn (:body response))))
Cljs-http is a nice way to manage HTTP requests. It uses core.async channels to deliver its results. For now, all you need to focus on is that http/get and http/post calls should occur inside a go form, and the result is a channel that can have its result read with
Dependent http gets can be chained together in a sensible way in a single go block that looks like sequential code, but occurs asynchronously.
I'm using secretary and reagent. This is my code :
(def view (atom nil))
(defn layout [view]
[:div #view])
(reagent/render-component [layout view] (.getElementById js/document "message"))
(secretary/set-config! :prefix "")
(secretary/defroute home-path "/" [query-params]
(timbre/info "Path : /, query params : " query-params)
(let [warning (:warning query-params)
success (:success query-params)
login-failed (:login_failed query-params)]
(when warning
(timbre/info "Warning found : " warning)
(reset! view [:h4 [:span.label.label-warning warning]]))
(when success
(timbre/info "Success found : " success)
(reset! view [:h4 [:span.label.label-info success]]))
(when login-failed
(timbre/info "Login failed")
(reset! view [:h4 [:span.label.label-warning "Login Failed."]]))))
(let [h (History.)]
(goog.events/listen h EventType.NAVIGATE #(secretary/dispatch! (.-token %)))
(doto h
(.setEnabled true)))
Disregarding the :prefix value (I tried "", "#" and also not setting the :prefix at all) this code only works with routes like :
http://localhost:8080/login#/?success=SuccessMessage
But it doesn't work with routes like :
http://localhost:8080/login?success=SuccessMessage
What I'm actually trying to achieve is to parse the login failure from friend, which in case of failure redirects me to
http://localhost:8080/login?&login_failed=Y&username=someUser
and display login failed message to the user. I don't need to use secretary for this, anything that works to parse the query-parameters would be ok for me.
The hard way would be to parse the query string which I can get with:
(-> js/window .-location .-search)
I believe that this is already done well in some library.
I found it. Using https://github.com/cemerick/url (works for both clojure and clojurescript), one can do :
(require '[cemerick.url :as url])
(:query (url/url (-> js/window .-location .-href)))
From the docs:
If a URI contains a query string it will automatically be extracted to :query-params for string route matchers and to the last element for regular expression matchers.
(defroute "/users/:id" [id query-params]
(js/console.log (str "User: " id))
(js/console.log (pr-str query-params)))
(defroute #"/users/(\d+)" [id {:keys [query-params]}]
(js/console.log (str "User: " id))
(js/console.log (pr-str query-params)))
;; In both instances...
(secretary/dispatch! "/users/10?action=delete")
;; ... will log
;; User: 10
;; "{:action \"delete\"}"
Is there a reader function in clojure to parse clojure data structure? My use case is to read configuration properties files and one value for a property should be a list. I'd like to be able to write this as:
file.properties:
property1 = ["value1" "value2"]
and in clojure:
(load-props "file.properties")
and get a map with value {property1, ["value1" "value2"]
Right now,m I'm doing the following, with the same input file "file.properties":
(defn load-props [filename]
(let [io (java.io.FileInputStream. filename)
prop (java.util.Properties.)]
(.load prop io)
(into {} prop)))
;; returns:
;; {"property1" "[\"valu1\", \"valu2\"]"}
(load-props "file.properties")
But I cannot get a way to parse the result to a clojure's vector. I'm basically looking for something like Erlang's file:consult/1 function. Any idea how to do this?
If you want to read java-style properties files, look at Dave Ray's answer - though properties files have many limitations.
If you are using Clojure 1.5 or later, I suggest you use edn, the extensible data notation used in Datomic - it's basically clojure data structures, with no arbitrary code execution, and the ability to add tags for things like instances or arbitrary types.
The simplest way to use it is via read-string and slurp:
(require 'clojure.edn)
(clojure.edn/read-string (slurp "filename.edn"))
That's it. Note that read-string only reads a single variable, so you should set up your configuration as a map:
{ :property1 ["value1" "value2"] }
Then:
(require 'clojure.edn)
(def config (clojure.edn/read-string (slurp "config.edn")))
(println (:property1 config))
returns
["value1" "value2"]
java.util.Properties implements Map so this can be done very easily without manually parsing properties files:
(require 'clojure.java.io)
(defn load-props
[file-name]
(with-open [^java.io.Reader reader (clojure.java.io/reader file-name)]
(let [props (java.util.Properties.)]
(.load props reader)
(into {} (for [[k v] props] [(keyword k) (read-string v)])))))
(load-props "test.properties")
;=> {:property3 {:foo 100, :bar :test}, :property2 99.9, :property1 ["foo" "bar"]}
In particular, properties files are more complicated than you think (comments, escaping, etc, etc) and java.util.Properties is very good at loading them.
Is there a reader function in clojure to parse clojure data structure?
Yes. It's called read. You can also use it to read configuration data.
A file props.clj containing
{:property1 ["value1" 2]
:property2 {:some "key"}}
can be read like this:
(ns somens.core
(:require [clojure.java.io :as io])
(:import [java.io PushbackReader]))
(def conf (with-open [r (io/reader "props.clj")]
(read (PushbackReader. r))))
When reading untrusted sources it might be a good idea to turn of *read-eval*:
(def conf (binding [*read-eval* false]
(with-open [r (io/reader "props.clj")]
(read (PushbackReader. r)))))
For writing configuration data back to a file you should look at print functions such as pr and friends.
contrib has functions for reading writing properties,
http://richhickey.github.com/clojure-contrib/java-utils-api.html#clojure.contrib.java-utils/as-properties
If this is for your own consumption then I would suggest reading/writing clojure data structures you can just print them to disk and read them.
(use '[clojure.contrib.duck-streams :only (read-lines)])
(import '(java.io StringReader PushbackReader))
(defn propline->map [line] ;;property1 = ["value1" "value2"] -> { :property1 ["value1" "value2"] }
(let [[key-str value-str] (seq (.split line "="))
key (keyword (.trim key-str))
value (read (PushbackReader. (StringReader. value-str)))]
{ key value } ))
(defn load-props [filename]
(reduce into (map propline->map (read-lines filename))))
DEMO
user=> (def prop (load-props "file.properties"))
#'user/prop
user=> (prop :property1)
["value1" "value2"]
user=> ((prop :property1) 1)
"value2"
UPDATE
(defn non-blank? [line] (if (re-find #"\S" line) true false))
(defn non-comment? [line] (if (re-find #"^\s*\#" line) false true))
(defn load-props [filename]
(reduce into (map propline->map (filter #(and (non-blank? %)(non-comment? %)) (read-lines filename)))))