Skip to content

Commit 903f212

Browse files
committed
Update single page app example
1 parent 360855f commit 903f212

File tree

1 file changed

+21
-26
lines changed

1 file changed

+21
-26
lines changed

index.adoc

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1425,24 +1425,24 @@ example we'll limit the application to the bare minimum.
14251425

14261426
The next step is to create the client code. For this we'll use
14271427
https://github.com/cjohansen/replicant[Replicant] for updating the DOM,
1428-
and https://github.com/r0man/cljs-http[cljs-http] for communicating with
1429-
the server API.
1428+
and https://github.com/duct-framework/client.http[Duct client.http] for
1429+
communicating with the server API.
14301430

14311431
This requires us to once again update the project dependencies:
14321432

14331433
.deps.edn
14341434
[,clojure]
14351435
----
14361436
{:deps {org.clojure/clojure {:mvn/version "1.12.1"}
1437+
org.duct-framework/client.http {:mvn/version "0.1.0"}
14371438
org.duct-framework/main {:mvn/version "0.1.10"}
14381439
org.duct-framework/module.cljs {:mvn/version "0.5.1"}
14391440
org.duct-framework/module.logging {:mvn/version "0.6.5"}
14401441
org.duct-framework/module.web {:mvn/version "0.12.9"}
14411442
org.duct-framework/module.sql {:mvn/version "0.8.0"}
14421443
org.xerial/sqlite-jdbc {:mvn/version "3.50.3.0"}
14431444
com.github.seancorfield/next.jdbc {:mvn/version "1.3.1048"}
1444-
no.cjohansen/replicant {:mvn/version "2025.06.21"}
1445-
cljs-http/cljs-http {:mvn/version "0.1.48"}}
1445+
no.cjohansen/replicant {:mvn/version "2025.06.21"}}
14461446
:aliases {:duct {:main-opts ["-m" "duct.main"]}}}
14471447
----
14481448

@@ -1454,32 +1454,22 @@ file for the client UI.
14541454
----
14551455
(ns todo.client
14561456
(:require [replicant.dom :as r]
1457-
[cljs-http.client :as http]
1457+
[duct.client.http :as http]
14581458
[clojure.core.async :as a :refer [<!]]))
14591459
1460-
;; Helper functions that add anti-forgery headers.
1461-
(defn delete [url]
1462-
(http/delete url {:headers {"X-Ring-Anti-Forgery" "1"}}))
1463-
1464-
(defn post [url params]
1465-
(http/post url {:headers {"X-Ring-Anti-Forgery" "1"}, :json-params params}))
1466-
1467-
(defonce todos
1468-
(js/document.getElementById "todos"))
1469-
14701460
(defonce store (atom {}))
14711461
14721462
(defn update-todos []
1473-
(a/go (let [resp (<! (http/get "/todos"))]
1463+
(a/go (let [resp (<! (http/get [:todos]))]
14741464
(swap! store assoc :todos (-> resp :body :results)))))
14751465
14761466
(defn delete-todo [id]
1477-
(a/go (<! (delete (str "/todos/" id)))
1467+
(a/go (<! (http/delete [:todos id]))
14781468
(<! (update-todos))))
14791469
14801470
(defn create-todo []
14811471
(a/go (let [input (js/document.getElementById "todo-desc")]
1482-
(<! (post "/todos" {:description (.-value input)}))
1472+
(<! (http/post [:todos] {:description (.-value input)}))
14831473
(<! (update-todos))
14841474
(set! (.-value input) ""))))
14851475
@@ -1496,30 +1486,35 @@ file for the client UI.
14961486
[:a {:href "#" :on {:click #(delete-todo id)}} "delete"]])
14971487
[:li (create-todo-form)]])
14981488
1489+
(defonce todos
1490+
(js/document.getElementById "todos"))
1491+
14991492
(add-watch store ::render (fn [_ _ _ s] (r/render todos (todo-list s))))
15001493
(update-todos)
15011494
----
15021495

1503-
WARNING: In the above example, the '`click`' event is bound to a function.
1504-
This is not considered best practice for Replicant, but used in the
1505-
example code for the sake of brevity.
1506-
15071496
Here we reach the edge of Duct. This ClojureScript file is not specific
15081497
to our framework, but would be at home in any Clojure project.
15091498
Nevertheless, for the sake of completeness we'll provide some
15101499
explanation of what this file does.
15111500

1512-
The `delete` and `post` functions add the `X-Ring-Anti-Forgery` header,
1513-
which is needed to get past the anti-forgery protection.
1501+
The `get`, `post` and `delete` functions from the Duct HTTP client
1502+
simplify communication with the server. They communicate using the
1503+
Transit serialization format, and automatically add headers to get
1504+
around the webservers CSRF protection.
15141505

15151506
The `update-todos`, `delete-todo` and `create-todo` functions all update
1516-
the `state` atom, which contains a data structure that represents the
1507+
the `store` atom, which contains a data structure that represents the
15171508
state of the UI. In this case, it's a list of todo items.
15181509

1519-
There is a watch attached to the `state` atom. When the state is
1510+
There is a watch attached to the `store` atom. When the store is
15201511
changed, the `todos` DOM element is updated accordingly, with a new
15211512
unordered list of todo items. Replicant is smart enough to update only
15221513
the elements that have changed, making updates efficient.
15231514

1515+
WARNING: In the example code, the '`click`' event is bound to a
1516+
function. This is not considered best practice for Replicant, but is
1517+
used in this example for the sake of brevity.
1518+
15241519
Now that we have both a server and client, we can `(reset)` the REPL
15251520
and check the web application at: <http://localhost:3000>

0 commit comments

Comments
 (0)