Sorry for the probably stupid question, but I'm new to Common Lisp (I migrated from Racket) and so far I'm having an absolute blast building a web app with Hunchentoot, cl-who, and a few other miscellaneous packages, but I've recently run into an issue I can't solve: I'm trying to loop through a hash and display its values (which are structs) if the hash is not empty. If it is, I want to display an "this is empty" message. However, cl-who is only outputting the HTML that comes after the call. Here is my code:
(tbnl:define-easy-handler (index :uri "/") ()
"Landing page."
(setf (tbnl:content-type*) "text/html")
(with-html-ouptut (*standard-output*)
(:html
(:head (:title "Chattr: Neo-BBS"))
(:body
(:div :id "header"
:style "text-align:center;"
(:h1 "Welcome to Chattr")
(:h3 "Please select the sub-board you would like to chat
on."))
(if (> (hash-table-size *boards*) 0)
(dolist (board (hash-table-values *boards*))
(htm
(:span (html-display board)) (:br)))
(htm
(:b "Sorry, there aren't any boards. Why not create
one?") (:br)))
(:a :href "/new-board" "Create New Board")))))
So in this case, "Create New Board" is showing up, but neither the bold text nor the header are. However, if I move the header after the if, it shows up.
I've struggled with this for upwards of six hours, does anyone have any hints for me? Thanks!
From http://weitz.de/hunchentoot:
Request handlers do their work by modifying the reply object if necessary and by eventually returning the response body in the form of a string or a binary sequence.
The handler should return a string and here the only thing that is emitted is the last value being printed (because write
returns its argument)1. That's why you only see the last element. You need to use with-html-output-to-string
; generally you add a variable out
in the binding, but in most cases that variable is not used (at least not here). You can use *standard-output*
but you have to be careful. Better rebind *standard-output*
in the smallest scope where it is useful.
It is possible to use the stream associated with the current response, if you prefer not building a string first .
As an alternative, they can also call SEND-HEADERS and write directly to a stream.
hash-table-count
You are using hash-table-size
instead of hash-table-count
, which means that if you have an empty table, the size (i.e. the capacity) is positive but you don't show anything because the dolist doesn't do anything. By the way, you can also iterate over hash-tables with maphash
or loop
:
(maphash (lambda (key board)
(declare (ignore key))
(htm ...))
*boards*)
(loop
for board being the hash-values of *boards*
do (htm ...))
1. You don't see any write
statement, but they are emitted by the macro. You can macroexpand
forms yourself, or use Slime to do it quickly.