简体   繁体   中英

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)

#() 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 .

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.

[: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, ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM