Fork me on GitHub
#clojurescript
<
2018-09-27
>
lgessler05:09:29

hey cljs, so, tale as old as time, in the next few months I'll be beginning a new project at work but for various reasons clojure/clojurescript is absolutely out of the picture. honestly I'm wincing at the thought of writing JS again, and i've also been out of the JS loop for two years--do any of you have any advice on favorite JS libraries to use for frontend development, or any advice more generally?

henrik05:09:17

I haven’t used it myself, but check this out: http://thi.ng/hdom

orestis08:09:02

Vue promises to get out of the way and minimize fatigue — so it would be “easy” in the Hickey sense. No idea about “simple” though.

lgessler13:09:30

Thanks all, I'll check these out

Digital Baboon07:09:11

Hey! Does anyone know how I can access a Promise (returned by js/fetch)? In JavaScript I just did response.key to get data, but I have no idea how to go about Clojure. All I get right now is #object[Promise (object Promise]]

ghiden08:09:28

@im how's this?

Digital Baboon08:09:48

I actually have identical code all the way till ;; do something

Digital Baboon08:09:53

I'm doing all that inside of a (def my-data), and am trying to use that (my-data) like this: (println my-data.key)

Digital Baboon08:09:08

which returns "nil"

pradyumna08:09:18

`(-> (js/fetch "http://..")

pradyumna08:09:28

(.then (fn [res]

henrik08:09:39

Also, if you want to check out a more ClojureScript-native way of working with promises, I hear that Promesa is rather good: http://funcool.github.io/promesa/latest/

Digital Baboon08:09:22

I'll check it out

👍 4
henrik08:09:52

It uses Bluebird under the hood, because apparently that’s the most performant alternative at the moment.

Digital Baboon08:09:50

I'm still very much at the "Hello, World" level. I have more-or-less gotten the hang of DOM manipulation, but working with data (like, getting remote data, turning it into a map or vector, so forth) is still very much new for me.

henrik08:09:24

Are you familiar with Go at all? If you’re OK with working with channels, cljs-http might offer an easier way of calling a remote resource. For example,

(ns example.core
  (:require-macros [cljs.core.async.macros :refer [go]])
  (:require [cljs-http.client :as http]
            [cljs.core.async :refer [<!]]))


(defn my-fetch [url params handler]
  (go 
    (let [result (<! (http/post url {:json-params params}))]
      (handler result))))


(my-fetch "" {:foo "bar"} println)

henrik08:09:19

Not necessarily easier if channels is a new concept.

henrik08:09:09

But you don’t have to deal with promises, and it handles the conversion between JSON to EDN for you (both parameters sent, and body returned).

pradyumna08:09:24

this should work. no js->clj before the .then

Digital Baboon08:09:46

This still only returns a promise

ghiden08:09:02

.json returns a promise

pradyumna08:09:12

yes it will return the promise. you'll need to handle the response inside the call back

ghiden08:09:52

once you call js->clj, it's just clj map

Digital Baboon08:09:11

`(def my-data (-> (js/fetch "https://...") (.then #(.json %)) (.then js->clj) (.then (fn [response] ;; do something )) (.catch #(js/console.error %)))) (println my-data)

ghiden08:09:12

is it json are you handling?

ghiden08:09:12

my-data is promise

Digital Baboon08:09:36

Yea, I have no idea how to extract data from it. Everything I've found on Google just leads to another promise

ghiden08:09:58

i would create a function does ajax

ghiden08:09:39

you don't use def like var in js

ghiden08:09:58

but your example doesn't work in js either. If you assign what comes out of fetch, it'll always be a promise.

Digital Baboon08:09:52

Yes but there I can do return response.json()

Digital Baboon08:09:03

and then in another .then block use the data

Digital Baboon08:09:11

however, I'm happy to say I got data working!

Digital Baboon08:09:31

Alright so I got it working. I'm using the cljs-http.client and async macros and the returning JSON is accessible as Clojure vector, or so it seems. Thank you for your help guys! 🙂

👍 4
Digital Baboon08:09:53

I'm actually very excited about this 😄

Digital Baboon08:09:38

It's a bit odd for me that accessing nested data goes like (:data (:that (:goes (:deep data)))), but I suppose I can get use to that

henrik08:09:24

Try threading it, (-> data :deep :goes :that :data)

Digital Baboon08:09:08

Oh that's cool!

henrik08:09:15

It takes data and calls functions on it in order, so it’s not limited to keys.

(-> 1 inc str vector)
;; => ["2"]

👍 4
henrik08:09:48

Be aware of ->> as well. The difference is what parameter the argument is mapped to for the subsequent functions. https://clojuredocs.org/clojure.core/-%3E%3E

pradyumna08:09:14

also can use (get-in data [:deep :goes :that :data]) with option to provide default value as well (get-in data [:deep :goes :that :data] default-value)

👍 4
😲 4
Digital Baboon09:09:45

So say that I set global state (set! js/app-name "Loading ...") and then get the actual name with a async call to an API. How do I overwrite js/app-name so it changes throughout the application?

Digital Baboon09:09:26

As much as I've read, everything is immutable (constant), which means I can't, right?

henrik09:09:30

If you’re building a webpage, have a look at Reagent for a straightforward way to handle components (functions, really), and state: http://reagent-project.github.io/

henrik09:09:30

Basically, it functions on the notion of a reactive atom. So instead of set! you would swap! some value in the atom, and all bits of the page that relies on it will be re-rendered automatically.

henrik10:09:52

Specifically, check out this example for how state is handled:

(ns example
  (:require [reagent.core :as r]))

(def click-count (r/atom 0))

(defn counting-component []
  [:div
   "The atom " [:code "click-count"] " has value: "
   @click-count ". "
   [:input {:type "button" :value "Click me!"
            :on-click #(swap! click-count inc)}]])
(it’s taken straight from the example in the URL above)

myguidingstar09:09:48

I've noticed clojurescript method names sometimes have dash (`-`) prefixes, like these https://github.com/omcljs/om/blob/master/src/main/om/core.cljs#L64

myguidingstar09:09:02

what's the meaning of that convention?

bbss09:09:12

I've always considered that means private, but could be wrong.

rastandy10:09:30

hello everyone

rastandy10:09:57

is there a way to customize the compiler output when compiling clojurescript through lein cljsbuild?

rastandy10:09:20

I’m trying to make a dev build work inside an aws lambda env

rastandy10:09:41

to be able to develop clojurescript nodejs apps with figwheel, having a repl and testing locally apis through AWS sam

mfikes11:09:21

@myguidingstar To avoid collisions with other names, it became idiomatic in ClojureScript to name protocol functions with hyphens. It doesn't semantically convey anything; it is just a convention. (Source: Stuart Halloway in Clojure Inside Out.)

myguidingstar11:09:35

@mfikes thanks for the info, though it's still unclear to me whether that convention is useful or not 😞

myguidingstar11:09:29

tbh I find the hyphen prefixes ugly 🙂

mfikes11:09:55

Oh, it is very helpful. For example, when implementing name, which works on keywords and symbols, there is an INamed protocol that defines -name. This allows Keyword and Symbol to define -name without colliding with name. It also allows name to be used in contexts where you don't want it to be a protocol function: name works on strings without having to extend INamed to them.

mfikes11:09:02

(So one pattern is to not call protocol functions directly in your code, but indirect through un-prefixed regular functions: In that case, the regular function can often be the place where you can implement cross-cutting functionality, like validation, etc.)

mfikes11:09:12

Here is a concrete example of common stuff being done in a regular function foo:

(defn- service-provider [opts]
  (or (::service-provider opts) (default-service-provider opts)))

(defn foo [representation signature opts]
  (-> (-foo (service-provider opts) representation signature)
    (assoc :type ::foo)))

mfikes11:09:08

In this particular example, even the object being operated upon has been essentially pulled out and moved over into opts.

myguidingstar11:09:55

hmm, this convention seems to be used in cljs only, or am I wrong?

mfikes11:09:50

That could be true. Your example is from Om, but ... 🙂

mfikes11:09:19

So, if you see -foo, it in fact may be the result of it either being in ClojureScript, or code heavily influenced by ClojureScript's implementation. It is an interesting question as to whether this is common outside of that subset.

mfikes11:09:43

The foo example above falls in the "heavily influenced" category.

dnolen12:09:37

I think -foo makes sense if it’s like ClojureScript, implementation thing and it will be wrapped by foo

mfikes12:09:33

To be honest, that’s what -foo has started to meant to me when I see it (internal implementation, most likely protocol)

Digital Baboon15:09:06

What do people recommend here for routing? (I'm looking for simplicity over anything)

Digital Baboon15:09:31

Thus far anything I've looked at seems unnecessarily complicated. Like, is it possible to do (route "/article/:id" "function-to-call") or something?

Digital Baboon15:09:00

And then simply (defn function-to-call (str "Hola!"))

Digital Baboon15:09:22

Ah that looks exactly what I need. Is it compatible with CLJS?

Jan K15:09:55

ah, actually I don't think it does CLJS

Braden Shepherdson15:09:25

I've never found a router for single-page apps that didn't feel complex. but also routing is kind of a tricky business, and it feels straightforward until it isn't anymore.

Braden Shepherdson15:09:47

(but I'll have need of one soon too, so let me know what you find!)

henrik15:09:14

Secretary or Bidi for routing.

justinlee15:09:35

I’ve been using secretary + accountant, and while I won’t say they are simple or elegant, they do work.

henrik15:09:52

I've used Secretary successfully, with Accountant for history integration.

justinlee15:09:21

if I were going to start again afresh I’d probably use reitit just because metosin seems to produce very well thought through libraries

justinlee15:09:29

but I haven’t tried it

Aron15:09:32

i do reverse routing. instead of keeping a central routing table to know on which page what I want to show, for each higher order visual component for which this is an issue, I keep a list of urls where to show it.

Aron16:09:04

the whole canonical url thing is a really bad idea anyway

Dormo02:09:26

Are you sure it works in the browser?

Dormo02:09:50

It uses cljs-ajax and there's no mention of node on the README https://github.com/JulianBirch/cljs-ajax

Dormo02:09:44

I imagine anything with ajax in the title will only work in browsers.

deliciousowl18:09:15

@justinlee just watched a metosin talk today. Good stuff

deliciousowl18:09:33

Seem to 'get' what clojure is all about