Liking cljdoc? Tell your friends :D

domino-ui

Clojars Project

A UI component library built on top of re-frame and Domino. The library provides a multimethod for declaring UI components along with re-frame events and subscriptions for managing the state of these components.

Creating components

Components are declared using the domino.ui.component/component multimethod. The dispatching function is as follows:

(defmulti component first)

As an example, let's take a look at declaring a text-input component. First, we'll need to require the following namespaces:

(ns myapp.components
  (:require
   [domino.ui.core :as core]
   [domino.ui.component :refer [component] :as cp]
   [re-frame.core :as rf]))

The component accepts a Hiccup style vector that contains a namespaced keyword specifying the type of the component, followed by an options map, and an optional body of the component. A declaration for a text input might look as follows:

[:domino.ui.component/text-input {:id :first-name}]

The :id key specified in the options map references the ID for the path in the Domino model. Given the above declaration we will write the following multimethod to instantiate the component:

(defmethod component :domino.ui.component/text-input [[_ {:keys [context id]}]]
  (fn []
    (let [{:keys [disabled?]} @(rf/subscribe [::core/component-state context id])]
      [:input
       {:type      :text
        :disabled  disabled?
        :value     @(rf/subscribe [::core/subscribe context id])
        :on-change #(rf/dispatch [::core/transact context id (-> % .-target .-value)])}])))   

The multimethod will receive the vector declaration for the component and create a Reagent component function using it.

Note that domino-ui supports multiple Domino contexts, and the context for each component will be injected into the options map when the engine is initialized.

Reading/Writing Component State

The component can observe its state using the :domino.ui.core/component-state subscription. This subscription should contain a map with the state of the component. In the example, the map can have a :disabled? key that toggles whether the component is disabled.

The state of the component can be modified using the :domino.ui.core/merge-component-state and :domino.ui.core/update-component-state events. The merge event accepts Domino context, component id, and a map containing the new state that will be merged on top of the current state. The update event accepts the Domino context, component id, and a function that should accept the current state of the component and return an updated one.

The value of the component is read using the :domino.ui.core/subscribe subscription and passing it the Domino context and the component id specified in the options map. In this case, the id is :first-name.

Finally, the component updates the current value in the model associated with the component using the :domino.ui.core/transact event. This event accepts the Domino context followed by the new value.

Using Components

Once the component is declared we can create a Domino schema and add a :views key to it. This key will contain a map of view declarations, e.g:

(def default-schema
    {:views   {:default [:dv
                         [:h3 "User"]
                         [:div
                          [:label "First name"]
                          [:domino.ui.component/text-input {:id :first-name}]]
                         [:div
                          [:label "Last name"]
                          [:domino.ui.component/text-input {:id :last-name}]]]}
     :model   [[:demographics
                [:first-name {:id :first-name}]
                [:last-name {:id :last-name}]
                [:full-name {:id :full-name}]]]
     :effects [{:inputs  [:first-name]
                :handler (fn [_ {:keys [first-name]}]
                           (rf/dispatch [::core/update-component-state
                                         :default-ctx
                                         :last-name
                                         {:disabled? (empty? first-name)}]))}]
     :events  [(domino/event [ctx {:keys [first-name last-name]} {:keys [full-name]}]
                             {:full-name (or (when (and first-name last-name)
                                               (str last-name ", " first-name))
                                             first-name
                                             last-name)})]})

The view declaration uses plain Hiccup to declare the scaffolding and uses domino-ui components for interactive elements. The schema can now be initialized calling the :domino.ui.core/init-ctx re-frame event:

(rf/dispatch-sync [:domino.ui.core/init-ctx :default-ctx default-schema {}])

This event will initialize the domino engine with domino/initialize and parse/render the domino-ui components used in the views.

Once the view is initialized, it can be used via the :domino.ui.core/view subscription.

The following subscriptions are provided:

Domino context

@(rf/subscribe [::core/ctx :default-ctx])

Domino model

@(rf/subscribe [::core/model :default-ctx])

view schema

@(rf/subscribe [::core/view :default-ctx :default])

all views for a given context

@(rf/subscribe [::core/views :default-ctx])

Domino DB state

@(rf/subscribe [::core/db :default-ctx])

component state

@(rf/subscribe [::core/component-state :default-ctx :first-name])

component states

@(rf/subscribe [::core/component-states :default-ctx])

subscription to the value associated with a UI component

@(rf/subscribe [::core/subscribe :default-ctx :first-name])

Domino change history for the latest transaction

@(rf/subscribe [::core/change-history :default-ctx])

Following events are provided

initialize Domino context with an initial state

@(rf/dispatch [::init-ctx :default-ctx default-schema {:demographics
                                                       {:first-name "Bob"}}])

transact values

@(rf/dispatch [::core/transact :default-ctx
               [:first-name "Bob"]
               [:last-name "Bobberton"]])

merge component state

@(rf/dispatch [::merge-component-state :default-ctx :first-name {:disabled? true}])

update component state

@(rf/dispatch [::update-component-state :default-ctx :first-name
               (fn [state-map] (assoc state-map :disabled? true))])

trigger effects

@(rf/dispatch [::core/trigger :default-ctx [:effect-id]])

See here for a complete example of this in action.

License

Copyright © 2018 FIXME

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

Can you improve this documentation? These fine people already did:
dmitri.sotnikov@gmail.com, Carmen La & Dmitri Sotnikov
Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close