OM how to add class to html element - clojure

Trying out OM, ReactJS and ClojureScript. I am trying to add a classname to my dom element. Ideally I want something like this:
<div>
<i class="icon-alert">Contact list</i>
</div>
right now: It writes like:
<div>
<i>Contact list</i>
</div>
This is my core.clj
(defn new-view [data owner]
(reify
om/IRender
(render [this]
(dom/i #js {:class "icon-alert"} "Contacts list")
)))
(om/root new-view app-state
{:target (. js/document (getElementById "row1"))})
This is the relevant part from my index.html
<div id="row1">
<h2>Figwheel template</h2>
<p>Checkout your developer console.</p>
</div>
Any ideas of what I am doing wrong?

Playing around with my code, make me realise: I was writing class instead of className. Feel so dumb. It works now. I will let it here so people with the same issues can get the help they need.
(defn new-view [data owner]
(reify
om/IRender
(render [this]
(dom/i #js {:className "icon-alert"} "Contacts list")
)))

Related

Right Click in ClojureScript?

Could anyone tell me how to produce a right-click event handler in Clojure? I am familiar with ":on-click" for simple clicks but not right or double clicks. Can't seem to find any helpful resources online. Thanks!
Frequently in ClojureScript the Google Closure library (Event Handling |  Closure Library | Google Developers) is used instead of raw JS. The events (Closure Library API Documentation - JavaScript) namespace contains the goog.events.EventType enumeration which specifies each individual event type:
(ns test.core
(:require [goog.dom :as dom]
[goog.events :as events]))
(letfn [(menu-listener [event]
(.log js/console (str "contextmenu " (.-button event))))
(click-listener [event]
(let [btn (.-button event)
msg (if (= btn 2) "Right-click" (str "Button " btn))]
(.log js/console msg)))]
(events/listen (dom/getElement "click-target") "contextmenu" menu-listener)
(events/listen (dom/getElement "click-target") "click" click-listener))
;; src/test/core.cljs
.
<!DOCTYPE html>
<html>
<head>
<title>contextmenu</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<p id="click-target">Right click on me</p>
<script src="out/test.js" type="text/javascript"></script>
</body>
</html>
<!-- index.html -->
Observe:
A right-click (button 2) fires the contextmenu listener. The click listener doesn't get to see it (even if there is no contextmenu listener).
A second right-click will dismiss the context menu but neither listener is fired.
Using om I got right click as context-menu event. Button number is 2 for right button:
{:onContextMenu (fn [e]
(prn e (.-button e)))}
or in plain html+cljs:
<div id="btn">Click me</div>
(.addEventListener (.getElementById js/document "btn")
"contextmenu" (fn [e] (prn e (.-button e))))
https://developer.mozilla.org/en/docs/Web/API/MouseEvent

ClojureScript/Om: Rendered HTML is missing attributes

I have a dead simple ClojureScript/Om application. It seems a little broken.
This is the core file:
(ns demo.core
(:require-macros [cljs.core.async.macros :refer [go]])
(:require [goog.events :as events]
[cljs.core.async :as async :refer [>! <! put! chan]]
[om.core :as om :include-macros true]
[om.dom :as dom :include-macros true]
[goog.events.EventType :as EventType]
[clojure.string :as string]))
(defn layout
[app owner]
(reify
om/IRender
(render [_]
(dom/div {:id "some-id"} "Pumpkin"))))
(defn main []
(om/root
layout
{}
{:target (. js/document (getElementById "app"))}))
It renders this HTML:
<div id="app">
<div data-reactid=".0">Pumpkin</div>
</div>
Why doesn't the div have the id #some-id?
You need to use the #js {} reader literal to specify a JS object rather than a plain-old map:
(dom/div #js {:id "some-id"} "Pumpkin")
This is elaborated a bit in the Om Tutorial.

ClojureScript no such namespace

I am new to Clojure and I am trying to implement some ClojureScript functions in my project. I added [org.clojure/clojurescript "0.0-2138"] to my dependencies in project.clj. I tried for example:
(js/alert "Hello from ClojureScript.")
It says No such namespace: js!
What am I doing wrong? Can anyone help?
Here is my complete project.clj:
(defproject MyProject "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"][table "0.4.0"][cljs-uuid "0.0.4"][lein-swank "1.4.5"][midje "1.5.1"]
[mysql/mysql-connector-java "5.1.18"][org.clojure/java.jdbc "0.1.1"]
[ring/ring-jetty-adapter "1.1.6"]
[compojure "1.1.3"]
[hiccup "1.0.2"]
[org.clojure/clojurescript "0.0-2138"]
[domina "1.0.2"]
[prismatic/dommy "0.1.1"]]
:plugins [[lein-cljsbuild "0.3.4"]]
;; cljsbuild options configuration
:cljsbuild {:builds
[{;; CLJS source code path
:source-paths ["src/cljs"]
;; Google Closure (CLS) options configuration
:compiler {;; CLS generated JS script filename
:output-to "resources/public/js/test.js"
;; minimal JS optimization directive
:optimizations :whitespace
;; generated JS code prettyfication
:pretty-print true}}]}
)
This is the file where I am trying to put the ClojureScript function. I am using Eclipse and Clojure REPL.
(ns webapp.pages
(:use [compojure.core :only (defroutes GET)]
[ring.adapter.jetty :as ring])
(:require [clojure.java.jdbc :as sql])
(:require
[ring.util.response :as resp])
(:use [hiccup.page :only (html5 include-css include-js)])
(:use webapp.db)
)
(defn showAlert []
(js/alert "Hello from ClojureScript.")
)
(defn insert-user [firstname lastname email password sex date]
(sql/with-connection db
(sql/insert-values :users
[:name :lastName :email :password :birthDate :gender]
[firstname lastname email password date sex])
)
(resp/redirect "/")
(showAlert)
)
(defn index []
(html5
[:head
[:title "Kladionica"]
[:meta {:name "viewport" :content "width=device-width, initial-scale=1"}]
(include-css "/public/css/bootstrap.min.css")
]
[:body
[:div {:class "modal fade" :id "myModal" :role "dialog" :aria-hidden "true"}
[:div {:class "modal-dialog"}
[:div {:class "modal-content"}
[:div {:class "modal-header"}
[:button {:type "button" :class "close" :data-dismiss "modal"}"x"]
[:h3 "Registracija"]
]
[:div {:class "modal-body"}
[:form {:action "/insert-user" :method "post" :class "form" :role "form"}
[:div {:class "row"}
[:div {:class "col-xs-6 col-md-6"}
[:input {:class "form-control" :style "margin-bottom: 10px;" :name "firstname" :placeholder "Ime" :type "text"}]
]
[:div {:class "col-xs-6 col-md-6"}
[:input {:class "form-control" :style "margin-bottom: 10px;" :name "lastname" :placeholder "Prezime" :type "text"}]
]
]
[:input {:class "form-control" :style "margin-bottom: 10px;" :name "email" :placeholder "Email" :type "email"}]
[:input {:class "form-control" :style "margin-bottom: 10px;" :name "password" :placeholder "Lozinka" :type "password"}]
[:input {:class "form-control" :style "margin-bottom: 10px;" :name "re-password" :placeholder "Ponovljena lozinka" :type "password"}]
[:label {:for ""} "Datum rodjenja"]
[:div {:class "row"}
[:div {:class "col-xs-4 col-md-4"}
[:select {:class "form-control" :name "month"}
[:option {:value "" :disabled "disabled" :selected "selected"} "Mjesec"]
[:option {:value "01"} "Januar"]
[:option {:value "02"} "Februar"]
[:option {:value "03"} "Mart"]
[:option {:value "04"} "April"]
[:option {:value "05"} "Maj"]
[:option {:value "06"} "Jun"]
[:option {:value "07"} "Jul"]
[:option {:value "08"} "Avgust"]
[:option {:value "09"} "Septembar"]
[:option {:value "10"} "Oktobar"]
[:option {:value "11"} "Novembar"]
[:option {:value "12"} "Decembar"]
]
]
[:div {:class "col-xs-4 col-md-4"}
[:select {:class "form-control" :name "day"}
[:option {:value "Day" :disabled "disabled" :selected "selected"} "Dan"]
(for [i (range 1 32)]
[:option {:value i} i]
(inc i))
]
]
[:div {:class "col-xs-4 col-md-4"}
[:select {:class "form-control" :name "year"}
[:option {:value "Year"} "Godina"]
(for [i (range 1900 2015)]
[:option {:value i} i]
(inc i))
]
]
]
[:label {:class "radio-inline"}
[:input {:type "radio" :name "sex" :id "maleId" :value "male"}]
"Musko"
]
[:label {:class "radio-inline"}
[:input {:type "radio" :name "sex" :id "femaleId" :value "female"}]
"Zensko"
]
[:br]
[:br]
[:button {:class "btn btn-lg btn-primary btn-block" :type "submit"} "Registruj se"]
]
]
[:div {:class "modal-footer"}
]
]
]
]
[:div {:class "navbar navbar-inverse navbar-static-top"}
[:div {:class "container"}
[:a {:href "#" :class "navbar-brand"} "TOP Kladionica"]
[:div {:class "navbar-collapse collapse"}
[:ul {:class "nav navbar-nav navbar-right"}
[:li [:a {:href "#myModal" :data-toggle "modal"} "Registracija"]]
[:li {:class "dropdown"}
[:a {:href "#" :class "dropdown-toggle" :data-toggle "dropdown" } "Login" [:b {:class "caret"}]]
[:ul {:class "dropdown-menu" :style "padding:15px;min-width:250px;"}
[:li
[:div{:class "row"}
[:div {:class "col-md-12"}
[:form {:class "form" :role "form" :method "post" :action "login" :accept-charset "UTF-8" :id "login-nav"}
[:div {:class "form-group"}
[:label {:class "sr-only" :for "inputEmail"} "Email adresa"]
[:input {:type "email" :class "form-control" :id "inputEmail" :placeholder "Email adresa" }]
]
[:div {:class "form-group"}
[:label {:class "sr-only" :for "inputPassword"} "Lozinka"]
[:input {:type "password" :class "form-control" :id "inputPassword" :placeholder "Lozinka" }]
]
[:div {:class "checkbox"}
[:label
[:input {:type "checkbox"} "Zapamti me"]
]
]
[:div {:class "form-group"}
[:button {:type "submit" :class "btn btn-success btn-block"} "Login"]
]
]
]
]
]
[:li {:class "divider"}]
[:li
[:input {:class "btn btn-primary btn-block" :type "button" :id "sign-in-google" :value "Google prijava"}]
[:input {:class "btn btn-primary btn-block" :type "button" :id "sign-in-twitter" :value "Twitter prijava"}]
]
]
]
]
]
]
]
[:div {:class "navbar navbar-default navbar-fixed-bottom"}
[:div {:class "alert alert-success alert-dismissable pull-right" :style "display:none"}
[:button {:type "button" :class "close" :data-dismiss "alert" :aria-hidden "true"} "x"]
[:strong "Uspijeh!"] "Uspjesno ste se registrovali!"
]
]
(include-js "//code.jquery.com/jquery-1.10.2.min.js")
(include-js "/public/js/bootstrap.js")
]
)
)
A couple of things here.
First of all, ClojureScript is a separate (but related) language with its own compiler, so you can't just put ClojureScript code in a Clojure file.
Second, ClojureScript needs to be compiled into JavaScript in order to do anything, and then that JavaScript needs to be executed by a JavaScript engine. You can do the compiling with the lein-cljsbuild plugin.
For a quick and easy introduction to ClojureScript, take a look at David Nolen's The Essence of ClojureScript. It will get you up and running in literally seconds, and you can take a look around the generated project.clj file to see how a project can be set up for ClojureScript if you're curious.
However, if you're very new to Clojure, it might be better to get comfortable with Clojure and Leiningen before jumping into ClojureScript. You don't have to, but it might be easier.
As other users are commenting here, you have to separate ClojureScript code from Clojure code (if you use cljsbuild to build cljs code the standard way is separate directories).
Why don't you use Clojure and ClojureScript in the same file?
This is not the exact question, as you should feel the ClojureScript code as Clojure code when typing it. The problem is that when you try to use an external Clojure library (not the core Clojure) in your ClojureScript code, then such external library uses the host JVM classes, while the ClojureScript one uses the host Javascript classes. That's the reason you have to use ClojureScript libraries (libraries adapted to JavaScript classes) inside ClojureScript code, and that's the reason your file is a bit messy.

How to generate a list of a pair of elements in Enlive?

I'm new to Enlive. I found that I can iterate with clone-for, however, it works for single element. I want to generate a list of a pair of elements like the following:
<div>
item 1<br>
item 2<br>
...
</div>
I tried to select <a> and use clone-for, but end with following result:
<div>
item 1item 2......<br>
</div>
What do I do to repeat <a> with <br> in each iteration?
I think fragments will work in this case.
Try something along these lines:
(html/sniptest "<div>Label<br/></div>" {[:a] [:br]}
(clone-for [{label :label url :url} [{:label "Google" :url "http://www.google.com" }
{:label "Stack Overflow" :url "http://www.stackoverflow.com"}]]
[:a] (do-> (content label)
(set-attr :href url)))))
;; =>
<div>
Google<br />
Stack Overflow<br />
</div>
If you always want the full content of the div to be cloned (not just the fragment :a -> :br) then you can use first-child and last-child. Just change the {[:a] [:br]} selector above to {[:div first-child] [:div last-child]}.

enlive remove html tag

I have this html snippet. I want parse that snippet and emit html without javascript tags
<html>
<body>
<div class="content">lorem ipsum</div>
<script src="/js/jquery.js" type="text/javascript"></script>
<script src="/js/bootstrap.min.js" type="text/javascript"></script>
</body>
</html>
become this
<html>
<body>
<div class="content">lorem ipsum</div>
</body>
</html>
I couldn't found enlive helper function to remove tags.
I have found the solution thanks for the example. So I write this code and the js disappeared
(html/deftemplate template-about "../resources/public/build/about/index.html"
[]
[:script] (fn js-clear [& args] nil)
)
My usual approach when I need to conditionally remove some tags of the rendered page is to use a nil returning function.
For instance
(html/defsnippet upgrade-plan "page_templates/upgrade-plan.html" [:#upgradePlanSection]
[pending-invoice ... ]
...
[:#delayedPlanWarning] #(when pending-invoice
(html/at %
[:#delayedPlanWarning] (html/remove-attr :style)
[:.messagesText] (html/html-content (tower/t :plans/pending-invoice
(:id pending-invoice)))))
...
In that particular case if pending-invoice is nil the delayedPlanWarning element is removed from the rendered html since the function returns nil.
If you don't mind the extra parsing and emitting, the following would do nicely:
(def orig (html-resource (java.io.StringReader. "<html>
<body>
<div class=\"content\">lorem ipsum</div>
<script src=\"/js/jquery.js\" type=\"text/javascript\"></script>
<script src=\"/js/bootstrap.min.js\" type=\"text/javascript\"></script>
</body>
</html>")))
(def stripped (transform orig [(keyword "script")] (substitute "")))
(apply str (emit* stripped))
When removing whole tags, you only need to use nil as your transformation.
(deftemplate tester1
(java.io.StringReader. "<html><body><div class=\"content\">...")
[]
[:script] nil)
(template1)
In your case, you would replace the StringReader with the resource you are using.