Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does swap! with assoc and (-> % .-target .-value) syntax mean in ClojureScript?

I am trying to understund this piece of code from Web Development With Clojure book. Its about clojure script:

(defn message-form [] 
  (let [fields (atom {})] 
   (fn [] 
    [:div.content
     [:div.form-group 
      [:p "Name:"
       [:input.form-control 
        {:type :text 
         :name :name 
         :on-change #(swap! fields assoc :name (-> % .-target .-value)) 
         :value (:name @fields)}]]] 
    [:p "Message:"
     [:textarea.form-control 
      {:rows 4 
       :cols 50 
       :name :message 
       :on-change #(swap! fields assoc :message (-> % .-target .-value))} 
      (:message @fields)]] 
    [:input.btn.btn-primary {:type :submit :value "comment"}]]))) 

Can anybody explain this part:

#(swap! fields assoc :name (-> % .-target .-value)

especially this: ...(-> % .-target .-value)

like image 960
nenad Avatar asked Aug 18 '18 17:08

nenad


3 Answers

[:input {:on-change #(swap! fields assoc :name (-> % .-target .-value)}]

This defines an input and a function that will be called each time the user changes the input field, i. e. types in it. The function will be called with the change event object as argument, which is then bound to %. The event target is the input field DOM element, which has as value its text content. The function then changes fields to contain as :name that content.

The #(…) and the % belong together: #(…) creates an anonymous function and % is the name of its parameter.

The -> is a macro that inverts the usual lispy prefix composition of forms into something resembling postfix composition or "piping": (-> % .-target .-value) is expanded to (.-value (.-target %)).

The .-value notation is JavaScript interop in ClojureScript. It denotes the access of a field "value" of a JavaScript object; (.-value foo) in ClojureScript is essentially the same as writing foo.value or foo["value"] in JavaScript.

like image 64
Svante Avatar answered Nov 12 '22 03:11

Svante


In your question swap! is taking an atom, a function (assoc) that updates the value the atom stores and subsequent arguments that are given to the function.

Here is another way of achieving the same result:

#(swap! fields (fn [prev] (assoc prev :name (-> % .-target .-value)))

See that :name and (-> % .-target .-value) would be the two subsequent arguments in the swap! invocation of your question, where apply is used internally to apply these arguments to assoc. Here there is just an update function - no subsequent arguments.

As you can see prev is the internal value of the atom that we are going to change and swap! just takes an 'updating' function, which returns the next value for swap! to store.

As far as (-> % .-target .-value) goes, remember you are already in the reader macro, so % is the first argument to it. The -> threading macro threads into the first argument of each following function, so % is given to .-target and then the result of that is given to .-value, giving us: (.-value (.-target %)).

:name in the map stored at fields is being set to the target value of some value that is being provided!

Going out a little wider % is a JavaScript event that is provided whenever the text input field is changed - basically whenever the user types text. You rarely want the whole of the event - just the text that has been typed, which is where getting the target of the event (as opposed to the source) and then that target's value, comes in. The .- part means you are getting a JavaScript property - this is 'interop', as opposed to normal ClojureScript syntax.

like image 40
Chris Murphy Avatar answered Nov 12 '22 02:11

Chris Murphy


#() is a reader macro. It is a short form for an anonymous function (fn [args] ...), where % is the first argument of this function.

In this particular case #(swap! fields assoc :name (-> % .-target .-value) is equal to (fn [X] (swap! fields assoc :name (-> X .-target .-value))

-> is a threading macro and (-> X .-target .-value) is equivalent to (.-value (.-target X)).

(.-target X) means get property target of object X.

So you define an anonymous function which receives single argument. This function will change the fields atom so that its key :name will be set to the value of the property value of the object in property target of the object X.

like image 33
akond Avatar answered Nov 12 '22 04:11

akond