metosin/re-fill

Name: re-fill

Owner: Metosin

Description: A collection of Re-frame components that most applications need.

Created: 2017-08-15 06:26:01.0

Updated: 2018-05-24 11:42:01.0

Pushed: 2017-10-12 12:09:22.0

Homepage: null

Size: 25

Language: Clojure

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Re-fill

A collection of Re-frame components that most applications need. Currently, Re-fill offers event handlers, subscriptions, effects and co-effects for:

Usage

Re-fill contains multiple utilities, which all have their own namespaces. It's possible to use only one of the utilities, or any combination of them. As in Re-frame, event handlers, subscriptions etc. will be registered globally after requiring the specific namespace for the wanted utility.

Routing

Routing utility provides means to listen for URL changes and dispatch events based on them, making it possible to write bookmarkable single-page apps with ease.

Currently, Re-fill only supports routes defined using Bidi syntax. For listening the URL in your address bar, Re-fill uses Pushy.

To use Re-fill for routing, first require the correct namespace(s):

example.core
require [reagent.core :as r]
        [re-frame.core :as rf]
        [re-fill.routing :as routing]))

Then, define your routes using Bidi syntax (no need to add dependency for it though). For example:

 routes ["/" {"" :routes/home
              ["page/" :id] :routes/page}])

It's recommended to use namespaced keywords, as those keywords will be dispatched by Re-frame.

You also need to create mappings between routes and views:

 views {:routes/home home-view
        :routes/page page-view
        ;; Value of :else will be used if there's no mapping for route
        :else loading-view})

In order to render the correct view, add routed-view in your root view / template, and give the mappings from previous step to it as argument. Here's an example:

n main-view
iews]
div.main
:h1 "This is shown in all views"]
; views refers to the mappings created in the previous step
routing/routed-view views]])

Lastly, in order to initialize the routing, dispatch :re-fill/init-routing during the startup of the application:

n init!

 routes refer to your Bidi-routes
f/dispatch [:re-fill/init-routing routes])
 views refer to your route - view mappings
/render [main-view views]
        (js/document.getElementById "app")))

t!)

After doing this, the URL in the address bar will be listened by Re-fill and the corresponding view will be rendered by template. You can either use links normally with hrefs (no preventDefault or manual history manipulation is needed), or dispatch `:re-fill/navigate` event to navigate programmatically. Here's an example of both:

v
Normal navigation with link
.controls__a {:href "/page/1"} "Page 1 (by link)"]

Navigation by dispatching an event
utton.controls__button
on-click (fn [_]
           (rf/dispatch [:re-fill/navigate
                         ;; The argument to re-fill/navigate is the actual
                         ;; route with path-params.
                         ;; See Bidi documentation for more information
                         [:routes/page :id 2]]))}
age 2 (by dispatch)"]]

The event `:re-fill/navigate` is great for initiating navigation from views. There's also an effect handler for :re-fill/navigate, which makes it possible to navigate from event handlers without dispatching more events. This is useful for cases where the view dispatches an event, and the event handler may conditionally initiate navigation to some other view in addition to other side effects (through effects of course).

If you need to refer to your current route from your views, you can use

(defn page-view [] (let [routing @(rf/subscribe [:re-fill/routing])]

[:h1 (pr-str routing)]))
r navigation happens, Re-fill dispatches an event using the route key as
dentifier for it. It's recommended to register an event handler for each
he routes, otherwise you get a warning to console about a missing event
ler. This dispatched event is great for setting up the state for the view
's gonna be rendered. Here's a simple example:

(rf/reg-event-fx :routes/home ;; The first argument is the co-effects (normal Re-frame stuff) ;; The second argument is the event itself. The route match ;; from bidi can be destructured from it (fn [ [ bidi-match]] ;; In real apps, this function would return effects map for ;; fetching data, setting state or something else. (js/console.log “Navigated to " bidi-match)))


l example](https://github.com/metosin/re-fill/tree/master/example-src/example/core.cljs)
more information.

Notifications

fications utility provides a generic notification interface for
ting and deleting notifications, and a subscription for using them
our views. Notifications are often used to show information or warning
ogs to end-users.

rder to use notifications, the correct namespaces must be required:

(ns example.core (:require [re-frame.core :as rf]

        [re-fill.notifications :as notifications]))
r that, notifications can be created with ```:re-fill/notify``` event.
example:

[:button.controls_button {:on-click (fn [] (rf/dispatch [:re-fill/notify

                               ;; The first argument is the data
                               ;; you want to use as notification.
                               ;; It's your responsibility to add types
                               ;; your notifications if you need them.
                               {:type :success
                                :content "Success!"}
                               ;; The second (optional) argument is used
                               ;; by re-fill to configure the notification.
                               ;; Currently, the only supported key is
                               ;; :hide-after, which can be used to delete
                               ;; the notification after a given time (in
                               ;; ms) has passed.
                               {:hide-after 3000}]))}

“Notify success!“]

``:hide-after``` is used, the notification will be removed automatically
r the timeout has passed. In order to delete the notification manually,
```:re-fill/delete-notification``` event can be used. 

ew must be created for rendering notifications. Re-fill provides a
cription ```:re-fill/notifications``` for getting the notifications
iew functions.

's a simple view to demonstrate the usage of

subscription:

n notifications-view

 Subscription to notifications
et [notifications @(rf/subscribe [:re-fill/notifications])]
[:div.notifications
 (for [{:keys [id type content]} notifications]
   [:div.notification
    {:key id
     :class (case type
              :success "notification--success"
              :warning "notification--warning")}
    [:span.notification__span content]
    [:button.notification__button
     ;; Deleting a notification requires the id of notification
     ;; Unique id is generated automatically by Re-fill 
     {:on-click #(rf/dispatch [:re-fill/delete-notification id])}
     "x"]])]))

See full example for more information.

Debounce

Debounce utility can be used for:

For those who are new to debounce there's a good explanation of debounce at css-tricks.com

To use debounce utility, the correct namespaces must be required:

example.core
require [re-frame.core :as rf]
        [re-fill.debounce :as debounce]))

To schedule an event to the future, the event `:re-fill/debouncecan be used. Here's an example of how to schedule the ``:re-fill/notify``` with a one second timeout.

tton.controls__button
n-click (fn [_] (rf/dispatch [:re-fill/debounce
                              ;; The :id is used to identify the scheduled
                              ;; event. If :id is not supplied, the id of
                              ;; the event will be used.
                              {:id :test-notify
                               ;; The actual event that will be dispatched
                               ;; or debounced.
                               :event [:re-fill/notify
                                       {:type :success
                                        :content "From debounce!"}
                                       {:hide-after 3000}]
                               ;; Timeout in ms for scheduling the dispatch
                               ;; to the future.
                               :timeout 1000}]))}
  "Notify with debounce"]

Debounce is more than just `:dispatch-later` built into Re-frame: this scheduled event can be moved further into the future by dispatching

 means that in the example above the ```:re-fill/notify``` event won't be
atched ever if the user keeps clicking the button repeatedly with the less
 one second intervals.

unce is great for dispatching events after the user has stopped doing
thing that results in multiple browser events, such as starting a search
r the user has stopped typing.

e's also a subscription ```:re-fill/debounce``` for getting all the
duled events which have not yet been dispatched. Debounce utility also
ws canceling the dispatch of the event while the timeout is still active
sing the ```:re-fill/stop-debounce``` event.

's an example of a cancel button which is only clickable if the event has
duled

(defn cancel-button [] (let [debounce @(rf/subscribe [:re-fill/debounce])]

[:button.controls__button
 {:on-click (fn [_] (rf/dispatch [:re-fill/stop-debounce :test-notify]))
  :disabled (not (:test-notify debounce))}
 "Stop debounce"]))

l example](https://github.com/metosin/re-fill/tree/master/example-src/example/core.cljs)
more information.

UUIDs

ill provides a co-effect ```:re-fill/uuids``` for injecting UUIDs to
t handlers. In fact, the ```:re-fill/notify``` event handler uses
nternally to generate unique ids for each notification.

to be created. Here's an example of how to use it:

e-fill.uuid must be required

reg-event-fx
ur/event
f/inject-cofx :re-fill/uuids 2)]
:uuids can be destructured from co-effects
 [{:keys [db uuids]} _]
; uuids is a vector of 2 UUIDs
)

Here's how re-fill.notifications utilizes this.

Development

All the files for actual library are located under src directory. There's an example app for development and testing under example-src and example-resources directories.

To get an interactive development environment run:

lein figwheel

and your browser will open automatically at localhost:3449. From there, you get the normal Figwheel development flow.

Releasing
 deploy clojars
TODO
License

Copyright © 2017 Metosin

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


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.