CLJS: Hidden Google Closure Library gems

ClojureScript builds on, and ships with, the Google Closure Library (GCL), a general-purpose JS library of utility functions. The library cover a vast area, from JS data types to various Browser APIs. The API docs are comprehensive but lack discoverability. So let’s make a list of the most useful functions in this thread.

What are your favorite GCL functions — the library’s hidden gems?

16 Likes

To get the ball rolling, here’s a first example. goog.object/get allows easy js-obj property access by key. It works just like (clojure.core/get m k)

cljs.user=> (goog.object/get #js{"a" 123} "a")
123

(goog.object/get m k) won’t throw, even if k is not in m or if k or m are nil. You can also supply an optional third argument to specify a default value, if the key is missing.

Next, and similar to clojure.core/get-in, goog.object/getValueByKeys allows nested property access. Note, however, that unlike get-in, it expects each key as a separate argument:

cljs.user=> (goog.object/getValueByKeys #js{"a" #js{"b" 123}} "a" "b")
123
4 Likes

I found the goog.functions namespace worth looking at. E.g. debounce is nice:

https://google.github.io/closure-library/api/goog.functions.html

I also like goog.string.format.

7 Likes

Especially when working with something like React, goog.async.ConditionalDelay is a useful way to wait for dynamic nodes to appear in the DOM. E.g. to scroll a node into view:

(defn scroll-to [eid options]
    (let [scroll-fn #(if-some [dom-node (.getElementById js/document (str eid))]
                             (do (.scrollIntoView dom-node options) true)
                             false)]
        (when-not (scroll-fn) (conditional-delay 200 scroll-fn)))))

Martin Klepsch also has a good summary on the aforementioned Debouncer.

2 Likes

The Closure Cheat Sheet website was a good collection of highlights:

2 Likes

Another useful namespace is goog.crypt. It contains implementations of common hashing algorithms such as MD5 or SHA1. Example usage here.

Similarly, the goog.crypt.base64 namespace contains functions for dealing with Base64-encoded blobs.

3 Likes

Another fun one: goog.ui.IdGenerator

cljs.user=> (import 'goog.ui.IdGenerator)
nil
cljs.user=> (def g (goog.ui/IdGenerator.))
#'cljs.user/g
cljs.user=> (.getNextUniqueId g)
":0"
cljs.user=> (.getNextUniqueId g)
":1"
cljs.user=> (.getNextUniqueId g)
":2"
3 Likes

We found goog.dom.classlist to be very handy for manipulating class-strings on dom elements and goog.positioning for doing all sorts of positioning and attaching elements like popovers and the like.

3 Likes

Positioning looks interesting — how do you use this for popovers etc? Absolutely positioned element appended to the end of the body, then figure out what to put on top, left?

1 Like

goog.testing.PseudoRandom, goog.labs.userAgent are useful

4 Likes

goog.Uri can save you a lot of error prone work.

3 Likes

True, and especially goog.uri.utils/appendParamsFromMap. The recommendation by @jumblemuddle is what prompted me to start this thread: CLJS: generate "GET" http request string

2 Likes

I tried getting the HTML editor to work, got it half working, but it’s so object oriented.
Most dom manipulation is easy through.

This is a great list! I didn’t know most of this stuff.

I like History and Html5History

They have the same API, so you can use Html5History with a fallback to History.

Eric

1 Like

I used this the other day:

... 
(:import (goog.i18n NumberFormat)
         (goog.i18n.NumberFormat Format))
...
(def formatter (NumberFormat. Format/COMPACT_LONG))
(.format formatter 4356555555) ;;=> "4.4 billion"
(.format (NumberFormat. Format/COMPACT_SHORT) 4356555555) ;;=> "4.4B"

I believe there are a lot of i18n options for this as well.

5 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.