I am new to Clojure and I am trying to make a page where you can see all the news that are in a table on the left, and only sports news on the right of the page. I tried to add a new parameter to the clostache/render:
(defn render-template [template-file params param]
(clostache/render (read-template template-file) params param))
(defn welcome []
(render-template "index" {:sports (model/justSports)} {:news (model/all)}))
where the model/all and model/justSports are:
(defn all []
(j/query mysql-db
(s/select * :news)))
(defn justSports []
(j/query mysql-db
(s/select * :news ["genre = ?" "sports"])))
and the news should be shown like this:
<div style="background-color: #D3D3D3; width: 450px; height: 800px; position: absolute; right: 10px; margin-top: 10px; border-radius: 25px;">
<sections>
{{#sports}}
<h2>{{title}}</h2>
<p>{{text}}<p>
{{/sports}}
</sections>
</div>
<div class="container" style="width: 500px; height: 800px; position: absolute; left: 20px;">
<h1>Listing Posts</h1>
<sections>
{{#news}}
<h2>{{title}}</h2>
<p>{{text}}<p>
{{/news}}
</sections>
</div>
But it doesn't work. It just shows the data from the first parameter on the page. What do you think, how can I make this work?
P.S.
Don't mind the ugly css, I will work on that :)
The following should make it work:
(defn render-template [template-file params]
(clostache/render (read-template template-file) params))
(defn welcome []
(render-template "index" {:sports (model/justSports)
:news (model/all)}))
render has three "arities":
(defn render
"Renders the template with the data and, if supplied, partials."
([template]
(render template {} {}))
([template data]
(render template data {}))
([template data partials]
(replace-all (render-template template data partials)
[["\\\\\\{\\\\\\{" "{{"]
["\\\\\\}\\\\\\}" "}}"]])))
You were calling the 3-arity overload which takes [template data partials], so the second map with the :news key was being taken as the partials by clostache. You want to call the 2-arity version which takes just [template data], passing one map with keys :news & :sports.
Related
I would like to traverse and collect the node with the data-table attribute, extract its value, then obtain its child with the data-field or additional attributes, and extract its value which would be saved as a list.
From the Html example below, I have set up anchor points of dom-attributes in a dom-tree, which is intended to be converted into a model structure after traversing and extracting them.
<body>
<div class="wrap" data-table="page"> Sample Text <p data-field="heading" class="format" >Welcome to this page</p>
<div class="flex-grid generic-card">
<h1 class="card " data-field="intro">Text </h1>
<div class="card " data-field="body"></div>
</div>
</div>
I am expecting the final result to be in a form of a flat list with something similar to (page . ("title" "intro" "body"))
with the following code, I'm able to traverse the node and extract 'data-table' but the problem is, I'm not able to extract data-field attached to data-table.
I unsuccessfully tried to use the recursion approach which consists of repeating the example of 'dom-struct' and dom-search function.
what I noticed is libxml-parse-html-region'' returns empty strings with newlines alongside the dom-nodes after parsing through the dom tree which generates an error.
This code's purpose is to extract the node from the tree recursively
(require 'dom)
(defun dom-struct (x)
(print (dom-attr x 'data-table)) ; extract the data-table attribute
(print (dom-tag (dom-node x))) ;extract dom-tag
(print (dom-children (dom-node x))) ; extract dom-children attached to a node but don't know how to extract data-field attribute
(print (dom-search (dom-children (dom-node x)) (lambda (node) (assq 'data-attribute (cadr node)))))
(mapconcat #'dom-struct (dom-children (dom-node x)) ""))
(defun macro-structify (tag-entries)
(with-temp-buffer
(insert tag-entries)
(let* ((mytags (libxml-parse-html-region (point-min) (point-max))))
(dom-struct (car (dom-by-tag mytags 'body))))))
(let ((myskel "<html>
<head>
<title>Demo: Gradient Slide</title>
</head>
<link href=\"https://fonts.googleapis.com/css?family=Nunito+Sans\" rel=\"stylesheet\">
<link rel=\"stylesheet\" href=\"dist/build.css\">
<body data-table=\"layout\">
<header data-field=\"title\">
<h1>Skeleton Screen</h1>
</header>
<div class=\"wrap\" data-table=\"page\"> Sample Text <p data-field=\"heading\" class=\"format\" data-attribute=\"somethingsomething\">Welcome to this page</p>
<div class=\"flex-grid generic-card\">
<div class=\"card loading\" data-field=\"intro\">Text </div>
<div class=\"card loading\" data-field=\"body\"></div>
</div>
</div>
</body>
</html>"))
(macro-structify myskel))
Here's a solution using esxml-query from the esxml package. It looks for all nodes with a data-field attribute that are children of a div node with a data-table attribute, then collects their attribute values into a list.
(require 'dom)
(require 'esxml-query)
(let* ((myskel "<html>
<head>
<title>Demo: Gradient Slide</title>
</head>
<link href=\"https://fonts.googleapis.com/css?family=Nunito+Sans\" rel=\"stylesheet\">
<link rel=\"stylesheet\" href=\"dist/build.css\">
<body data-table=\"layout\">
<header data-field=\"title\">
<h1>Skeleton Screen</h1>
</header>
<div class=\"wrap\" data-table=\"page\"> Sample Text <p data-field=\"heading\" class=\"format\" data-attribute=\"somethingsomething\">Welcome to this page</p>
<div class=\"flex-grid generic-card\">
<div class=\"card loading\" data-field=\"intro\">Text </div>
<div class=\"card loading\" data-field=\"body\"></div>
</div>
</div>
</body>
</html>")
(dom (with-temp-buffer
(insert myskel)
(libxml-parse-html-region (point-min) (point-max))))
(table-node (esxml-query "div[data-table]" dom))
(model-nodes (esxml-query-all "[data-field]" table-node))
(model-data-table (dom-attr table-node 'data-table))
(model-data-fields (mapcar (lambda (node) (dom-attr node 'data-field)) model-nodes)))
(cons model-data-table model-data-fields))
;; => ("page" "heading" "intro" "body")
The result is different from what you've specified for several reasons:
The whole HTML snippet contains a body tag with a data-table attribute before a div tag with a data-table attribute, but your HTML fragment looks at the latter, so I've changed the code to look for a div tag with a data-table attribute
There is a header tag with a data-field attribute set to "title" (the expected field), but it's part of the body tag with the data-table attribute set to "layout", not the div tag with the data-table attribute set to "page" (the actual field)
The remaining fields are as expected, but printed differently than specified, because in many Lisp languages, (foo . (bar baz)) is identical to (foo bar baz) and usually printed in the latter form
I have a html page, with one structure that I want to turn into Clojure data structure. I’m hitting a mental block on how to approach this in an idiomatic way
This is the structure I have:
<div class=“group”>
<h2>title1<h2>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading1</h3>
<a href=“path1” />
</div>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading2</h3>
<a href=“path2” />
</div>
</div>
<div class=“group”>
<h2>title2<h2>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading3</h3>
<a href=“path3” />
</div>
</div>
Structure I want:
'(
[“Title1” “subhead1” “path1”]
[“Title1” “subhead2” “path2”]
[“Title2” “subhead3” “path3”]
[“Title3” “subhead4” “path4”]
[“Title3” “subhead5” “path5”]
[“Title3” “subhead6” “path6”]
)
The repetition of titles is intentional.
I’ve read David Nolan’s enlive tutorial. That offers a good solution if there was a parity between group and subgroup, but in this case it can be random.
Thanks for any advice.
You can use Hickory for parsing, and then Clojure has some very nice tools for transforming the parsed HTML to the form you want:
(require '[hickory.core :as html])
(defn classifier [tag klass]
(comp #{[:element tag klass]} (juxt :type :tag (comp :class :attrs))))
(def group? (classifier :div "“group”"))
(def subgroup? (classifier :div "“subgroup”"))
(def path? (classifier :a nil))
(defn identifier? [tag] (classifier tag nil))
(defn only [x]
;; https://stackoverflow.com/a/14792289/5044950
{:pre [(seq x)
(nil? (next x))]}
(first x))
(defn identifier [tag element]
(->> element :content (filter (identifier? tag)) only :content only))
(defn process [data]
(for [group (filter group? (map html/as-hickory (html/parse-fragment data)))
:let [title (identifier :h2 group)]
subgroup (filter subgroup? (:content group))
:let [subheading (identifier :h3 subgroup)]
path (filter path? (:content subgroup))]
[title subheading (:href (:attrs path))]))
Example:
(require '[clojure.pprint :as pprint])
(def data
"<div class=“group”>
<h2>title1</h2>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading1</h3>
<a href=“path1” />
</div>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading2</h3>
<a href=“path2” />
</div>
</div>
<div class=“group”>
<h2>title2</h2>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading3</h3>
<a href=“path3” />
</div>
</div>")
(pprint/pprint (process data))
;; (["title1" "subheading1" "“path1”"]
;; ["title1" "subheading2" "“path2”"]
;; ["title2" "subheading3" "“path3”"])
The solution can be splited in two parts
Parsing: parse it with clojure html parser or any other parser.
Custom data structure: modify the parsed html, you can use clojure.walk for that if you want.
You can solve this problem with the tupelo.forest library. Here is an annotated unit test showing the approach. You can find more information in the API docs and both the unit tests and the example demos. Additional documentation is forthcoming.
(dotest
(with-forest (new-forest)
(let [html-str "<div class=“group”>
<h2>title1</h2>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading1</h3>
<a href=“path1” />
</div>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading2</h3>
<a href=“path2” />
</div>
</div>
<div class=“group”>
<h2>title2</h2>
<div class=“subgroup”>
<p>unused</p>
<h3>subheading3</h3>
<a href=“path3” />
</div>
</div>"
enlive-tree (->> html-str
java.io.StringReader.
en-html/html-resource
first)
root-hid (add-tree-enlive enlive-tree)
tree-1 (hid->hiccup root-hid)
; Removing whitespace nodes is optional; just done to keep things neat
blank-leaf-hid? (fn fn-blank-leaf-hid? ; whitespace pred fn
[hid]
(let [node (hid->node hid)]
(and (contains-key? node ::tf/value)
(ts/whitespace? (grab ::tf/value node)))))
blank-leaf-hids (keep-if blank-leaf-hid? (all-leaf-hids)) ; find whitespace nodes
>> (apply remove-hid blank-leaf-hids) ; delete whitespace nodes found
tree-2 (hid->hiccup root-hid)
>> (is= tree-2 [:html
[:body
[:div {:class "“group”"}
[:h2 "title1"]
[:div {:class "“subgroup”"}
[:p "unused"]
[:h3 "subheading1"]
[:a {:href "“path1”"}]]
[:div {:class "“subgroup”"}
[:p "unused"]
[:h3 "subheading2"]
[:a {:href "“path2”"}]]]
[:div {:class "“group”"}
[:h2 "title2"]
[:div {:class "“subgroup”"}
[:p "unused"]
[:h3 "subheading3"]
[:a {:href "“path3”"}]]]]])
; find consectutive nested [:div :h2] pairs at any depth in the tree
div-h2-paths (find-paths root-hid [:** :div :h2])
>> (is= (format-paths div-h2-paths)
[[{:tag :html}
[{:tag :body}
[{:class "“group”", :tag :div}
[{:tag :h2, :tupelo.forest/value "title1"}]]]]
[{:tag :html}
[{:tag :body}
[{:class "“group”", :tag :div}
[{:tag :h2, :tupelo.forest/value "title2"}]]]]])
; find the hid for each top-level :div (i.e. "group"); the next-to-last (-2) hid in each vector
div-hids (mapv #(idx % -2) div-h2-paths)
; for each of div-hids, find and collect nested :h3 values
dif-h3-paths (vec
(lazy-gen
(doseq [div-hid div-hids]
(let [h2-value (find-leaf-value div-hid [:div :h2])
h3-paths (find-paths div-hid [:** :h3])
h3-values (it-> h3-paths (mapv last it) (mapv hid->value it))]
(doseq [h3-value h3-values]
(yield [h2-value h3-value]))))))
]
(is= dif-h3-paths
[["title1" "subheading1"]
["title1" "subheading2"]
["title2" "subheading3"]])
)))
I have a html page that contains a navigation bar at the top of the screen. In the navigation bar, I have a search box and what I want it to do is, you type in this box, hit enter and the results are displayed as a dropdown menu
<li><input type="text" id="search-bar" placeholder="Search"></li>
This is the html search input box. I have given it an id search-bar to eventually create the dropdown menu in ClojureScript
(when-let [section (. js/document (getElementById "search-bar"))]
(r/render-component [search-bar-component] section))
Currently I have a search-form that looks like the following
(defn search-form
[]
[:div
[:p "What are you searching for? "
[:input
{:type :text
:name :search
:on-change #(do
(swap! fields assoc :search (-> % .-target .-value))
(search-index (:search #fields)))
:value (:search #fields)}]]
[:p (create-links #search-results)]])
(defn- search-component
[]
[search-form])
This is my search-component.
What I want to happen is when you type in the input box on the navbar (say "hello", it calls search-index from the search-form with the parameter being the value you type in ("hello") and then returns the results as a dropdown menu below.
search-form works right now as a form on a html page, where you input some text into a form and then the results are displayed below. I want to change it to be on the navbar instead of as a separate page, where the input form is on the navbar and the results are displayed below
How would I have to change my search-form in order to do this?
I think I can do something along the lines of this
(defn search-bar-form
[]
[:div
[:input
{:type :text
:name :search
:on-change #(do
(swap! fields assoc :search (-> % .-target .-value))
(search-index (:search #fields)))
:value (:search #fields)}]
[:p (create-links #search-results)]])
(defn- search-bar-component
[]
[search-form])
Any help would be much appreciated.
re-com provides a typeahead component. It looks like you're using reagent, so you could just use that, otherwise you can use it as inspiration.
I can't seem to figure out why my ruby .each enumerable is churning out an empty div if my array of objects is empty, or adding an empty div at the bottom if there is one object in my #posts variable.
Here is my index.html.erb page:
<div id="post_feed">
<%if #posts.any?%>
<%#posts.each do |p|%>
<div class="post_text_box">
<%=p.body%>
</div>
<%end%>
<%end%>
</div>
Post Controller:
def index
#posts = current_user.posts
#new_post = current_user.posts.new
end
CSS:
#post_feed{
margin-right:auto;
margin-left:auto;
width:400px;
}
.post_text_box{
padding:10px;
margin:10px;
background-color:#FFFFFF;
}
rails console shows 1 item.
irb(main):014:0> Post.count
(1.3ms) SELECT COUNT(*) FROM "posts"
=> 1
Here is an image of the empty div.
I figured it out. In my controller, I am creating an new object but not saving it. My .each iterator is recognizing it as an object in my #posts object array even though its not saved.
I fixed it by check if the record was new using the new_record? method.
<div id="post_feed">
<%if #posts.any?%>
<%#posts.each do |p|%>
<%if ! p.new_record?%>
<div class="post_text_box">
<%=p.body%>
</div>
<%end%>
<%end%>
<%end%>
</div>
Even though it hasn't been saved yet, Rails is considering the #new_post to be part of current_user.posts, so you have a blank post at the end of your #posts list.
It doesn't turn up in the database query because it hasn't been saved.
Depending on what you need to do, you could make #new_post just an empty post (#new_post = Post.new) and assign the user when saving.
Or in your each loop you you can check to see if the post has a body before creating the div if you can rely on that check to give you the results you want:
<div id="post_feed">
<%#posts.each do |p|%>
<% if p.body %>
<div class="post_text_box">
<%=p.body%>
</div>
<%end%>
<%end%>
</div>
You don't need the if #posts.any? check as it will always evaluate to true because of the new post created with #new_post = current_user.posts.new.
And generally in ruby, you won't need to check if an array is empty before running an each loop because the each loop won't do anything (or throw an error) with an empty array.
I was trying to create an option to switch between a list view and widget view in ASP.net MVC (with razor view engine).
However, I am having some trouble trying to both add an image, as well as scale it to the 'correct height' (the same height as the next next to it).
I was looking to create something like:
Desired Result:
---------------------------------------------------------------------------------
[≡] List View | [+] Widget View
---------------------------------------------------------------------------------
where the [≡] and [+] were actually small icon images.
Attempts so far include:
The action link was something like:
#Html.ActionLink("List View", "listView",
new { #style = "background-image:url('~/Content/Images/listIcon.png')" },null)
which only displayed the text.
I also tried creating the actionlink like:
<img src="~/Content/Images/listIcon.png" />#Html.ActionLink("List View", "Index")
but this resolved in
a) the image wasn't part of the link; and
b) the image was almost twice the size of the text (similar to diagram below)
_ _ _ _
| | | |
| icon | | icon |
|_ _| List View | |_ _| Widget View
I wouldn't even mind trying to create it like:
Alternative:
---------------------------------------------------------------------------------
_ _ _ _
| | | |
| icon | List View | | icon | Widget View
|_ _| |_ _|
if I had to.
Would anyone know/advise how to solve/create this?
You can use Url.Action for hyperlink and Url.Content for image source.
Then you can style the appearance with CSS.
<ul class="links">
<li>
<a href="#Url.Action("ListView", "Home")" title="List View" class="links">
<img alt="List View" src="#Url.Content("~/Images/ListView.png")">
List View
</a>
</li>
<li>
<a href="#Url.Action("WidgetView", "Home")" title="Widget View" class="links">
<img alt="Widget View" src="#Url.Content("~/Images/WidgetView.png")">
Widget View
</a>
</li>
</ul>
<style type="text/css">
ul.links {
list-style-type: none;
}
ul.links li {
display: inline-block;
border-left: 1px solid black;
padding-left: 10px;
margin-left: 10px;
}
ul.links li:first-child {
border-left: 0;
padding-left: 0;
margin-left: 0;
}
</style>
You need to create the anchor by hand, and insted of using the #Html.ActinLink helper... you can use the #Url.Action helper
<a href="#Url.Action("YourAction", "YourController", null)">
<img src="yourImageSource" style="vertical-align: middle; width: 30px;"> List View
<a/> |
<a href="#Url.Action("YourAction", "YourController", null)">
<img src="yourImageSource" style="vertical-align: middle; width: 30px;"> Grid View
<a/>
The size of the image can be modified via CSS.
The Url.Action gives you the "link to your action".
The ActionLink, creates an anchor with the link to the action.
The reason this code did not work:
#Html.ActionLink("List View", "listView", new { #style = "background-image:url('~/Content/Images/listIcon.png')" },null)
was because the 3rd parameter of #Html.ActionLink is to add additional route values. If you want to add more HTML attributes, you need to use:
#Html.ActionLink("List View", "listView", null, new { #style = "background-image:url('~/Content/Images/listIcon.png')" })
Additionally, like others have said, you can't use the ~.
Note that inline styles are generally frowned upon, as the best practice would be to create a CSS class that contains your background-image and add the class as the HTML attribute instead, but #style would functionally work here as well. More info on why inline styles are bad can be found here: What's so bad about in-line CSS?
Try this:
Html.ActionLink(" ", "Edit", new {id = c.ID}, new { #style = "background:url('../../Images/Menu/edit.png') no-repeat center right; display:block; height: 30px; width: 50px" }