clojurescriptclojurescript-javascript-interop

CLJS: Setting a JS property to the result of calling a method on it


I'm looking to modify some existing text in a web app, which I'm accessing with expressions like this:

(.-innerHTML (.getElementById js/document "myElementId"))

What I specifically want to do is mutate that text via a search/replace:

(set! (^that whole thing) (.replace (^that whole thing) "old" "new"))

And I'd ideally like to do that without having to repeat the property-access expression. Is there any existing shortcut for this? I'm envisioning something like Raku's object .= method(args) shorthand for object = object.method(args); maybe a good clj name/pattern would be (.set! (access-expression) method & args). I can always write a macro, but was wondering if I'd overlooked something already there.


Solution

  • I was curious too and looked in the CLJS CheatSheet and they link to the CLJS Oops library which provides similarly looking functions.

    It seems to be that the most robust solution would be to just rely on the Google Closure Library (which is always at hand) and use a combination of goog.object/get and goog.object/set (no need for a macro), something like:

    
    (require 'goog.object)
    
    (defn update-obj! [obj field f & args]
      (let [old-val (goog.object/get obj field)
            new-val (apply f old-val args)]
        (goog.object/set obj field new-val)))
    
    ;; Example:
    (update-obj! (.getElementById js/document "my-div") "innerHTML" (partial str "it works: "))
    

    This should work in both development and optimized output.