Zero configuration consists of registering components, streams, effects, and injections. Telling Zero
how to read attributes for particular components. And customizing how Zero harvests useful data from
DOM events. All of Zero's configuration functionality is in zero.config
, assumed to be aliased as
zc
in this doc.
Use zc/reg-components
to register a set of web components. This function takes a sequence of name + options
pairs, with the :view
option being the only one required. Here's what each option does:
:view
- Gives the function used to render the component. Said function receives a map of props, and should
return the component's markup with the notation described here.:props
- Tells Zero which it should pass to the :view
function, and where to get them. The simplest form
for this prop is simply a set of keywords #{:foo :bar :baz}
. This will tell Zero to add fields with the given
names (or the cammelCase variant of the same name) to the web component class it generates, and also to observe
attributes of the same name. If further customization is required, a map can be given instead of a set. The map
consists of name -> spec
mappings, where the spec
can look like:
:attr
- Zero won't create a field for the prop, it'll source the value only from the attribute
of the same name.:field
- Zero will generate a field for the prop.:default
- Zero will generate a field for the prop as well as observing the attribute of the same name
for values.IWatchable
(any maybe IDeref
), or a factory function for the same - Zero will add logic to have every
instance of the component watch (and source the prob value from) the given object. If said object satisfies
IDeref
then it'll be deref'd for an initial value before the component's first render. If the given value
is a function (factory for an IWatchable
) then it'll receive the component instance.:attr <attr-name>
- The name of an attribute (should be a string) that the prop's value can be sourced from.:field <field-name>
- The name of the field Zero should generate for this prop.:state IWatchable
- An IWatchable
thing to pull the value from. If a :field
is also specified, then the
generated field will be read-only. If it satisfies IDeref
then it'll be deref'd for an initial value.:state-factory IFn
- A state factory. Receives the component instance, should yield something that can be
used as :state
. Ignored if :state
is given explicitly.:state-cleanup IFn
- A cleanup function for state created by a state factory. Receives the state and
the component instance.:inherit-doc-css?
- If true, Zero will try to pull in CSS linked in the root document. The referenced CSS
resources are pulled in as CSSStyleSheet
instances; which ignore imports. So beware.:focus
- Can be one of:
:delegate
- Zero will create the component's ShadowRoot with the delegatesFocus option,
causing any clicks (or other attempts to focus) the component to instead focus its first focusable child. Useful
for components that wrap native input elements.:self
- Causes Zero to attach an implicit tabIndex = 0
to instances of the component if the field hasn't
been set to something else explicitly. This makes the component focusable.Here's an example:
(zc/reg-components
:example/button
{:props #{:text}
:inherit-doc-css? true
:focus :delegate
:derive :example/Button
:view (fn [{:keys [text]}
[:button text]])}
:example/input
{:props #{:type :value}
:inherit-doc-css? true
:focus :delegate
:view (fn [{:keys [type value]}]
[:input :type type :value value])})
TODO
TODO
TODO: make this better
Zero's data streams fill a similar role to subscriptions from other frameworks (eh, re-frame, that is). But they
work at a lower level, and integrate well with the rest of ClojureScript through the IWatchable
and IDeref
protocols. In essence, data streams are the source nodes for push-based reactive data pipelines. They boot up when needed
(when they have active watches), push data changes into the pipeline as they occur, then spin down when no longer needed.
We tap into data streams by creating bindings (e.g (bnd :stream-key ...args)
), which are IWatchable
and IDeref
,
and have value semantics. Watching or deref'ing a binding actually watches or derefs the underlying data stream; so
if the stream is not active a deref will yield nil
.
(zc/reg-streams
:random
(fn [rx max]
(let [interval (js/setInterval #(rx (js/Math.floor (* (js/Math.random) max))) 10000)]
(fn cleanup []
(js/clearInterval interval)))))
(add-watch (bnd :random 100) ::my-watch
(fn [_ _ old-val new-val]
(js/console.log :random new-val)))
Since bindings are IWatchable
, Zero will automatically bind them when passed as element props, causing said props
to react to changes in the binding.
Though the default API works at a lower level, Zero provides some convenient utilities for creating derived streams
(streams whose value is derived from one or more other streams) or setting up a watch to perform some action when one
or more streams change. These utilities, along with others, are provided in zero.extras.util
; assumed to be aliased
to zu
in the following examples:
(zc/reg-streams
:derived-stream
(zu/derived
(fn [[foo bar] & _args]
(+ foo bar))
(bnd :foo-stream)
(bnd :bar-stream)))
(zu/watch ::my-watch-key
(fn [foo bar]
(do-something! foo bar))
(bnd :foo-stream)
(bnd :bar-stream))
(js/setTimeout #(zu/unwatch ::my-watch-key) 10000)
Note that although these utilities are designed with streams in mind, they're based on the IWatchable
and IDeref
protocols; so will work on anything data source that's IWatchable
.
Injections are placeholders for data meant to be lazily substituted into Zero markup, an action, or a binding.
An injection is created with (<< :injection-key ..args)
, and will be substituted at a time depending on where
said injection is found:
Multiple instances of the same injection (same injection key and args) found in the same context, will share a single evaluation, so the injection handler will only be called once for a particular set or arguments, within a given context.
Register injection handlers with zc/reg-injections
.
(zc/reg-injections
:event-data
(fn [context & _args]
(-> context :z.event/data)))
Injection handlers receive a context map, representing the context in which said injection is being evaluated. For actions dispatched on an event, this context will look like this:
{:zero.core/event.data (comment "data harvested from the event")
:zero.core/event.target (comment "event target at time of action dispatch")
:zero.core/event.current (comment "event currentTarget at time of action dispatch")
:zero.core/event (comment "the event with which the action was dispatched")
:zero.core/host (comment "if :z/root is a ShadowRoot, then its host; generally this is the current component's DOM node")
:zero.core/root (comment "the root node of :z/event.current, usually the ShadowRoot of the current component")}
For injections in a component's markup, the context will have:
{:zero.core/host (comment "the DOM node of the component being rendered")
:zero.core/root (comment "the ShadowRoot of the component being rendered")}
For injections bindings, the context will be empty.
Actions are callable objects with value semantics. They allow us to give a sequence of declarative side effects to be invoked when the action is dispatched. The benefits of actions over regular functions include the following:
Actions are created like this (act ?options ...effects)
, where effects
is a sequence of vectors representing
the ordered effects to be invoked when the action is dispatched. The options
are optional, any may be
provided as a map to customize the behavior of the action. The following options are supported:
:prevent-default?
- If true, and the action is dispatched on an event, the event's preventDefault()
method will be called:stop-propagation?
- If true, and the action is dispatched on an event, the event's stopPropagation()
method will be called:log?
- If true then various bits of useful info will be logged to the console when the action is dispatched:dispatch
- The dispatch strategy. One of :default
, :immediate
, :throttle
, :debounce
. If :throttle
or :debounce
are given then the action's dispatches will be regulated accordingly. The :immediate
dispatch strategy causes the action's
side effects (and injections) to be invoked immediately when the action is dispatched, as opposed to the :default
, which does
so later after the event has become stale:delta
- If the :throttle
or :debounce
dispatch strategies are used, this controls the throttle time (in ms)Effects are registered with zc/reg-effects
.
(zc/reg-effects
:do-something
(fn [x]
(js/console.log (str "doing something (" x ")")))
:do-something-else
(fn [x]
(js/console.log (str "doing something else (" x ")"))))
(def my-act (act {:log? true} [:do-something "foo"] [:do-something-else "bar"]))
(my-act)
;> doing something (foo)
;> doing something else (bar)
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close