It's now here: https://github.com/vvvvalvalval/reagent-phonecat-tutorial/wiki.
The official AngularJS 'Phonecat' tutorial ported to a ClojureScript + Reagent stack.
This project is based on the Reagent Leiningen template, which has the following workflow:
lein deps # installing the dependencies
lein figwheel # compiling clojurescript and serving the files with a development server
## you can now visit the running app on [localhost:3449/#/](http.//localhost:3449/#/)
## Optional: starting a ClojureScript REPL
lein repl # starting a Clojure REPL
>reagent-phonecat.handler: (browser-repl) # starting the ClojureScript REPL
See here for more information.
You can see the code for each step in the tutorial by git
-checking out the corresponding branches (step-1
to step-12
). Please note that:
step-11
has not been implemented, since I have not found an equivalent ofngResource
for ClojureScriptstep-12
(animations) is currently in a quite experimental state, suggestions appreciated.
The original tutorial consists of writing a small Single Page App that is a catalog of Android devices, featuring templating, data-binding, AJAX, routing, and some AngularJS-specific topics.
Here are some links to help you navigate efficiently through both implementations:
- Step 0: Initial app setup.
- Step 1: Static template - adding some static content to the HTML. (Angular diff | Reagent diff)
- Step 2: Making the same HTML dynamically generated from data. Note that in the Reagent case, there is no HTML code, and you can experiment with livecoding. (Angular diff | Reagent diff)
- Step 3: Adding text search capabilities, which introduces user input, state and DOM updates. Note that implementing the filtering logic took some more work in Reagent, as there is no such thing as the built-in filter AngularJS filter in Reagent; however, you get to work with the full ClojureScript collection manipulation capabilities. (Angular diff | Reagent diff)
- Step 4: More user input with a sorting feature. This does not add much in Reagent, but was an occasion to introduce Reagent cursors. (Angular diff | Reagent diff)
- Step 5: Instead of hardcoding the data, load it with AJAX. (Angular diff | Reagent diff)
- Step 6: Adding images, in Reagent this is trivial. (Angular diff | Reagent diff)
- Step 7: Demonstrates routing, which takes a bit of setup in Reagent. Note that in Reagent, 'pages' are just components. (Angular diff | Reagent diff)
- Step 8: Implementing a detailed phone view. No new topic. The components-based approach of Reagent was leveraged. (Angular diff | Reagent diff)
- Step 9: Displaying a boolean property with check marks. It takes a filter in Angular and a function in Reagent. (Angular diff | Reagent diff)
- Step 10: More UI state with a changeable main image feature. (Angular diff | Reagent diff)
- Step 11:
$resource
service, no ClojureScript equivalent. - Step 12: Animations, still experimental for this project.
- Reagent for 'templating'
- Reagent and reagent-cursor for managing state
- A combination of secretary and Google Closure's History for client-side routing
- cljs-ajax for AJAX
Except for UI state with limited scope, all the state of the application is held in a global atom, that acts notably as a partial copy of the database. Ajax calls are used only to populate and update this atom. While the drawbacks of global state are well-known, I feel there are several things in this configuration that mitigate them:
- Combined with ClojureScript livecoding capabilities, having all the state in one accessible place makes it easy to debug and reason about your stateful app
- Reagent cursors allow us to decouple view rendering/interactions from state storage
- React components enable you to build the view incrementally, with much finer control flow than the one-step controller->view relationship you typically find in Angular apps.
Navigation is done by keeping the current page component (i.e a function) and the associated route params in the state. This does not feel very neat, I'd rather have only the location hash in the state, but from what I've seen I don't think that's easily compatible with secretary currently.
Everything is in one namespace, splitting it felt like an overkill since there are only about 200 LoC at most.
This is my first try at Reagent, and many of the choices I made are questionable. Comments, critics and suggestions for improvement are appreciated and encouraged.