ClojureScript - get checkbox element value - clojurescript

I have the following code (from om.next) creating a checkbox input. The creation works fine, but I don't know how to get the event value, as I don't see the value somewhere in the logs when I click on it (there is a lot of data though) :
(dom/input #js {:type "checkbox"
:onClick (fn [e] (js/console.log e)) ;; how do I get the current true/false value from `e` ?
})
I get in the logs (abbreviated) :
SyntheticMouseEvent {dispatchConfig: Object, dispatchMarker:
".0.0.1.$[cardpath]=1[om_tutorial=1B_UI_Exercises]=1[exercise-3].0.$-184795562.1.0",
nativeEvent: MouseEvent, target: input, currentTarget: input…}
Note : the code is from there.

(.- target e) returns you the element,
generally you want the .-value from an element, but for a checkbox you want .-checked instead... so something like
(.. e -target -checked)

Related

Javascript spread operator in Clojurescript?

I am trying to achieve the following javascript code in clojurescript:
const a = {
"foo": "bar",
//...
};
let b = {
...a,
//^ what is the clojurescript equivalent for this?
"newprop": 10,
};
I have tried to assoc-in, thinking it would behave like a clojure map, with no success...
To provide you with another option, you can use goog.object to interact with JavaScript objects in ClojureScript. The following code will work:
(require 'goog.object)
(def a #js {:foo "bar"})
;; Modify `a` inline
(goog.object/set a "newprop" 10) ;; In JS, this is equivalent to a.newprop = 10
If you want to do a shallow copy of a and modify that value, you can use clone (which will behave like the spread operator).
;; Shallow copy a
(def b (goog.object/clone a))
;; Modify the cloned object
(goog.object/set b "newprop" 10)
There's a neat library you can use though to interact with JavaScript objects if you need to do it often: https://github.com/binaryage/cljs-oops
My question was not clear enough, I had an object defaultProps coming from an external js library. My goal was to create a new instance of this js object and extending it with new props, and feeding it back to a js function expecting a js object. There was more to it than I first foresaw. I finally managed to do it with some juggling with js->clj and clj->js:
(def b
(clj->js (assoc (js->clj a) "newprop" 10)))
Thank you for your answers!
(def a {:foo "bar"})
(def b (assoc a :newProp 10))
This is really just combining two maps together. That can be done with merge:
(def a {"foo" "bar"}) ; Emulating the constant
(def b {"newprop" 10})
(def c (merge a b)) ; {"foo" "bar", "newprop" 10}

Disable Elm form submission without no-op message

I have an Elm app with an HTML form (containing an input field and a submit button) that I would like to disable while the input is not valid. I've managed to do that by binding form submission to a no-op message when the input is invalid:
type Msg
= Input String
| Submit
| Noop
viewForm : Model -> Html Msg
viewForm = form [ onSubmit (if model.valid then Submit else Noop) ]
[ input [ onInput Input ] []
, button [ disabled (not model.valid) ] [ "Submit" ]
]
update then does nothing on a Noop message. The button is also disabled, but this is secondary, since I also care about form submission by hitting Enter from the text input. Note that it does not work to skip the onSubmit handler, because then hitting Enter will reload the page.
This feels a bit messy and inefficient, so my questions are:
What is the idiomatic way to achieve this in Elm?
How much more expensive is this use of a no-op message to a no-op Javascript event handler.
First of all, I have to say that your implementation is alright, there's no harm in sending Noop message, that's why it exists.
The best scenario would be to remove the listener entirely(if possible) and disable the button visually. This actually might have a performance drawback if model.valid changes super-frequently.
I'm using a helper for conditionally adding stuff to a list:
appendIf : Bool -> a -> List a -> List a
appendIf flag value list =
if flag == True then
list ++ [ value ]
else
list
So you could use it like that:
view model =
button
([ type_ "" submit ] |> appendIf model.valid (onClick Submit))
[]

How can I change CSS class name dynamically with reagent?

About reagent.
I need to change some CSS class name dynamically.
How should I do that?
Sample code is here.
(defn app []
(let [array [1, 2, 3]]
(fn []
[:div
(for [index array]
;; I wanna change this classname like `item-1, item-2, ...`
^{:key index} [:div.i-wanna-change-this-classname-dynamically index])])))
Change
[:div.i-wanna-change-this-classname-dynamically index]
to
[:div {:class (str “item-” index)} index]
Reagent provides a shorthand syntax of :div.class1.class2#id, but you can also set these in a map as the first item in the vector after :div.
Also keep in mind the CSS :nth-child() selector as another option for dynamic styling.

Editing an HTML page with Clojure's zip maps and Hickory

UPDATE: See updated observations below.
ORIGINAL:
I have a usage question about the Clolure library Hickory. I want to use it to find code listings in an HTML page and do syntax highlighting on those pages. (This is a desktop app, not a web app.) Here is what I have tried.
(ns ....
(:require [clojure.data.zip.xml :as zx]
[clojure.zip :as zip]
[clygments.core :as cly]
[hickory.core :as hkc]
[hickory.render :as hkr]
[hickory.zip :as hkz])
...)
(defn hilite
"Return a version of the input HTML with syntax highlighted
code listings."
[html-in]
(let [hz (hkz/hickory-zip (hkc/as-hickory (hkc/parse html-in))) ; Convert to zipmap.
cz (zx/xml-> hz :html :body :pre :code)] ; Get array of zips containing code listings.
(doseq [code-z cz] ; For each code listing...
(let [code-n (zip/node code-z)] ; Convert to a zipmap to node.
(if-let [lang (get-in code-n [:attrs :class])] ; See if the node contains a class...
(when (.startsWith lang "language-") ; ...with a language declaration.
(let [language (str/replace lang "language-" "") ; Extract the language.
cntnt (:content code-n) ; Get the content (the code listing).
hili (cly/highlight cntnt (keyword language) :html {:styles "xcode"}) ; Do the highlighting.
prsed (first (hkc/parse-fragment hili))] ; Parse the highlighted code listing.
(zip/replace code-z prsed) ; Replace the original with the highighted version.
(zip/root code-z)))))) ; Unzip and apply the changes.
(hkr/hickory-to-html (zip/node hz)))) ; Return the html with highlighted code listings.
Things work fine up to the point where I try to replace the existing code listing with the highlighted version -- the code listings are found correctly and highlighted versions are generated correctly. But I just get back a copy of the original HTML, not the highlighted version. I assume I am getting something wrong with zip/replace and zip/root, but I don't know what. I have been looking at the docs and examples for hickory and clojure.zip, but still haven't gotten to a working version.
Do you see what I am doing wrong?
UPDATE: As was pointed out by Alan Thompson, replacing the contents of a node does not do the edit in-place but returns a copy with the modification, as is typical for Clojure. But there was still more wrong. The update itself was structured incorrectly. The structure of the new node intended to replace the existing node has to be fleshed out a bit more. Here is a revised version that sort of works.
(defn hilite
"Return a version of the input HTML with syntax highlighted
code listings."
[html-in]
(let [hz (hkz/hickory-zip (hkc/as-hickory (hkc/parse html-in))) ; Convert to zipmap.
cz (zx/xml-> hz :html :body :pre :code)] ; Get array of zips containing code listings.
(doseq [code-z cz] ; For each code listing...
(let [code-n (zip/node code-z)] ; Convert to a zipmap to node.
(if-let [lang (get-in code-n [:attrs :class])] ; See if the node contains a class...
(when (.startsWith lang "language-") ; ...with a language declaration.
(let [language (str/replace lang "language-" "") ; Extract the language.
cntnt (:content code-n) ; Get the content (the code listing).
hili (cly/highlight cntnt (keyword language) :html {:styles "xcode"}) ; Do the highlighting.
hck-hili (hkc/as-hickory (first (hkc/parse-fragment hili))) ; NEW
new-node {:type :element :attrs {:class (str "lang-" language)} ; NEW
:tag :code :content [hck-hili]} ; NEW
nz (zip/root (zip/replace code-z new-node))] ; Unzip and apply the changes. NEW
(hkr/hickory-to-html nz)))))))) ; Generate the html with highlighted code listings.
By the time the HTML is generated at the bottom of the function, it contains the highlighted code listing. There are still a few problems with this though. First, since this editing takes place in a doseq form, it returns nil, not the generated HTML. Second, if there is more than one listing to highlight, each pass through the doseq is editing the original HTML, not one that contains any previous edits. Sigh. Have to re-think the approach.
Not being familiar with Hickory, it looks like these 2 lines are the problem:
(zip/replace code-z prsed)
(zip/root code-z)
The return value of replace is not being used, so root is being called with the original value of code-z. You probably meant for something more like this:
(zip/root
(zip/replace code-z prsed))
so that root is begin called with the output of replace as its input. Since data structures in Clojure are immutable, any changes generate a new data structure as the return value of the function; the original input to the function remains unchanged.
After some additional research, I came across this blog post. The solution proposed involves parsing the html into a zipmap and walking the entire tree of nodes. Each node is checked for whether it should be edited, and if so an editing function is applied and used to replace the original node in the tree.
The tree editing is quite general. You give the tree editing function the root node of the zipmap, a predicate that determines if a particular node should be edited, and an editing function that returns a revised version of the node. Here is what I came up with for syntax highlighting code listings in an HTML page.
(ns ...
(:require [clojure.string :as str]
[clojure.zip :as zip]
[clygments.core :as cly]
[hickory.core :as hkc]
[hickory.render :as hkr]
[hickory.zip :as hkz]))
(defn is-highlightable?
"Return true if the node is of a type that this program can
highlight. Specifically, is the node wrapped in a :pre tag,
tagged as :code and does it include a language name."
[node]
(let [is-pre (= (:tag node) :pre)
fc (first (:content node)) ; Get inside the <pre> tag.
has-code (= (:tag fc) :code)
has-class (get-in fc [:attrs :class])
has-language (when has-class
(.startsWith has-class "language-"))]
(and is-pre has-code has-class has-language)))
(defn hilight-node
"Return a highlighted version of the source code in the node."
[node]
(let [fc (first (:content node)) ; Get inside the <pre> tag.
lang (get-in fc [:attrs :class])
language (str/replace lang "language-" "")
cntnt (first (:content fc))
hili (cly/highlight cntnt (keyword language) :html)
frag (hkc/parse-fragment hili)
hck-hili (hkc/as-hickory (first frag))
new-node {:type :element :attrs {:class (str "lang-" language)}
:tag :div :content [hck-hili]}]
new-node))
(defn edit-html-tree
"Take a zipper, a function that matches a pattern in the tree,
and a function that edits the current location in the tree. Examine the tree
nodes in depth-first order, determine whether the matcher matches, and if so
apply the editor."
[zipper matcher editor]
(loop [loc zipper]
(if (zip/end? loc)
(zip/root loc)
(if-let [matched (matcher (zip/node loc))]
(let [new-loc (zip/edit loc editor)]
(if (not= (zip/node new-loc) (zip/node loc))
(recur (zip/next new-loc))))
(recur (zip/next loc))))))
(defn hilight-syntax
"Return a version of the input HTML where the code listings have
been syntax highlighted."
[html-in]
(hkr/hickory-to-html (edit-html-tree
(hkz/hickory-zip (hkc/as-hickory (hkc/parse html-in)))
is-highlightable?
hilight-node)))
The code is specific to the way my input HTML is laid out to begin with, but could probably be used with few modifications. The edit-tree function is quite general, as mentioned above, and can be used for other, similar editing tasks just by supplying a different matching predicate and node editing function.

Why is it not possible to define the state of a reagent component in a let?

Say that we have a text text area defined in hiccup syntax.
(def written-text (reagent/atom ""))
(defn text-area []
[:textarea
{:value #written-text
:on-change #(reset! written-text (-> % .-target .-value))
:on-click #(println #written-text)}])
Say that we want to have multiple copies of text-area in our document, each with different state in them. Then we'll have to move the state currently available to all in the current namespace into a lexically scoped symbol. Something like:
(defn text-area []
(let [written-text (reagent/atom "")]
[:textarea
{:value #written-text
:on-change #(reset! written-text (-> % .-target .-value))
:on-click #(println #written-text)}]))
But as it stands this code doesn't work. The text field always ends up empty no matter what the user inputs. Why is that? And how do I enclose my state in a per component lexical scope?
The answer to this issue can be found in the re-frame documentation which describes the different forms of components that are normally found in a reagent project. It states that in order to provide a persistent lexical scope for the per-component state to live in, one must write a function which returns another rendering function; else the state atom will be redefined every time reagent tries to re-render the component. So for the given component:
(defn text-area []
(let [written-text (atom "")]
(fn []
[:textarea
{:value #written-text
:on-change #(reset! written-text (-> % .-target .-value))}])))
This way, the function called by reagent when it wishes to redraw the component is the internal, anonymous, function. The let which defines the lexical scope won't be re-run.
All credit for this answer goes to user mccraigmccraig on the clojurians slack.