clojurescript / hiccup - inline css to set dispatch to a hover action - css

I'm trying to hover a text and change the :display of another text to none, in order to make it disappear. I've used :onMouseOver, but it does not work because it needs this.value I think.
So, any idea for how I can do that? I require inline CSS in this case.
This is what I have:
[:p {:onMouseOver #(rf/dispatch [:events/hover-feedback])} "hello"]
[:p {:style {:display #(rf/subscribe [::subs/hover-feedback])}} "world"]
And in events file:
(rf/reg-event-db
::hover-feedback
(fn [db]
(assoc db :hover-feedback "none")))
In subs file:
(rf/reg-sub ::hover-feedback (fn [db] (get-in db [:hover-feedback]

I think you forgot one colon in the first :p dispatch. Try this:
Views:
[:p {:on-mouse-over #(rf/dispatch [::events/hover-feedback])}
"hello"]
[:p {:style {:display #(rf/subscribe [::subs/hover-feedback])}}
"world"]
Events (without change):
(re-frame/reg-event-db
::hover-feedback
(fn [db]
(assoc db :hover-feedback "none")))
Subs (unnecessary get-in):
(re-frame/reg-sub
::hover-feedback
(fn [db]
(:hover-feedback db)))

Related

Clojurescript Semantic UI React Search custom renderer

I am trying to implement a search in Clojurescript with reagent/re-frame and semantic-ui. Semantic-ui uses a renderer for the suggestions. This renderer defaults to image, price, title, description. As I want to have suggestions on geocoding I want to list addresses. This is the return data I am getting. I basically want to display name, city and postcode in the suggestions.
{:hits
[{:osm_type "W",
:name "Am Pfuhl",
:osm_value "residential",
:city "Berlin",
:postcode "12209",
:state "Berlin",
:osm_key "highway",
:extent [13.322584 52.4205878 13.3258975 52.419743],
:point {:lng 13.3241429, :lat 52.4201622},
:osm_id 103012039,
:country "Deutschland"}
:took 7}
The code I wrote does not show me any results. I tried a lot but I don't know how to look into the component to see if the state of it changes and if it stores results. The subscription does give me back results when I call it directly.
(def search (helper/component "Search"))
(def grid (helper/component "Grid"))
(def grid-row (helper/component "Grid" "Row"))
(defn on-search-change [event props]
(rf/dispatch [:get-geocode (:value (js->clj props :keywordize-keys true))]))
(defn on-result-select [event props]
(rf/dispatch [:geocode-selected]))
(defn get-geocode-results []
#(rf/subscribe [:geocode-results]))
(defn result-renderer [& meh]
(fn [meh]
[:div (str meh)]))
(defn geocode-component []
[:> grid
[:> grid-row
[:> search {:minCharacters 3
:loading (when (:geocode #(rf/subscribe [:loading])) )
:defaultValue "Berlin"
:selectFirstResult true
:onSearchChange on-search-change
:onResultSelect on-result-select
:resultRenderer result-renderer
:results get-geocode-results}]]])
I would very much appreciate some help on:
How do I find out if the component stores the results correctly?
How do I write a renderer that just renders all of the results for debugging?
Thanks and regards! Timo
Edit: solution on https://gist.github.com/TimoKramer/7e93758afb81dcad985fafccc613153a
From the docs it looks like :resultRenderer expects a React Component, and you're giving it a Clojure function. You can convert your hiccup-style components to React components with reagent.core/as-component.
I haven't tested this, but maybe it can be as simple as:
(defn result-renderer [& meh]
(reagent.core/as-component [:div (str meh)]))
I've used a similar strategy with tooltips:
(defn info-icon
([message]
(info-icon {} message))
([options message]
(let [popup (component "Popup")
icon (component "Icon")]
[:> popup
{:trigger (reagent/as-component [:> icon (merge {:name "info"} options)])}
" "
message])))
Where component probably matches your helper/component, and reagent is reagent.core

Clojure map outside scope

I am trying to save data into a collection of some sort, but the program that I have is saving everything into a separate map. I want to make it one map.
(defn readFile []
(map (fn [line] (clojure.string/split line #";"))
(with-open [rdr (reader "C:/Users/Rohil/Desktop/textfile.txt")]
(doseq [[idx line] (map-indexed vector(line-seq rdr))]
(if(.contains line "201609")
(if(not(.contains line "TBA"))
(println(assoc table :code(nth(clojure.string/split line #";")3) :instructor(nth(clojure.string/split line #";")19)))
)
)
)
)
)
)
)
Any help will be appreciated.
Looks like you are adapting to clojure :-) I went to the same process. Hang on, it will be worth it!
First: it is important to realize that map will save the result of the function into a new collection. Like cfrick mentions, println returns nil and assoc does not change a map.
I'm guessing a bit here what you are trying to do: You want to have a collection of dicts, where every dict has two keys, like so:
[
{ :code 1 :instructor "blah"}
{ :code 2 :instructor "boo" }
]
You need these values to come from a file, but you only want to save the lines where the line contains "201609" but not "TBA"
First some general remarks:
You probably want to split this function into smaller parts. One could be the check for lines (contains 201609 but not tba ), another could read the file...
I know it is the title of your question, but most likely there is a better way than to change a global variable. Maybe you could make the function readFile return the table?
try if you can pass in arguments to your function.
I'm not sure what you are trying to do with the line (doseq [[... Please give us more context there. I will ignore it
Here is a possible solution:
(ns test
(:require [clojure.string :as s]
[clojure.java.io :as io]))
(defn line-filter [include exclude line]
(and (not (s/includes? line exclude))
(s/includes? line include)))
(defn process-line [line]
(let [line-parts (s/split line #";")
code (nth line-parts 3)
instructor (nth line-parts 19)]
{:code code :instructor instructor}))
(defn read-file [file-name]
(s/split (slurp (io/resource file-name)) #"\n"))
(defn parse-lines [lines]
(map process-line lines))
(defn read-file-and-parse
"This function will read a file, process the lines, and output a collection of maps"
[filename search-for exclude]
(parse-lines
(filter #(line-filter search-for exclude %)
(read-file filename))))
you could now call this function like this: (read-file-and-parse "test.txt" "201609" "TBA")
If you want to add the result of this function into your table, you can use concat. But again, this will return a new version of your list (with new entries added) and not change the one you defined earlier.
Welcome to functional programming :-)))

Recur not at tail position

How can I use something similiar to recurnot at tail position?
Take a look at my code:
(defn -main [& args]
(println "Hi! Type a file name...")
(defn readFile[])
(let [fileName(read-line)]
(let [rdr (reader fileName)]
(if-not (.exists rdr)
((println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))
(defn list '())
(doseq [line (line-seq rdr)]
(if-not (= "" line)
(concat list '(line)))
(list))))
(defn fileLinesList (readFile))
...
...)
I know I can't use recur here... But I neither know how can I make it in clojure.
I'm a newbie in Clojure and I'm coming from a OOP context. So...
Is there a way to use recursion in this case?
What would be an alternative?
First of all you should not nest your functions definitions in another defn (-main in this case). defn or def always defines symbol bindings at the top level of namespace and they don't nest. If you want to define a locally scoped function you need to use let and fn, e.g.
(let [my-fn (fn [a b] (+ a b))]
(my-fn 1 2))
In your particular case I think it would be easier to split your code into multiple functions. This way it will be more readable.
Prompting for a file name is one piece of your logic.
(defn get-existing-filename []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
filename
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))))
Then you can use it to read a file removing empty lines:
(with-open [input (clojure.java.io/reader (get-existing-filename))]
(->> (line-seq input)
(remove empty?)
(doall)))
For a file with following content:
AAA
BBB
CCC
DDD
it will return
("AAA" "BBB" "CCC" "DDD")
If you really want it as a single function, the following will work:
(defn read-file []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
(with-open [input (clojure.java.io/reader (get-existing-filename))]
(->> (line-seq input)
(remove empty?)
(doall)))
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))))
Finally, this function can be called from -main.
I have also noticed another issue in your sample code:
((println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
if and if-not require a single expression for their then and else branches. If you want to have multiple expressions you need to nest them in do:
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
If you need if or if-not without the else branch then you can use when or when-not macros. Then you don't need to wrap multiple expressions because when/when-not will wrap them for your inside of do.
(when true
(println 1)
(println 2))
is equivalent to
(if true
(do
(println 1)
(println 2)))

Idiomatic way of rendering style info using Clojure Hiccup

I need to build style info within hiccup in order to place an element at a location indicated by the variables "top" and "left". My code looks like so:
(html [:div {:style (str "top" top ";left" left)}
"some text"])
This code is pretty ugly. It would be nicer if hiccup automatically rendered the "style" attribute using standard CSS style rules... Then I could write the following:
(html [:div {:style {:top top :left left}}
"some text"])
Is there already a library that does this? Or, do I need to roll my own solution?
Thank you Clojurians for any pointers!
You could write a function that would do that, and it would even be slightly less typing than the map. For example:
(defn style [& info]
{:style (.trim (apply str (map #(let [[kwd val] %]
(str (name kwd) ":" val "; "))
(apply hash-map info))))})
Which would allow you to write it like this...
(html [:div (style :top top :left left) "some text"])
Sample output from the function...
user=> (style :top 32 :left 14)
{:style "top: 32; left: 14;"}
What about this:
(defn style [s]
(str/join ";" (map #(str (name %) ":" ((keyword %) s)) (keys s))))
(style {:padding "20px"
:background "#e68a00"
:color "white"
:font-size "large"
:font-weight "bold"})
Not much into Clojure yet, but a 'transformation' based approach like that of Enlive sounds like the solution for these kind of needs -
https://github.com/cgrand/enlive

Hunchentoot/cl-who page composition

Hunchentoot/cl-who Page Composition
I'm trying to put together a few pages in hunchentoot as an experiment, and I'm running into an unexpected wall. As an example, I have the following template macro.
(defmacro page-template ((&key title) &body body)
`(with-html-output-to-string
(*standard-output* nil :prologue t :indent t)
(:html :xmlns "http://www.w3.org/1999/xhtml" :xml\:lang "en" :lang "en"
(:head (:meta :http-equiv "Content-Type" :content "text/html;charset=utf-8")
(:title ,(format nil "~#[~A - ~]Test Site" title)))
(:body ,#body))))
Now when I have a pure text page, or one filled with html literals like
(define-easy-handler (test-page :uri "/") ()
(page-template (:title "Splash Page") (:p "Testing testing")))
everything is a-ok. The page outputs properly and I can see te efforts of my code instantly. However, when I have a page which is made up of redundant elements, it's not as simple. For example, lets say I have a page on which for whatever reason I want to display three RSS newsfeeds. This is a sufficiently complex component that I want to abstract it out, so to my minnd, I should be able to do something like
(define-easy-handler (test-feed :uri "/feeds") ()
(page-template (:title "Splash Page")
(publish-newsfeed "http://nf-one.html")
(publish-newsfeed "http://nf-two.html")
(publish-newsfeed "http://nf-three.html")))
(defmacro publish-newsfeed (url &optional (item-limit 5))
(flet ((get-text (s-tree node-path) (car (last (xmls-tools:find-subtree s-tree node-path)))))
(let ((rss-feed (xmls:parse (drakma:http-request url))))
`(:div :class "rss-feed"
(:a :href ,(get-text rss-feed '("channel" "link")) :target "_top" (:h1 ,(get-text rss-feed '("channel" "title"))))
(:ul ,#(mapcar #'(lambda (item)
`(:li (:a :href ,(get-text item '("link")) :target "_top" (:h2 ,(get-text item '("title"))))
(:p :class "date" ,(get-text item '("pubDate")))
(:p ,(get-text item '("description")))))
(let ((items (xmls-tools:find-all-children (xmls-tools:find-subtree rss-feed '("channel")) "item")))
(if (> (length items) item-limit) (subseq items 0 item-limit) items))))))))
But the result of the above is a "Server Error" page. I'm not quire sure why; page-template is a macro so the calls to publish-newsfeed shouldn't be expanded until they're in the context of with-html-output-to-string. Can anyone tell me what I'm doing wrong?
Also, on closer inspection of the various Hunchentoot/cl-who tutorials, none of them seems to do this kind of page composition. Can anyone with some Hunchentoot experience tell me what the correct/canonical way of decomposing a page into components is?
EDIT:
Correct response by Ramarren below; the with-html-output macros work under different evaluation rules. The version of publish-newsfeed that would actually work in this situation is actually
(defun publish-newsfeed (url &optional (item-limit 5))
(flet ((get-text (s-tree node-path) (car (last (xmls-tools:find-subtree s-tree node-path)))))
(let* ((rss-feed (xmls:parse (drakma:http-request url)))
(items (xmls-tools:find-all-children (xmls-tools:find-subtree rss-feed '("channel")) "item"))
(ltd-items (if (> (length items) item-limit) (subseq items 0 item-limit) items)))
(with-html-output
(*standard-output* nil :indent t)
(:div :class "rss-feed"
(:a :href (get-text rss-feed '("channel" "link")) :target "_top" (:h1 (str (get-text rss-feed '("channel" "title")))))
(:ul (dolist (item ltd-items)
(htm (:li (:h2 (:a :href (get-text item '("link")) :target "_top" (str (get-text item '("title")))))
(:p :class "date" (str (get-text item '("pubDate"))))
(:p (str (get-text item '("description")))))))))))))
Note the removal of mapcar for dolist (I'm a Schemer, don't give me too much of a hard time about liking lambdas, but they weren't the right choice here), and the use of htm to escape blocks of html s-exps (h-exps?) that wouldn't otherwise be in context for with-html-output. Finally, I had to wrap text but NOT :href properties in (str ) to get them to expand dynamically.
The macro with-html-output-to-string expands its body using special evaluation rules. In particular, any not recognized forms are left as is, which means macros are not expanded before the html generating code is generated, which means by the time your publish-newsfeed macro is expanded by the standard compiler it is no longer in context ofwith-html-output-to-string. This is clear when expanding the macro manually, especially using slime macroexpansion feature.
To make it work you should make publish-newsfeed a function and use with-html-output inside it with the same stream (either assume `standard-output everywhere or pass the stream explicitly).

Resources