如何有条件地生成打嗝结构?

How to generate Hiccup structures conditionally?

我有一堆描述位置的地图:

 (def pizzeria
  {
   :LocationId "pizzeria"
   :Desc       "Pizzeria where Timur works"
   :Address    "Vorgartenstraße 126, 1020 Wien"
   :Comment    ""
   })

(def dancing-school
  {
   :LocationId "dancing-school"
   :Desc       "Dancing school"
   :Comment    "4th district"
   })

其中有些地图有 :Comment,有些则没有。

我想创建一个 Clojure 函数,除其他外,它会将注释输出到 HTML,如果存在的话。

我写了这个函数:

(defn render-location-details
  [cur-location]
  (let [
        desc (get cur-location :Desc)
        address (get cur-location :Address)
        comment (get cur-location :Comment)
        address-title [:h4 "Address"]
        address-body [:p address]
        comment-hiccup [
                        (if (clojure.string/blank? comment)
                          nil
                          [:div
                           [:h4 "Comment"]
                           [:p comment]
                           ])
                        ]
        ]
    [:h3 desc
     address-title
     address-body
     comment-hiccup
     ]
    )
  )

如果我运行使用这个函数的代码,我得到错误

Execution error (IllegalArgumentException) at
hiccup.compiler/normalize-element (compiler.clj:59).
 is not a valid element name.

如果我将 comment-hiccup 更改为 nil,错误就会消失。

如何使用 Hiccup 有条件地将数据输出到 HTML?

注意:我是 Clojure 的新手,所以如果我的方法完全错误,请告诉我正确的做法。

下面的代码似乎有效。仍然欢迎改进建议。

(defn render-location-details
  [cur-location]
  (let [
        desc (get cur-location :Desc)
        address (get cur-location :Address)
        comment (get cur-location :Comment)
        address-title [:h4 "Address"]
        address-body [:p address]
        comment-hiccup (if (clojure.string/blank? comment)
                          nil
                          [:div
                           [:h4 "Comment"]
                           [:p comment]
                           ])

        ]
    [:div
      [:h3 desc]
      address-title
      address-body
      comment-hiccup
     ]
    )
  )

您的第一次尝试将创建 comment-hiccup 如:

[nil]

其中有效的打嗝总是以关键字标签开头,例如:

[:h3 "hello"]
[:div nil]

所以错误消息基本上是说 nil 不是有效的 html 标签。但是,在 clojure 中 nil 转换为字符串时长度为零,因此错误消息变为 is not a valid element name. 而不是 foo is not a valid element name.

但是,Hiccup 和许多类似的表单将接受 nil 而不是有效的 Hiccup 表达式,并在呈现剩余的有效表单之前将它们过滤掉。此功能的存在正是为了允许使用内联 ifwhen 表单,这些表单会产生有效的打嗝或 nil.

示例代码:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [hiccup.core :as h]))

(dotest
  (is= (h/html [:p "bar"]) "<p>bar</p>")
  (is= (h/html [:p nil]) "<p></p>")
  (is= (h/html nil) "")

  ; if uncommented, this creates an error during macroexpansion of `h/html`
  ; (println :err1 (h/html [nil]))  ; <=== line 12
  ;    `Syntax error macroexpanding h/html at (tst/demo/core.clj:12:18). 
  ;       java.lang.IllegalArgumentException:  is not a valid element name.`

  )

基于my favorite template project

旁注: 像上面这样的神秘错误消息是避免在您自己的代码中不必要地使用宏的有力理由。上面的错误不是在代码执行的时候出现的,而是在代码编译的时候出现的。因此,即使我们用 try/catch.

包裹有问题的行,我们也无法处理错误

P.S.

大多数 clojurists 会将上述代码压缩成更“内联”的样式,例如:

(defn render-location-details
  [cur-location]
  [:div
   [:h3 (:Desc cur-location)]
   [:h4 "Address"]
   [:p (:Address cur-location)]
   (when-not (clojure.string/blank? (:Comment cur-location))
     [:div
      [:h4 "Comment"]
      [:p (:Comment cur-location)]])])