Fork me on GitHub
#clojurescript
<
2022-06-07
>
erre lin07:06:07

Hello, I'm wondering why the following does (not) work: I want to get the name (in string format) of the file I just uploaded using <input type="file"> This doesn't work:

[:input
    {:type "file"
     :id "my-img"
     :name "my-img"
     :accept "image/*"
     :on-change (fn [e] (.log js/console (get (-> e .-target .-files) 0)))}]

;; log `null' in the console
I also tried to detect if I got a FileList (as explained by https://developer.mozilla.org/en-US/docs/Web/API/FileList):
:on-change (fn [e] (.log js/console (-> e .-target .-files)))

;; this does log the file list into console
But array? and object? all return false
:on-change (fn [e] (.log js/console (array? (-> e .-target .-files))))

;; log `false' in the console
I searched online for a while and found https://tech.toryanderson.com/2021/11/06/uploading-files-and-handling-upload-requests-in-clojurescript/#ajax-submissions uses aget instead:
(defn submit-image
  "submit the selected image that is on input `#input-id`"
  [input-id]
   (let [el (.getElementById js/document input-id)
    name (.-name el)
    file (aget (.-files el) 0) ;; this line indicates using `aget'
    form-data (js/FormData.)
    _ (.append form-data "file" file)
     submit-params {:uri "/upload-image"
                    :body form-data
                    :method :post}]
     (rfc/dispatch [:upload-image submit-params])))
So I tried it out and this time the code works
:on-change (fn [e] (.log js/console (.-name (aget (-> e .-target .-files) 0))))
But I don't see why.

p-himik07:06:48

Try replacing get with aget.

erre lin08:06:36

@U2FRKM4TW Hi, thank you. That's actually the question I want to ask. I'm sorry I messed again with the Slack's line-break (Shift+Enter). I've edited my question: Why aget works? The https://clojuredocs.org/clojure.core/aget explains:

;; aget can be used to check the existence of an element
;; this approach works with documents in ClojureScript where core/contains? does not
(aget js/document "getElementById")
;;=> #object[getElementById "function getElementById() { [native code] }"]
(contains? js/document "getElementById")
;;=> false
But there is not even an entry in https://cljs.info/cheatsheet/. Did you happen to know if there is other info on this topic? Thank you

p-himik08:06:39

Don't follow that comment - it's misleading. While it does work right now, it's not a part of its contract. Instead, if you need to get an attribute of some object, use either interop (just like you did with e.g. .-target) or goog.object/get (useful for when the name of the attribute is in some variable). aget is for arrays, that's it. In Clojure, it's Java arrays. And in ClojureScript, it's JavaScript arrays.

👍 1
😲 1
p-himik08:06:34

get didn't work in your case because get is for associative CLJ/CLJS data types. A JS object is not such a type.

👍 2
p-himik08:06:51

Regarding why aget works on something for which array? returns false - that's because array? checks only for a specific type of array, the js/Array. But JS has plenty of other arrays and array-like structures. FileList being one of them, Float64Array being another.

👍 1
p-himik08:06:34

BTW, not that it's important, but you can avoid aget in this specific case altogether because FileList has a method called item. So you can use (.. e -target -files (item 0)).

👍 1
erre lin08:06:58

Thank you so much for the detailed information. 👍 I did try using the item method, but perhaps I messed up with other places so I finally landed on aget in my case. I'll try item again. Now all of this is much clearer to me. 🤓

👍 1
Michaël Salihi12:06:10

@dnolen Is your 2013 answer still relevant in 2022, please? https://stackoverflow.com/a/16670400/5773724

dnolen14:06:14

@admin055 yes you can get the current namespace in a macro via &env