The client library for the Untangled Web framework.
What follows is a quick start tutorial that assumes at least a passing familiarity with ClojureScript and Figwheel. For an more information & depth, checkout the links in Learn more.
-
about Untangled Client
-
about Untangled & checkout the Documentation Reference
-
interactively with the Untangled Tutorial
The following instructions assume:
-
You’re using leiningen
-
You’d like to be able to use Cursive REPL integration with IntelliJ
-
You’ll use Chrome and would like to have nice support for looking at cljs data structures in the browser and console log messages.
In addition to the base untangled client library, the following are the minimum requirements for a project file:
-
Choose a version of clj/cljs
-
Choose a version of Om (Untangled requires Om, but treats it as a provided dependency)
If you copy/paste the following file into a project.clj
it will serve as a good start:
(defproject client-demo "1.0.0"
:description "Untangled Client Quickstart"
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.8.51"]
[org.omcljs/om "1.0.0-alpha46"]
[navis/untangled-client "0.5.6"]]
; needed or compiled js files won't get cleaned
:clean-targets ^{:protect false} ["resources/public/js/compiled" "target" "i18n/out"]
; needed for macros and our recommended figwheel setup
:source-paths ["dev/server" "src/client"]
:cljsbuild {:builds [{:id "dev"
:source-paths ["dev/client" "src/client"]
:figwheel true
:compiler {:main "cljs.user"
:asset-path "js/compiled/dev"
:output-to "resources/public/js/compiled/app.js"
:output-dir "resources/public/js/compiled/dev"
:recompile-dependents true
:optimizations :none}}]}
; figwheel dependency and chrome data structure formatting tools (formatting cljs in source debugging and logging)
:profiles {:dev {:dependencies [[figwheel-sidecar "0.5.7"]
[binaryage/devtools "0.6.1"]]}})
Create the directories as follows (OSX/Linux):
mkdir -p src/client/app dev/client/cljs dev/server resources/public/css resources/public/js script
then create a base HTML file in resources/public/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Application</title>
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div id="app"></div>
<script src="js/compiled/app.js"></script>
</body>
</html>
and an empty CSS file:
touch resources/public/css/app.css
Make the application itself, with an initial state, in src/client/app/core.cljs
:
(ns app.core
(:require [untangled.client.core :as uc]))
; The application itself, create, and store in an atom for a later DOM mount and dev mode debug analysis
; of the application.
; The initial state is the starting data for the entire UI
; see dev/client/user.cljs for the actual DOM mount
(defonce app (atom (uc/new-untangled-client)))
Notice that making the application is a single line of code.
then create the base UI in src/client/app/ui.cljs
:
(ns app.ui
(:require [om.next :as om :refer-macros [defui]]
[untangled.client.mutations :as mut]
[untangled.client.core :as uc]
[om.dom :as dom]))
;; A UI node, with a co-located query of app state and a definition of the application's initial state.
;; The `:once` metadata ensures that figwheel does not redefine the static component with each re-render
(defui ^:once Root
static uc/InitialAppState
(initial-state [this params] {:ui/react-key "ROOT"
:some-data 42})
static om/IQuery
(query [this] [:ui/react-key :some-data])
Object
(render [this]
(let [{:keys [ui/react-key some-data]} (om/props this)]
(dom/div #js {:key react-key}
(str "Hello world: " some-data)))))
Create an application entry point for development mode in dev/client/cljs/user.cljs
:
(ns cljs.user
(:require
[cljs.pprint :refer [pprint]]
[devtools.core :as devtools]
[untangled.client.logging :as log]
[untangled.client.core :as uc]
[app.ui :as ui]
[app.core :as core]))
;; Enable browser console
(enable-console-print!)
;; Set overall browser loggin level
(log/set-level :debug)
;; Enable devtools in chrome for data structure formatting
(defonce cljs-build-tools (devtools/install!))
;; Mount the Root UI component in the DOM div named "app"
(swap! core/app uc/mount ui/Root "app")
technically, only the ns
declaration and last line are necessary.
We don’t use the lein plugin for figwheel, as we’d rather have IntelliJ REPL integration, which we find works better with a figwheel sidecar setup.
The setup can read the cljs builds from the project file, and can also support specifying which builds you’d like to initially start via JVM options (e.g. -Dtest -Ddev will cause it to build the test and dev builds).
To get this, place the following in dev/server/user.clj
:
(ns user
(:require [figwheel-sidecar.system :as fig]
[com.stuartsierra.component :as component]))
(def figwheel-config (fig/fetch-config))
(def figwheel (atom nil))
(defn start-figwheel
"Start Figwheel on the given builds, or defaults to build-ids in `figwheel-config`."
([]
(let [props (System/getProperties)
all-builds (->> figwheel-config :data :all-builds (mapv :id))]
(start-figwheel (keys (select-keys props all-builds)))))
([build-ids]
(let [default-build-ids (-> figwheel-config :data :build-ids)
build-ids (if (empty? build-ids) default-build-ids build-ids)
preferred-config (assoc-in figwheel-config [:data :build-ids] build-ids)]
(reset! figwheel (component/system-map
:figwheel-system (fig/figwheel-system preferred-config)
:css-watcher (fig/css-watcher {:watch-paths ["resources/public/css"]})))
(println "STARTING FIGWHEEL ON BUILDS: " build-ids)
(swap! figwheel component/start)
(fig/cljs-repl (:figwheel-system @figwheel)))))
and you’ll also want the following startup script in script/figwheel.clj
:
(require '[user :refer [start-figwheel]])
(start-figwheel)
and now you can either start figwheel from the command prompt with:
lein run -m clojure.main script/figwheel.clj
or from Cursive in IntelliJ with a run profile:
-
Local REPL
-
Use clojure main in a normal JVM, not an NREPL
-
Under Parameters, add: script/figwheel.clj
Once you’ve started figwheel you should be able to browse to:
and see the UI. Any changes you make to the UI or to the CSS will automatically reload.
We recommend going through the Untangled Developers Guide, which you should clone and work through on your local machine.
An Untanged template is in progress. A pretty complete version is available at https://github.com/untangled-web/untangled-template and has:
-
Full stack with sample UI for login/sign up.
-
Newer version of figwheel (better errors, etc.)
-
Bootstrap CSS
-
Examples of adding REST routes to the server
-
Examples of hooking into the Ring handlers
-
Sample tests for the server and client
-
Uberjar building
-
Deployment to Heroku (or similar environments)
-
CI (command-line runnable) testing for UI (via karma) and server
-
Devcards