Reagent-Forms radio buttons displaying as text fields - clojure

I am trying to display a group of radio buttons in a reagent/cljs app. I have followed the same process from http://yogthos.github.io/reagent-forms-example.html but the radio buttons I am displaying are showing up as textfield input boxes.
(def ^:private options (atom nil))
(defn set-options []
(reset! options
[{:name "label name"}
{:name "label name"}
{:name "label name"}]))
(defn set-radio-buttons []
(set-options)
(for [option #options]
[:div.radio
[:label
[:input {:field :radio}]
(option :name)]]))
(defn response-box []
[:div#response
(set-radio-buttons)])
I am then placing response-box in the ui layer of the app.
Thanks

Field is not a correct input element attribute.
[:input {:field :radio}]
(option :name)]]))
Should probably be
[:input {:type :radio}]
(option :name)]]))

Related

How do I access individual fields of a form in Clojure?

I'm building my very first web app, and I am having a hard time accessing individual fields of a form when the user submits the form. Here's what I have:
(defroutes app
(GET "/" [] homepage)
(POST "/city" request display-city)
(route/resources "/")
(route/not-found "Not Found"))
(defn display-city [request]
(html5
[:div {:class "the-city"}
[:h2 "ALL ABOUT YOUR CITY"]
[:ul
[:li "Your city is " (str request) "! That's all"]]]))
;; and here's the hiccup form:
[:form {:action "/city" :method "post"}
(anti-forgery-field)
[:p "Enter your home address"]
[:div
[:label {:for "street-field"} "Street:"]
[:input {:id "street-field"
:type "text"
:name "street"}]]
[:div
[:label {:for "city-field"} "City:"]
[:input {:id "city-field"
:type "text"
:name "city"}]
[:div
[:label {:for "state-field"} "State:"]
[:input {:id "state-field"
:type "text"
:name "state"}]
[:label {:for "zip-field"} "ZIP:"]
[:input {:id "zip-field"
:type "text"
:name "zip"
:size "10"}]]
[:div.button
[:button {:type "submit"} "Submit"]]]])
;; When I run the code above, I can see the entire form that's submitted via (str request), in what looks to be a Clojure map. But I can't figure out how to extract individual "key/vals" (from that address form, I'd like to extract the city), or how to store those results in a way that I can use it. Any ideas?
This is a super basic /city page that I am trying to get running to understand how things work before building bigger things. Thanks!
In your request map, there should be a key :form-params with a map of key/value pairs that were POSTed. Here's how you could get an individual value out:
(get-in request [:form-params :city])
Or you could destructure :form-params map to bind many values at once:
(let [{:keys [city state zip]} (:form-params request)]
(format "%s, %s %s" city state zip))

Simple Clojurescript form

I'm working with Reagent and CLJS, familiar with React and Clojure, less so CLJS. I'd like to make a simple form, but it's not obvious to me in CLJS.
(defn form []
[:div
[:input {:type "text" :name "first-name" :id "first-name"}]
[:button {:on-click (fn [e] (test-func "hello"))}
"Click me!"]
])
I want to grab the value of that input, and pass it to a function when the button is clicked. How do I get that input's value into my on-click function?
The idiomatic and technically correct way is to avoid keeping any state in DOM and accessing it directly. You shouldn't rely on the input's value. Keep the state as Reagent's atom. Then you can do anything with it.
(def first-name (r/atom ""))
(defn form []
[:div
[:input {:type "text"
:value #first-name
:on-change #(reset! first-name (.-value (.-target %)))
}]
[:button {:on-click #(test-func #first-name)} "Click me!"]])
You can grab the element's value like this: (.-value (.getElementById js/document "first-name"))
(defn form []
[:div
[:input {:type "text" :name "first-name" :id "first-name"}]
[:button {:on-click (fn [e] (test-func (.-value (.getElementById js/document "first-name"))))}
"Click me!"]
])
If there is a better answer out there, please share. :)

How to get the dialog functions work

I want to open a dialog, and work with the data after the the dialog returns ok. The problem is that :success-fn is not called after the dialog is submitted. That has something to do with the listener from the button. If the connectDialog is called without listener, the function of :success-fn is called.
Code:
(def dbConnectionForm
(grid-panel :columns 2
:items ["Database Driver" (combobox :id :dbdriver :model ["postgresql" "mysql"])
"Database" (text :id :dbname :text "postgres")
"Port" (text :id :dbport :text "32768")
"Username" (text :id :username :text "postgres")
"Password" (text :id :password :text "postgres")]))
(defn connectionDialog []
(print (-> (dialog
:content dbConnectionForm
:option-type :ok-cancel
:type :plain
:success-fn (fn [e] (print (value dbConnectionForm)))
)pack! show!))
)
(def connectButton (button :text "Connect"
:listen [:action (fn [e] (connectionDialog))]))
This is probably because you're using print. Change it to println or add a call to flush inside the callback after the print.
If (value dbConnectionForm) returns a small value (as in something that when turned into a String has only a few characters), and doesn't contain newlines, it might not prompt the outstream to automatically flush, so the text gets stuck in the buffer.

How to pass new props to state of acomponent in Reagent?

I have a component:
(defn inner-input [cljs_element activeEl title]
(let [form (atom title)]
(fn [cljs_element activeEl title]
[:input {:type "text"
:onChange #(reset! form (.. % -target -value))
:on-blur #(change-title cljs_element (.. % -target -value))
:style {:display (if (:active (:node cljs_element)) "block" "none")
:width (* (+ 1 (count #form)) 8)
:max-width 730
:min-width 170}
:value #form}])))
It is nested in other component:
(defn card-input [cljs_element activeEl]
(fn [cljs_element activeEl]
(let [title (:title (:node cljs_element))]
[:div
[inner-input cljs_element activeEl title]])))
When i type data to the input in the inner-input component, i need update the local state form. And when the outer component card-input updates i want to reset my form to new title prop from argument. How can i achieve that?
I tried put (reset! form title) between let and fn in the inner-input component but it will not help
You can use reagent/track! to listen to changes to title, and reagent/dispose to stop listening. You can alternatively use add-watch and remove-watch, but track is a more convenient syntax.
(defn inner-input [title]
(reagent/with-let
[form (reagent/atom #title)
watch (reagent/track! (fn [] (reset! form #title)))]
[:label
"Inner input"
[:input {:on-change (fn [e]
(reset! form (.. e -target -value)))
:on-blur (fn [e]
(reset! title (.. e -target -value)))
:value #form}]]
(finally
(reagent/dispose! watch))))
(defn card-input []
(reagent/with-let
[title (reagent/atom "hello")]
[:div
[:label "Title"
[:input {:on-change (fn [e]
(reset! title (.. e -target -value)))
:value #title}]]
[inner-input title]]))
Now if you type in the inner input it will only update the outer when you exit the input box, but changing the outer title will immediately change the inner one. Is that what you wanted?
But if you don't want to pass title as a ratom and have to pass it as a value, then instead you can compare it to the previous value to determine if it changed, and reset form only when it changes.
(when (not= #previous-title title)
(do (reset! previous-title title)
(reset! form title)))
This code can go in render seeing as it is safe to call when form changes... nothing will happen.

Parsing values from multiple checkboxes using compojure

I have created small compojure web application, which can display multiple values, fetched from other website, using provided URL. At the moment, this URL is hard coded in one of my functions, and now I would like to add feature for dynamical URL creation, based upon values in text field and checkbox.
This is how my page looks like:
(defn view-layout [& content]
(html [:body content]))
(defn view-input []
(view-layout
[:h2 "Find"]
[:form {:method "post" :action "/"}
( for [category ["Cat1" "Cat2" "Cat3"]]
[:input {:type "checkbox" :id category } category ] )
[:br]
[:input {:type "text" :id "a" :value "insert manga name"}] [:br]
[:input.action {:type "submit" :value "Find"}]
[:a {:href "/downloads"} "Downloads"]]))
(defn view-output []
(view-layout
[:h2 "default images"]
[:form {:method "post" :action "/"}
(for [name (get-content-from-url (create-url))]
[:label name [:br]]
)]))
(defn create-manga-url
[]
"http://www.mysite.net/search/?tfield=&check=000")
Here are the routes:
(defroutes main-routes
(GET "/" []
(view-input))
(GET "/downloads" []
(view-downloads))
(POST "/" []
(view-output) ))
At the moment, I need help with (create-url) function (returns a string), where I would like to fetch all fields, mandatory for my search (one text field and 3 checkboxe's) , and parse values from them, which will be fed into (concatenated) the URL - for checkbox, if checked, the check section will have value 1, instead of 0, or remain 0 if not (check=100, or 010, 011 if two check boxes were selected) . In case of text field, the tfield=userinputtext.
EDIT
I spent a lot of time as a .Net and Java developer, and this part of compojure is a total mystery for me.
This is what I would like to achieve with (create-url) function (pseudo code written in OO style):
(defn create-url [*text_field cbox1 cbox2 cbox3*]
(def url "http://www.mysite.net/search/?")
(def tfield "tfield=")
(def cbox "&check=")
(if (checked? cbox1)
(str cbox "1")
(str cbox "0"))
(if (checked? cbox2)
(str cbox "1")
(str cbox "0"))
(if (checked? cbox3)
(str cbox "1")
(str cbox "0"))
(str tfield (:value text_field))
(str url tbox cbox))
I apologize for how this pseudo code looks like, but this is the part that I would like to learn: How can I scoop data from form, and parse it (in this case i would like to attachh values from form fields into the string)
Can anyone help me with this?
First, you need to add 'name' attributes to your HTML input elements. The 'id' attributes aren't sent to the server on post.
Next, I guess a quick way of doing this similar to your example is:
(POST "/" [a Cat1 Cat2 Cat3] (create-url a [Cat1 Cat2 Cat3]))
and then something like this:
(defn checked? [c]
(and c (= c "on")))
(defn checked->num [c]
(if (checked? c) "1" "0"))
(defn create-url [a cats]
(str "x?tfield=" a "&check="
(apply str (for [c cats] (checked->num c)))))
Or just drop the two helpers:
(defn create-url [a cats]
(str "x?tfield=" a "&check="
(apply str (map #(if (= "on" %) "1" "0") cats))))