Situation
I am using clojure + figwheel + devcards.
https://www.youtube.com/watch?v=G7Z_g2fnEDg
Everything is great, except for the following problem:
I can prototype UI components of my app. However, I don't want my full app to be inside a card.
So in particular, I want the following:
localhost:8000/cards.html <-- show me all my namespaces + devcards
localhost:8000/app.html <-- don't show me any devcards; don't show me the directory of devcards; just run my app
Question:
How do I get this setup? Almost everything I have read is about how to use devcards not how to setup a separate devcards vs main app distinction.
Thanks!
This is pretty much the default already for the devcards template (e.g. lein new devcards my-app).
Have more than one build in your project.clj. One for the devcards (note the different paths and the figwheel config). The dev is pretty much the default.
(This code is from the template):
; ...
:builds [{:id "devcards"
:source-paths ["src"]
:figwheel { :devcards true ;; <- note this
;; :open-urls will pop open your application
;; in the default browser once Figwheel has
;; started and complied your application.
;; Comment this out once it no longer serves you.
:open-urls ["http://localhost:3449/cards.html"]}
:compiler { :main "xxx.core"
:asset-path "js/compiled/devcards_out"
:output-to "resources/public/js/compiled/xxx_devcards.js"
:output-dir "resources/public/js/compiled/devcards_out"
:source-map-timestamp true }}
{:id "dev"
:source-paths ["src"]
:figwheel true
:compiler {:main "xxx.core"
:asset-path "js/compiled/out"
:output-to "resources/public/js/compiled/xxx.js"
:output-dir "resources/public/js/compiled/out"
:source-map-timestamp true }}
;...
Now you need two different HTML files. One you are already using (the cards.html) and your app.html (or what the template is using: index.html). They load:
<script src="/js/compiled/xxx_devcards.js" type="text/javascript"></script>
The other:
<script src="/js/compiled/xxx.js" type="text/javascript"></script>
Note, that those are the two from :output-to.
Run this setup with lein figwheel dev devcards. Open both the index and the cards in your browser. Enjoy.
In practice it might be even better to separate that a bit more. You can do this, by either using different ns for your :main - or you can use multiple :source-paths.
How I solved this with only one build:
1) In the HTML create a global variable that indicates whether devcards should be loaded:
<script type="text/javascript">
var showDevcards = true; // or false
</script>
2) In your initial ClojureScript namespace check this variable:
(if js/showDevcards
(devcards/init!)
(init!)) ;; what you had previously
Related
I'm trying to put a simple site together to demonstrate a ClojureScript library with a couple of "pages", each of which contains several examples.
I used a leiningen template to make a project with devcards, routing etc.
I now have the default boilerplate :
(defn home-page []
[:div [:h2 "Welcome to site"]
[:div [:a {:href "/about"} "go to about page"]]] )
(defn about-page []
[:div [:h2 "About site"]
[:div [:a {:href "/"} "go to the home page"]]])
I now want to put some devcards into these "pages".
However, just putting a defcard either next to or inside one of these functions doesn't seem to add it to the page.
So can devcards be used in this context? If so, where / how do I put them?
I am using ClojureScript Reagent. Which provides hiccup-like HTML generation.
I have a String with HTML:
(def code "<b>hello world</b>")
When passed to Hiccup it will be escaped and I get no bold text on my page:
[:div code]
How to pass code to my HTML output so it will be integrated there without being escaped?
Reagent
Use the dangerouslysetInnerHTML native React call
[:div {:dangerouslySetInnerHTML {:__html code}}])
Also see:
Issue #14
(real) Hiccup
You need to use the raw-string function from hiccup.utils:
[:div (raw-string code)]
I'm trying to do something simple with Clojure Enlive lib: I want the top menu of my page to be different based on language selected by the user (English or Japanese). I can't find how to implement this basic feature.
I have a "template/header.html" template with 2 sections (one per language):
<div class="header en-US">...</div>
<div class="header ja-JP">...</div>
I created 2 corresponding snippets in my templating code:
(:require [net.cgrand.enlive-html :as html])
...
(html/defsnippet header-ja-JP "templates/header.html" [:div.header.ja-JP] [])
(html/defsnippet header-en-US "templates/header.html" [:div.header.en-US] [])
Now when I load my main template (index.html) I want to populate my header menu code into my nav tag.
Below, the argument 'lang' value is either "en-US" or "ja-JP":
(defn process-template
[lang]
(let [header-selector (str “header-“ lang)
header-content (#(resolve (symbol header-selector)))]
(apply str
(html/emit*
(html/at
(html/html-resource "templates/index.html")
[:nav] (html/content (header-content)))))))
It throws a java.lang.NullPointerException
If I replace the last line by
[:nav] (html/content (header-ja-JP)))))))
or
[:nav] (html/content (header-en-US)))))))
it works fine (so the snippets work), but I've tried lots of different syntaxes and can't make the dynamic header work based on language.
The above code (let part) seems ok if I execute it manually in REPL, the header-content is JSON object containing the correct content so I don't understand why I get a NullPointerException.
Ideally I think I should have only one snippet taking the language as argument and returning the corresponding HTML section, but didn't manage to do that either.
I'm very new at Clojure so I'm probably missing something obvious here; if someone could point me to what's wrong in the code above or another way to make this work that will be awesome.
Thanks,
Olivier
If we want to do it differently as cgrand suggests, there are many, many ways to accomplish this. I have a hard time imagining how he'd do it with maps, but I'd do it this way:
(defn process-template [lang]
(html/select (html/html-snippet
"<html>
<head></head>
<body>
<nav>
<div class=\"en\"></div>
<div class=\"jp\"></div>
</nav>
</body>
</html>")
[(keyword (str "div." lang))]))
(apply str (html/emit* (process-template "en")))
=> "<div class=\"en\"></div>"
Of course read it from file instead of HTML in a string. The function plucks out the matching HTML node according to a selector.
Your (#(resolve (symbol header-selector))) returns a value.
(resolve (symbol header-selector)) returns a function.
[:nav] (html/content (header-content))))))) calls header-content as a function.
Replace (#(resolve (symbol header-selector))) with (resolve (symbol header-selector)).
I'm fuzzy on what html/at and html/html-resource do so if other problems crop up you might need to cram in clojure.java.io/file in there.
I have never done any kind of web programming before so I have no idea what I need to do to get this Clojure app I wrote to run on live server. The url of my page is http://rowdy.msudenver.edu/~jnels124/. Here is my Clojure code
(ns startingclojure.core
(:use (compojure handler[core :only (GET POST defroutes)])
[clojure.pprint])
(:require [net.cgrand.enlive-html :as en]
[ring.util.response :as response]
[ring.adapter.jetty :as jetty]))
(defonce counter (atom 10000))
(defonce urls (atom {}))
( defn shorten
[url]
(let [id (swap! counter inc)
id (Long/toString id 36)]
(swap! urls assoc id url)
id))
(en/deftemplate homepage
(en/xml-resource "homepage.html")
[request]
[:#listing :li]
(en/clone-for [[id url] #urls]
[:a] (comp ;; comp composes any number of functions
(en/content (format "%s : %s" id, url))
(en/set-attr :href (str \/ id)))))
(defn redirect
[id]
(response/redirect (#urls id)))
(defroutes app*;; * ususally means implementation detail or lower level operation
(GET "/" request (homepage request))
(POST "/shorten" request
(let [id (shorten (-> request :params :url))]
(response/redirect "/")))
(GET "/:id" [id] (redirect id)))
(def app (compojure.handler/site app*))
Here is the html
<html>
<head>
<link type="text/css" rel="stylesheet" href="style.css"/>
</head>
<body>
<form method="POST" action="/shorten">
<input type="text" name="url"/>
<input type="submit" value="Shorten!"/>
</form>
<ul id="listing">
<li>
id :url
</li>
</ul>
</body>
</html>
So here are the actual questions I have.
How do I get the Clojure to execute on the server. I have been loading this code in the repel and starting with (def server(jetty/run-jetty #'app {:port 8080 :join? false}))?
Also, what is the correct way to bring this project into view(i.e file structure). The file structure on the server is just
Top level: bin public_html
in public_html is cgi-bin index.html startingclojure(my app, from tutorial)
in index html is what you see when that page is entered and startingclojure has the leiningen file structure.
I am not sure if I have provided the all of the necessary information but I am happy to provide anything you may need to help me get started. Thanks in advance.
Running lein, or even a repl, on a production box is a bad idea.
You should define a -main function that creates and runs your web server process if you execute lein run locally. Then, when you are ready to deploy, run lein uberjar to create a jar containing your entire app and all its dependencies. Once you have that jar file on the server, you can run it via java -jar my-app.jar. You will probably want a script that also sets up all the right java execution variables and the port it should run on etc. (these can be provided on the command line and are passed in as strings to -main).
If the app requires certain files to be unpacked, you will need to extract the jar (or at least that part of the jar) and make sure the app process can find it at runtime. A jar is a zip file, and can be extracted with the unzip command from the command line. If you need to refer to resources that are still inside the jar for reading, you should replace any references to clojure.java.io.file (or native java File objects) with clojure.java.io/resource.
It will probably take a while to work out all these inter-environment differences, but luckily each of these steps can be tested locally as you refine them.
I'm a Rails dev getting my feet wet in Clojure. I'm trying to do something which was very simple with ERB but I can't for the life of me figure it out in enlive.
Say I have a simple layout file for a website in layout.html:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
</body>
</html>
And I have these snippets, for instance, header.html and footer.html and this simple route.
(deftemplate layout "layout.html" [])
(defroutes home-routes
(GET "/" [] layout))
How can I make it so whenever a request goes to "/" it transforms the layout and inserts the header and footer snippets into it?
defsnippet only matches a specific part of your html (which is why it takes a selector as an argument), and transforms it. deftemplate takes the entire html, and transforms it. Also, defsnippet returns a Clojure data structure while deftemplates returns a vector of strings, so a defsnippet is usually used within a deftemplate.
To give you an idea of what the data returned by a snippet (or selector) look like:
(enlive/html-snippet "<div id='foo'><p>Hello there</p></div>")
;=({:tag :div, :attrs {:id "foo"}, :content ({:tag :p, :attrs nil, :content ("Hello there")})})
In your case you want something like:
header.html:
<div id="my-header-root">
...
</div>
Clojure code:
(enlive/defsnippet header "path/to/header.html" [:#my-header-root] []
identity)
(enlive/defsnippet footer "path/to/footer.html" [enlive/root] []
identity)
(enlive/deftemplate layout "layout.html" [header footer]
[:head] (enlive/content header)
[:body] (enlive/append footer))
(defroutes home-routes
(GET "/" [] (layout (header) (footer))
The identity function used in the snippets returns it's argument, which in this case is the data structure selected by the :#my-header-root selector (we don't do any transformation). If you want to include everything in i.e head.html you can use the root selector-step that comes with enlive.
You can view the html generated from a defsnippet using something like this:
(print (apply str (enlive/emit* (my-snippet))))
I also recommend the tutorial: https://github.com/swannodette/enlive-tutorial/
and the one by Brian Marick for some more details of how the defsnippet and deftemplate macros work.
Last tip, you can experiment with selectors and transformations using the sniptest macro that comes with enlive:
(enlive/sniptest "<p>Replace me</p>"
[:p] (enlive/content "Hello world!"))
;= "<p>Hello world!</p>"
There is great answer with examples in the enlive tutorial.
Warning. All source files links seem broken. You need to insert enlive-tutorial/blob/master/ after https://github.com/swannodette/ in all links or just open them directly from tutorial project.