keechma.ui-component

->UIComponent

(->UIComponent component-deps subscription-deps renderer)

constructor

(constructor opts)

Createas a UIComponent record. Accepts opts as the argument. opts is a map that can have the following params:

  • :component-deps - Which child component is this component dependent on
  • :subscription-deps - Which subscriptions is this component dependent on
  • :topic - On which topic should this component send commands (to the controller)
  • :renderer - A renderer function (Reagent component)

The UIComponent record is a way to list all the dependencies for a component. When the application is started each component’s renderer function will be partiall applied with a verion of it’s UIComponent that has the component and subscription dependencies resolved.

This allows you to write components that are completely decoupled from the rest of the application.

When you want to resolve injected component or subscription dependencies, you can use the functions defined on the UIComponent protocol:

(def render [ctx] ;; `ctx` is a UIComponent with resolved dependencies
  (let [child-component (component ctx :component-key)
        subscription (subscription ctx :subscription-key)])

(def component (constructor {:renderer render
                             :component-deps [:component-key]
                             :subscription-deps [:subscription-key]))

IUIComponent

protocol

IUIComponent protocol defines functions that can be called on the UIComponent records.

members

redirect

(redirect this params)

Redirects page to the URL generated from params

send-command

(send-command this command)(send-command this command args)

Sends a command to the controller.

subscription

(subscription this key)(subscription this key args)

Returns a subscription based on the key.

report

(report this name payload)(report this name payload path)(report this name payload path severity)

current-route

(current-route this)

Returns a current route data. It will use the :current-route-fn that is injected from the outside to return the data.

component

(component this key)

Returns a component based on the key.

renderer

(renderer this)

Returns a component renderer function that has the component record partially applied (ctx) as the first argument.

url

(url this params)

Returns a URL based on the params. It will use the :url-fn that is injected from the outside to generate the URL based on the current app routes.

make-ex

(make-ex msg)

map->UIComponent

(map->UIComponent G__14575)

resolve-component-dep

resolve-dep

(resolve-dep dep-kw coll-kw component key component-dep)

Resolves a dependency on the UIComponent record. Returns a new version of the record with the resolved dependency.

(def news-component (constructor {:component-deps [:user-profile :similar-news]
                                  :subscription-deps [:current-news :comments]}))

(resolve-dep :subscription-deps :subscriptions news-component :current-news (fn []...))
;; Returns a new version of the `news-component` with the :current-news subscription resolved

(resolve-dep :component-deps :components news-component :user-profile user-profile-component))
;; Returns a new version of the `news-component` with the :user-profile component resolved

Two shorthand functions are defined that allow you to omit first two arguments:

  • resolve-subscription-dep - Resolves a subscription dependency
  • resolve-component-dep - Resolves a component dependency

resolve-subscription-dep

system

(system components)(system components subscriptions)

Creates a component system.

Component system is a group of components that are interdependent. Each component system must define a :main component which will be returned from the system function.

system function resolves dependencies between components.

For example, let’s say that your application consists of three components:

  • layout
  • sidebar
  • chat room list

Layout is rendering the sidebar component and sidebar component is rendering the chat room list component.

Chat room list component has a subscription dependency on :chat-rooms which will return the list of chat rooms to render.

Usually you would either pass the chat rooms subscription from layout to sidebar to the chat room list component, or the chat room list component would have a dependency on a global :chat-rooms subscription.

Keechma allows you to avoid both of these problems:

  • only the chat room list component will know about it’s dependencies
  • the dependencies will be injected from the outside by the system function
(defn layout-renderer [ctx] ;; ctx is `layout-component` record with resolved dependencies
[:div.main
[:div.sidebar [(component ctx :sidebar)]]]) ;; Resolve the `:sidebar` component

(def layout-component (constructor {:component-deps [:sidebar]
:renderer layout-renderer}))

(defn sidebar-renderer [ctx]
[:div.sidebar
[(component ctx :chat-room-list)]) ;; Resolve the `:chat-room-list` component

(def sidebar-component (constructor {:component-deps [:chat-room-list]
:renderer sidebar-renderer}))

(defn chat-room-list-renderer [ctx]
(let [chat-rooms (subscription ctx :chat-rooms)])) ;; Resolve the `:chat-rooms` subscription

(def chat-room-list-component (constructor {:subscription-deps [:chat-rooms]}))

(def main-component (system {:main layout-component ;; Map compnents to keys
:sidebar sidebar-component
:chat-room-list chat-room-list-component}
{:chat-rooms (fn [app-state-atom])})) ;; Map subscriptions to keys

In the example above main-component will be a Reagent component that can be mounted into the DOM, with all of the dependencies between components resolved.

system function provided everything that components need from the outside, and all of the components are reusable by design.

UIComponent