tape-framework / tools

Some common pieces of infrastructure: input data helpers, timeouts & intervals, current view.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

README

tape.tools

About

Some common pieces of infrastructure:

  • input data helpers

  • timeouts & intervals effects

Install

Add tape.tools to your deps:

deps.edn
{:deps {tape/tools {:local/root "../tools"}}}

Usage

You must be familiar with tape.module and tape.mvc (particularly the controller part) before proceeding. Depending on pieces used, require as:

(:require
 [tape.tools.current.controller :as current.c]
 [tape.tools.timeouts.controller :as timeouts.c]
 [tape.tools.intervals.controller :as intervals.c]
 [tape.tools.ui.form :as form :include-macros true])

The lens pattern

A form/lens* is a helper for making cursors that read from a subscription and write by dispatching an event.

(defn form-fields []
  (let [lens (form/lens* ::posts.c/post ::posts.c/field)
        title (r/cursor lens [:title])]
    [:input {:value @title
             :on-change #(reset! title (-> % .-target .-value))}]))

When the title cursor above is reset!, it dispatches an event: [::posts.c/field :title "some value"]. When it is deref 'ed it reads the (:title @(rf/subscription [::posts.c/post])) value.

Form tools

Inputs

The form/field helper creates an input over a cursor source (ratom or get-set fn).

(defn form-fields []
  (let [lens (form/lens* ::posts.c/post ::posts.c/field)]
    [:<>
     [form/field {:type :text, :source lens, :field :title}]
     [form/field {:type :textarea, :source lens, :field :description}]]))

We assume the following 2 controller functions are present:

app/posts/controller.cljs
(ns blog.app.posts.controller ...)
(defn ^::c/event-db field [db [_ k v]] (assoc-in db [::post k] v))
(defn ^::c/sub post [db _] (::post db))
Lens Ergonomic API

Ergonomic API uses macros with symbols of controller event handlers. Such symbols can be navigated via IDE "jump to definition". The macros are macroexpanded in the API above, so there’s no performance penalty at runtime.

(form/lens posts.c/post posts.c/field)

Validation feedback

The form/field-with-list-errors is a drop-in replacement for form/field that adds the class is-danger on the input when there are :errors on the input map m, and also lists the errors below.

Constraint Validation

The form/when-valid helper can be used on forms to to check and show the HTML5 Constraint Validation API.

(defn save-button []
  (let [save #(rf/dispatch [::posts.c/save])]
    [:button {:on-click (form/when-valid save)} "Save"]))

If the form to which the button above belongs is valid, on clicking the button the save function is called; if the form is invalid the save function is not called, and the form validity is reported.

Timeouts and Intervals

We have two controllers with event handlers and effect handlers for setting and clearing timeouts and intervals. Don’t forget to add ::timeouts.c/module & ::intervals.c/module to the modules config map.

A timeout is a map with 3 positions: the number of milliseconds, an optional event that gets dispatched when the timeout is set (with the timeout id added) and an event that gets dispatched when the timer times out:

{:ms 3000
 :set [::was-set] ;; optional, dispatched as [::was-set timeout-id]
 :timeout [::timed-out]}

Similarly, an interval:

{:ms 3000
 :set [::was-set] ;; dispatched as [::was-set interval-id]
 :interval [::period-reached]}

These are given as arguments to events or effects, as follows:

[::timeouts.c/set a-timeout] ;; event
[::timeouts.c/clear a-timeout-id] ;; event

{::timeouts.c/set a-timeout} ;; effect
{::timeouts.c/clear a-timeout-id} ;; effect

[::intervals.c/set an-interval] ;; event
[::intervals.c/clear an-interval-id] ;; event

{::intervals.c/set an-interval} ;; effect
{::intervals.c/clear an-interval-id} ;; effect

Current view

The "current page" that changes alongside the URL (whether via hash-change or History API) is so established on the web that we decided to make it available by default. Add ::current.c/module to the modules config map.

The "current view" is set in app-db under ::current.c/view. It’s value is an event keyword (controller namespaced), for example: :blog.app.posts.controller/index.

The "current view" is automatically set in app-db by an interceptor if not set already from the event handler, if there exists a view corresponding to the event, per the naming convention. This interceptor is added to all handlers that have matching views; example: blog.app.posts.controller/indexblog.app.posts.view/index.

There are two subscriptions:

  • (rf/subscribe [::current.c/view]) yields the event set.

  • (rf/subscribe [::current.c/view-fn]) yields the view function that can be rendered in Reagent layouts.

License

Copyright © 2019 clyfe

Distributed under the MIT license.

About

Some common pieces of infrastructure: input data helpers, timeouts & intervals, current view.


Languages

Language:Clojure 100.0%