Parsing values from multiple checkboxes using compojure

270 views
Skip to first unread message

Stefan

unread,
May 27, 2012, 3:42:55 AM5/27/12
to Compojure
Hi everyone! 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.

Can anyone help me with this?

Stefan

unread,
May 27, 2012, 4:44:51 AM5/27/12
to comp...@googlegroups.com
Hi here is the pseudo code for my function (I apologize if something is messed up, but this would be my best guess how this function should work):

(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))
The thing is, I guess this has something to to with routes, but I am really new to this part of clojure, and I can't find solution for this problem. Please help me with this one.

Regards. 

James Reeves

unread,
May 27, 2012, 5:16:43 AM5/27/12
to comp...@googlegroups.com
Hi Stefan,

The first thing you might want to do is to make use of the helper
functions included with Hiccup. For example:

(use '[hiccup core page form element])

(defn view-layout [& content]
  (html5 [:body content]))

(def all-categories
["Cat1" "Cat2" "Cat3"])

(defn view-input []
 (view-layout
[:h2 "Find"]
(form-to [:post "/"]
(for [category all-categories]
(checkbox "categories[]" false category))
   [:br]
(text-field {:placeholder "insert manga name"} :a) [:br]
(submit-button "Find")
(link-to "/downloads" "Downloads"))))

Note that I've named the checkboxes "category[]", because I want their
values to be placed in a vector. (A current limitation of the
wrap-nested-params middleware that's applied by compojure.handler/site
is that any parameter with multiple values needs to end with a "[]").

When we submit the form, we wind up with a parameter map that looks a
little like:

:params {:a "foobar", :categories ["Cat1" "Cat3"]}

Which shows us that Cat1 and Cat3 were selected. The next problem is
how to turn this into a sequence of true/false values:

(defn category-checks [selected]
(->> all-categories
(map (set selected))
(map #(if % 1 0))
(apply str)))

This might require a little bit of explanation. We start with a list
of all categories, and then we want to know which categories are
selected. If we convert the selected values into a set, then we can
use the set as a function; we get a "truthy" value if the item is in
the set, or a "falsey" value (nil) if it is not.

After that, we can map over an if statement that converts true to 1
and false to 0, and then apply a str to the list of 0s and 1s so we
get our final value. This can then be used to create the URL:

(defn create-manga-url [selected-cats]
(str "http://www.mysite.net/search/?tfield=&check="
(category-checks selected-cats)))

(POST "/" [categories]
(view-output categories))

- James
> --
> You received this message because you are subscribed to the Google Groups "Compojure" group.
> To post to this group, send email to comp...@googlegroups.com.
> To unsubscribe from this group, send email to compojure+...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/compojure?hl=en.
>

Stefan

unread,
May 27, 2012, 7:59:26 AM5/27/12
to comp...@googlegroups.com
Hi James, thank you very much for replying. I have tried your code, and it seems that the  category-checks  function returns nil, no matter if the checkboxes are selected or not .Can you assist me on this a bit further?  Also, can you explain me how to include (concatenate) values from text field as well? 

Regards.

James Reeves

unread,
May 27, 2012, 10:21:42 AM5/27/12
to comp...@googlegroups.com
On 27 May 2012 12:59, Stefan <mitkepre...@gmail.com> wrote:
> Hi James, thank you very much for replying. I have tried your code, and it
> seems that the  category-checks  function returns nil, no matter if the
> checkboxes are selected or not .Can you assist me on this a bit further?

Are you certain? I don't believe there's any circumstances under which
str can return nil.

Could you provide me with more or (if possible) all of your code? It's
hard to judge what the error could be without seeing the code
directly.

> Also, can you explain me how to include (concatenate) values from text field
> as well?

You can use the "concat" function to concatenate two lists, and the
"conj" function to add a single value to a list.

I'd need to know more about your application to explain further. For
instance, does the text field contain just one value, or many, and if
it is the latter, how are the values separated?

- James

Stefan

unread,
May 27, 2012, 10:56:43 AM5/27/12
to comp...@googlegroups.com
Hi, a have attached the files I am using, also I am using Leningen for running my application.  Please help me to pin point the cause of this. Thanks for your help.


On Sunday, May 27, 2012 9:42:55 AM UTC+2, Stefan wrote:
pages.clj
project.clj
run.clj

Stefan

unread,
May 27, 2012, 11:05:15 AM5/27/12
to comp...@googlegroups.com
Hi, a have attached the files I am using, also I am using Leningen for running my application. Text field should contain several words (like name of book), which will be concatenated to URL, similar to values from checkboxes . The functions in these files are slightly different then those I posted earlier, but the essence is the same (feel free to use URL from earlier posts): I just want to input values from text field and checkboxes into URL (a string) .

 Thanks for your help. 

James Reeves

unread,
May 27, 2012, 12:24:10 PM5/27/12
to comp...@googlegroups.com
On 27 May 2012 15:56, Stefan <mitkepre...@gmail.com> wrote:
> Hi, a have attached the files I am using, also I am using Leningen for
> running my application.  Please help me to pin point the cause of this.
> Thanks for your help.

Okay, here are some comments in the order I found them:

1. Don't use clojure-contrib: it's deprecated.

2. I'd suggest using lein-ring over lein-run, so you don't have to
restart the server each time you make a change.

3. The compojure.handler/site function adds a bunch of middleware like
wrap-params usually necessary for a website.

4. The "html" macro doesn't add <html> tags. Use the html5 or xhtml
functions for that.


The reason the code I provided didn't work for you is because I was
assuming you were using compojure.handler/site to wrap your routes in.

Here's the result I came up with. I removed view-output because I
didn't need it for testing:

https://gist.github.com/2814954

"lein ring server" will start a development server.

- James

Stefan

unread,
May 28, 2012, 6:48:34 PM5/28/12
to comp...@googlegroups.com
Hi James, sorry for not replying sooner. I have applied your suggestions, and code works perfectly now !!!! :D 

But I still need to use "lein run", I couldn't get the "lein ring server" command to work. For some reason I am getting error message, stating that system can't find class for hiccup/page_helpers (from hiccup 0.3.8). I used this library when I begin with first tutorials, now I am using hiccup 1.0.0, which doesn't need this special library (I think...), but for some reason, when I execute "lein ring server" , it states that he needs this class in buildpath. This is not the case when I execute "lein run". Can you give me any advice on this one?

Anyway, thanks for all of your help. 

Cheers mate!

fdaoud

unread,
May 28, 2012, 7:39:22 PM5/28/12
to Compojure
Hi Stefan,

I think that upgrading ring to version 1.1.0 in your project.clj file
will solve that problem.

Hope that helps,

Fred

James Reeves

unread,
May 28, 2012, 9:25:13 PM5/28/12
to comp...@googlegroups.com
On 28 May 2012 23:48, Stefan <mitkepre...@gmail.com> wrote:
> But I still need to use "lein run", I couldn't get the "lein ring server"
> command to work. For some reason I am getting error message, stating that
> system can't find class for hiccup/page_helpers (from hiccup 0.3.8). I used
> this library when I begin with first tutorials, now I am using hiccup 1.0.0,
> which doesn't need this special library (I think...), but for some reason,
> when I execute "lein ring server" , it states that he needs this class in
> buildpath. This is not the case when I execute "lein run". Can you give me
> any advice on this one?

Which versions of Ring, Compojure and Lein-Ring are you using? If you
use the latest version for all of them, your dependency problem should
be resolved.

- James

Stefan

unread,
Jun 16, 2012, 11:48:52 AM6/16/12
to comp...@googlegroups.com
Hi James, I am using  the newest release from clojure, ring, compojure, and other projects, I have identified the source of previous problem, but now, I am getting another error message when I execute "lein ring server" command :

Exception in thread "main" java.lang.IllegalArgumentException: Vector arg to map conj must be a pair

        at clojure.lang.APersistentMap.cons(APersistentMap.java:34)
        at clojure.lang.RT.conj(RT.java:551)
        at clojure.core$conj.invoke(core.clj:83)
        at clojure.core$merge$fn__4155.invoke(core.clj:2631)
        at clojure.core$reduce1.invoke(core.clj:880)
        at clojure.core$reduce1.invoke(core.clj:871)
        at clojure.core$merge.doInvoke(core.clj:2631)
        at clojure.lang.RestFn.invoke(RestFn.java:457)
        at ring.server.leiningen$serve.invoke(leiningen.clj:20)
        at user$eval1522.invoke(NO_SOURCE_FILE:1)
        at clojure.lang.Compiler.eval(Compiler.java:6511)
        at clojure.lang.Compiler.eval(Compiler.java:6501)
        at clojure.lang.Compiler.eval(Compiler.java:6477)
        at clojure.core$eval.invoke(core.clj:2797)
        at clojure.main$eval_opt.invoke(main.clj:297)
        at clojure.main$initialize.invoke(main.clj:316)
        at clojure.main$null_opt.invoke(main.clj:349)
        at clojure.main$main.doInvoke(main.clj:427)
        at clojure.lang.RestFn.invoke(RestFn.java:421)
        at clojure.lang.Var.invoke(Var.java:419)
        at clojure.lang.AFn.applyToHelper(AFn.java:163)
        at clojure.lang.Var.applyTo(Var.java:532)
        at clojure.main.main(main.java:37)

I have attached my project.clj file, perhaps problem lies there. This kind of stuff does not happen when command "lein run" is executed, but it would be great thing not to be forced to execute this command whenever I change something.

Regards.
project.clj

James Reeves

unread,
Jun 16, 2012, 1:04:42 PM6/16/12
to comp...@googlegroups.com
On 16 June 2012 16:48, Stefan <mitkepre...@gmail.com> wrote:
> Exception in thread "main" java.lang.IllegalArgumentException: Vector arg to
> map conj must be a pair

You've used the wrong data structure for the :ring options. You've written:

:ring [:handler adder.pages/app]

But it should be:

:ring {:handler adder.pages/app}

- James

Stefan

unread,
Jun 17, 2012, 6:11:09 PM6/17/12
to comp...@googlegroups.com
Worked like a charm, thank you so much (to all of you guy's)  for your help.

Best regards.
Reply all
Reply to author
Forward
0 new messages