In an effort to learn ClojureScript, I’m wanting to do quick/simple examples I’ve either done before or that are used by other languages. The first one I’m trying is one of Elm’s example of a simple counter.
I’m using only shadow-cljs and hiccups. I’d like to do this as minimally as possible for learning purposes. Using Reagent would obviously make this trivial, but I’d rather not.
If you see an error on load, just press Ctrl+Enter. Not sure why, but Klipse seems to need to double compile the defmulti for it to work.
I didn’t even use hiccup, so I’m doing string templating to create the HTML by using cl-format. So this is a no dependency implementation. It only uses the ClojureScript standard libs.
The thing about the ELM example, is that beginnerProgram is a frontend framework in a way. It does a few things for you under the hood. So I needed to re-create a similar framework, thats my msg and -redraw functions. With those in place, you can build the counter in the same way you would in ELM.
So model must be an atom.
You create new update defmethods for every messages your app will have. Like in ELM, update takes a message, the model (already unwrapped from the atom).
And your view takes the model (already unwrapped once again), and returns HTML. Here I’ve used cl-format, but you can replace this with hiccup if you prefer.
Similarly to ELM, you can bind events in the HTML, and have it send a msg to your update function, so it can take actions on the model and redraw the html.
In this case, you can also pass arguments to your commands, like my +10 and -10 buttons do. Technically, I didn’t need :increment and :decrement commands, but I wanted to show how the arguments are optional.
This was great! Thank you so much. I was foreseeing the problem with implementing a beginnerProgram, so this helped me out immensely. I got your suggestion working with what I was trying (with hiccups and everything), and it works great. Again, thank you so much!
I do have a couple of questions:
I definitely had to go back and re-read about defmulti and defmethod. Is this a more Clojure-idiomatic way as opposed to using case statements? Or are these preferred in future instances where the defmethods can get more complex than one would want to deal with in a case statement, and it’s good to get into this habit?
When specifically calling counter.core.msg() for the onclick event, I wasn’t sure why I wasn’t able to do something like (msg "increment").
There’s nothing wrong with using the case macro. In the end, defmulti is pretty much a glorified case. You can also easilly refactor a case to a defmulti, so its easy to start with a case and later move to a defmulti if needed.
The distinction is two fold:
Syntactic: They differ in syntax, and so sometimes the syntax of one is more manageable and maybe easier to read than the other, depending on the situation. I’d say for small number of cases and when each case does very little, case is probably nicer.
Semantic: They actually differ a little semantically. Case is closed to extension. Defmulti is open to extension. You can add more defmethod from outside the function, even from another namespace. So for situations where you expect people who use your namespace to add their own cases, only defmulti can do it. For case, they’ll need access to the source, and modify the original case expression.
My guess is that the hiccup lib you’re using doesn’t support that.