• Stars
    star
    385
  • Rank 111,464 (Top 3 %)
  • Language
    JavaScript
  • Created about 12 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Tiny reactive template engine

reactive Build Status

Simple and Flexible template and view binding engine with support for custom bindings and real-time updates on model changes.

Installation

With component:

$ component install component/reactive

With npm via browserify:

$ npm install reactive

Quickstart

Rendering a basic html template with a predefined data model.

var view = reactive('<p>Hello {name}!</p>', {
  name: 'Adam'
});

// you can add the view "element" to the html whenever you want
// view.el contains the html element
document.body.appendChild(view.el);
<p>Hello Adam!</p>

Handling events

Reactive provides an easy way to register handlers for dom events via predefined "bindings".

var handlers = {
  clickme: function(ev) {
    // console.log('button clicked');
  }
};

var template = '<button on-click="clickme">clickme</button>';
var view = reactive(template, {}, {
  delegate: handlers
});

A recommended approach is to wrap the reactive instance inside of your own View classes. See the Views example.

Iteration

Iteration is achieved by using the each binding on the element you wish to iterate.

var template = '<ul><li each="people">{this}</li></ul>';
var model = {
  people: ['Sally', 'Billy']
};

var view = reactive(template, model);
<ul>
  <li>Sally</li>
  <li>Billy</li>
</ul>

You can push (pop, shift, etc) to the array and the view will be updated accordingly.

model.people.push('Eve');
<ul>
  <li>Sally</li>
  <li>Billy</li>
  <li>Eve</li>
</ul>

Hiding and showing elements

DOM elements can be shown or hidden via the data-visible and data-hidden bindings.

Using the following html template.

var tmpl = '<p data-hidden="items.length">no items</p>' +
  '<ul data-visible="items.length"><li each="items">{this}</li></ul>';
var model = { items: [] };
var view = reactive(tmpl, model);

When rendering the above, we will see no items, because the array is empty.

model.items.push('one');

Will change the output to · one and hide no items. Notice how data-visible and data-hidden act in opposite directions.

API

reactive(string | element, model, [options])

Create a new reactive instance using string or element as the template and model as the data object. This binds a DOM element to a model.

If you do not have a data model and want to specify options, you can pass null or {}. Remember you must have this argument before the options argument.

Options

option type description
delegate object, instance an object or instance defining overrides and handlers for properties and events
adapter function defines how reactive will interact with the model to listen for changes
bindings object define custom bindings (see bindings docs below)

Bind object to the given element with optional view object. When a view object is present it will be checked first for overrides, which otherwise delegate to the model object.

set(prop, val)

Set the property prop to the given value val in the view.

set({prop: val})

Set multiple properties prop and given values val in the view.

get(prop)

Get the value for property prop.

bind(name, fn)

Recommend using bindings option during construction instead. Will be removed in the future.

Create a new binding called name defined by fn. See the writing bindings section for details.

use(fn)

Use a reactive plugin. fn is invoked immediately and passed the reactive instance.

destroy()

Destroy the reactive instance. This will remove all event listeners on the instance as well as remove the element from the dom.

Fires a destroyed event upon completion.

Model Adapters

Model Adapters provide the interface for reactive to interact with your model implementation. By using a custom adapter you can support models from backbone.js, modella, bamboo, etc..

You can make reactive compatible with your favorite model layer by creating a custom adapter. Changes to your model will cause the reactive view to update dynamically. The following API is required for all adapters.

constructor

The adapter option is a function which accepts one argument, the model and should return an instance with all of the adapter methods implemented. The constructor will be called as a function - without the new keyword.

As an example, the builtin adapter's constructor is:

function Adapter(model) {
  if (!(this instanceof Adapter)) {
    return new Adapter(model);
  }

  var self = this;
  self.model = model;
};

In addition to the constructor, the adapter must implement these methods:

  • subscribe
  • unsubscribe
  • unsubscribeAll
  • set
  • get

subscribe(prop, fn)

Subscribe to changes for the given property. When the property changes, fn should be called.

Adapter.prototype.subscribe = function(prop, fn) { ... };

unsubscribe(prop, fn)

Unsubscribe from changes for the given property. The fn should no longer be called on property changes for prop.

unsubscribeAll

Unsubscribe all property change events. Used when a reactive instance is being torn down.

set(prop, val)

Set the property prop to the given value val.

get(prop)

Get the value for property prop

Stock Adapters

Plugins

Custom bindings to extend reactive are listed on the plugins wiki page

Interpolation

Bindings may be applied via interoplation on attributes or text. For example here is a simple use of this feature to react to changes of an article's .name property:

<article>
  <h2>{name}</h2>
</article>

Text interpolation may appear anywhere within the copy, and may contain complex JavaScript expressions for defaulting values or other operations.

<article>
  <h2>{ name || 'Untitled' }</h2>
  <p>Summary: { body.slice(0, 10) }</p>
</article>

Reactive is smart enough to pick out multiple properties that may be used, and react to any of their changes:

<p>Welcome { first + ' ' + last }.</p>

Interpolation works for attributes as well, reacting to changes as you'd expect:

<li class="file-{id}">
  <h3>{filename}</h3>
  <p><a href="/files/{id}/download">Download {filename}</a></p>
<li>

Declarative Bindings

By default reactive supplies bindings for setting properties, listening to events, toggling visibility, appending and replacing elements. Most of these start with "data-*" however this is not required.

data-text

The data-text binding sets the text content of an element.

data-html

The data-html binding sets the inner html of an element.

data-<attr>

The data-<attr> bindings allows you to set an attribute:

<a data-href="download_url">Download</a>

each

The each binding allows you to iterate a collection of objects within the model:

<ul>
  <li each="children">{name}</li>
</ul>

The model is expected to have a children property whose value is an array.

on-<event>

The on-<event> bindings allow you to listen on an event:

<li data-text="title"><a on-click="remove">x</a></li>

remove is expected to be a method on the specified delegate object:

var delegate = {
  remove: function(ev) {
    console.log('Removing thing!');
    ...
  }
}

reactive(template, model, {
  delegate: delegate
});

data-append

The data-append binding allows you to append an existing element:

<div class="photo" data-append="histogram"></div>

The histogram property on the model is expected to contain a DOM element.

data-replace

The data-replace binding allows you to replace an existing element, and carryover its attributes:

<div class="photo" data-replace="histogram"></div>

The histogram property on the model is expected to contain a DOM element.

data-{visible,hidden}

The data-visible and data-hidden bindings conditionally add "visible" or "hidden" classnames so that you may style an element as hidden or visible.

<p data-visible="hasDescription" data-text="truncatedDescription"></p>

data-visible will add a visible class if the property is truthy. For arrays, use the .length property to trigger on empty or non-empty arrays.

data-hidden is the opposite of visible and will add a visibile class if the value is false and .hidden class if the value is truthy.

data-checked

Toggles checkbox state:

<input type="checkbox" data-checked="agreed_to_terms">

data-selected

Toggles option state:

<option data-selected="selected"></option>

Writing bindings

To author bindings, simply create a function that will accept two arguments, the element and binding value. For example, here is a binding which removes an element when truthy:

function removeIf(el, property){
  var binding = this;
  binding.change(function() {
    if (binding.value(property)) {
      el.parentNode.removeChild(el);
    }
  });
};

var template = '<span remove-if="name">no name</span>';
var view = reactive(template, { name: 'foobar' }, {
 bindings: {
  'remove-if': removeIf
 }
});

Notice that you can call the binding whatever you want when you create your view allowing you to select appropriate names. Binding authors should recommend names that make sense.

Here is another binding which uses momentjs to pretty print a javascript date.

var template = '<span moment="timestamp" format="MMM Do YY"></span>';
var view = reactive(template, { timestamp: new Date() }, {
 bindings: {
  'moment': momentFormat
 }
});

function momentFormat(el, property) {
  var binding = this;
  var format = el.getAttribute('format');
  binding.change(function () {
     var val = binding.value(property);
     el.innerText = moment(val).format(format);
  });
};

Would output the following html

<span>Mar 3rd 14</span>

You can easily re-use such bindings by making them plugins and enabling them on your instance with .use()

Interpolation

Some bindings such as data-text and data-<attr> support interpolation. These properties are automatically added to the subscription, and react to changes:

<a data-href="/download/{id}" data-text="Download {filename}"></a>

Notes

Get creative! There's a lot of application-specific logic that can be converted to declarative Reactive bindings. For example here's a naive "auto-submit" form binding:

<div class="login">
  <form action="/user" method="post" autosubmit>
    <input type="text" name="name" placeholder="Username" />
    <input type="password" name="pass" placeholder="Password" />
    <input type="submit" value="Login" />
  </form>
</div>
var reactive = require('reactive');

var view = reactive(document.querySelector('.login'), {}, {
 bindings: {
  autosubmit: autosubmit
 }
});

function autosubmit(el){
  el.onsubmit = function(e){
    e.preventDefault();
    var path = el.getAttribute('action');
    var method = el.getAttribute('method').toUpperCase();
    console.log('submit to %s %s', method, path);
  }
};

View patterns

Typically a view object wraps a model to provide additional functionality, this may look something like the following:

function UserView(user) {
  this.user = user;
  this.view = reactive(tmpl, user, {
    delegate: this
  });
}

UserView.prototype.clickme = function(ev){ ... }

Often a higher-level API is built on top of this pattern to keep things DRY but this is left to your application / other libraries.

For more examples view the ./examples directory.

Run examples and tests

$ git clone https://github.com/component/reactive.git
$ cd reactive
$ npm i
$ make
$ open examples

$ make test

License

MIT

More Repositories

1

debounce

Debounce functions. Useful for implementing behavior that should only happen after a repeated action has completed.
JavaScript
609
star
2

textarea-caret-position

xy coordinates of a textarea or input's caret
JavaScript
590
star
3

emitter

Event emitter component
JavaScript
582
star
4

escape-html

Escape string for use in HTML
JavaScript
430
star
5

dom

DOM traversal, manipulation and events aggregate library (like jQuery)
JavaScript
227
star
6

domify

html -> elements
JavaScript
201
star
7

ease

Easing functions for canvas etc
JavaScript
136
star
8

rope

Efficient data structure for large mutable strings.
JavaScript
134
star
9

merge-descriptors

Merge objects using descriptors
JavaScript
124
star
10

model

Minimalistic extensible data models
JavaScript
122
star
11

component.github.io

components search using component-crawler
JavaScript
119
star
12

infinity

infinite scrolling with loading and unloading.
HTML
105
star
13

tip

Tooltips with a nice flexible API
JavaScript
96
star
14

scroll-to

Smooth window scrolling with requestAnimationFrame and the Tween component
JavaScript
89
star
15

throttle

Throttle function calls
JavaScript
77
star
16

calendar

Calendar UI component
JavaScript
74
star
17

todo

Todo list example application using components and Express for the backend
JavaScript
73
star
18

type

Type assertions aka less-broken `typeof`
JavaScript
69
star
19

cookie

Cookie component
JavaScript
69
star
20

to-function

Convert property access strings to a function ("user.name.first" etc)
JavaScript
64
star
21

swipe

Swipe component with touch support (for image carousels, dynamic content etc)
HTML
61
star
22

enumerable

Enumerable mixin
JavaScript
57
star
23

pinch

pinch in and out on elements on handheld devices
JavaScript
56
star
24

classes

Cross-browser element class manipulation
JavaScript
52
star
25

dialog

Dialog component
JavaScript
52
star
26

path-to-regexp

DEPRECATED use https://github.com/pillarjs/path-to-regexp
JavaScript
52
star
27

focus

Image focal point detection algorithm
JavaScript
51
star
28

url

url parsing utility
JavaScript
47
star
29

regexps

Collection of regular expressions (urlsafe, url, email, credit card, ....)
46
star
30

audio

Sleek radial audio player skin for the <audio> tag
JavaScript
46
star
31

trace

Client-side tracing for performance analysis
JavaScript
46
star
32

s3

Upload files to s3 from the client
JavaScript
45
star
33

autoscale-canvas

Retina-enable an HTML Canvas element
JavaScript
45
star
34

histogram

Create JavaScript image histograms with Canvas
JavaScript
45
star
35

network

Measure network latency to make dynamic adjustments to content
JavaScript
40
star
36

upload

file upload and progress api
JavaScript
39
star
37

xhr-image

XHR2 driven images for progress events
JavaScript
39
star
38

tween

Motion tween engine using "ease"
HTML
36
star
39

notification

growl-style notifications for the browser
JavaScript
36
star
40

events

High level dom node event management (with delegation support)
JavaScript
35
star
41

event

Event binding component
JavaScript
35
star
42

raf

requestAnimationFrame
JavaScript
34
star
43

clipboard-dom

Makes a DOM element (i.e. <button>) write to the system clipboard
JavaScript
34
star
44

piecon

Pie favicons -- great for upload progress etc
JavaScript
34
star
45

querystring

Simple key / value pair query-string parser
JavaScript
34
star
46

delegate

Event delegation component
HTML
34
star
47

in-viewport

Check if an element is in the viewport
JavaScript
33
star
48

autosuggest

Autosuggest values for text inputs
JavaScript
31
star
49

router

Simple client-side router
JavaScript
31
star
50

drop

Drag and drop file uploads a single normalized event
JavaScript
31
star
51

progress

Circular progress indicator using Canvas
JavaScript
30
star
52

css

DOM element css helper
JavaScript
30
star
53

fullscreen

Fullscreen api wrapper
JavaScript
29
star
54

github-buttons

Script version of @mdo's Github Buttons
CSS
28
star
55

per-frame

throttle per animation frame
JavaScript
28
star
56

schema

A simple, fluent API for generating immutable schemas.
JavaScript
28
star
57

selectable

Selectable DOM elements
JavaScript
26
star
58

onload

Add onload transitions to DOM elements (fade in images etc)
JavaScript
26
star
59

humanize-number

Humanize a number 1000000.99 -> 1,000,000.99
JavaScript
26
star
60

queue

function job queue with concurrency and timeout support
JavaScript
25
star
61

dropload

Drag and drop uploads
JavaScript
25
star
62

datepicker

Datepicker component built on Calendar
JavaScript
24
star
63

query

Query the DOM with selector engine fallback support
JavaScript
24
star
64

overlay

Page overlay component
JavaScript
23
star
65

menu

Menu component
JavaScript
23
star
66

file-picker

File picker component
HTML
22
star
67

clipboard

Clipboard API wrapper
JavaScript
21
star
68

bind

Function binding utility
JavaScript
21
star
69

spin

Higher level spinner api built on component/spinner - positions and scales automatically within target element
JavaScript
21
star
70

drop-anywhere

Drag and drop a file anywhere to upload
JavaScript
21
star
71

ie

get the running version of IE without UA sniffing.
JavaScript
20
star
72

has-cors

Detects support for Cross-Origin Resource Sharing
JavaScript
20
star
73

touchit

multi-touch event simulation in the browser
JavaScript
20
star
74

array-equal

check if two arrays are equal
JavaScript
20
star
75

worker

Nicer web worker API
JavaScript
20
star
76

convolve

Canvas convolution filters
JavaScript
19
star
77

humanize-keys

Humanize keys (command -> ⌘)
JavaScript
19
star
78

popover

Popover component built on top of Tip
HTML
19
star
79

channel

two-sided event emitter with support for middleware
JavaScript
19
star
80

is-near-bottom

Check if the document is scrolled near the bottom
JavaScript
19
star
81

clone

object deep clone component
JavaScript
18
star
82

tap-event

Create tap event listeners
JavaScript
17
star
83

set

Set container
JavaScript
17
star
84

color-parser

CSS color string parser
JavaScript
17
star
85

dropdown

Dropdown menu
JavaScript
16
star
86

gesture

Multi-touch gesture sugar layer on top of hammer.js
JavaScript
16
star
87

favicon

Dynamic favicon library via data uris (canvas.toDataURL() etc)
JavaScript
16
star
88

collection

Enumerable data model collections
JavaScript
16
star
89

route

Route implementation for client-side routers
JavaScript
16
star
90

inherit

https://github.com/component/inherits
JavaScript
16
star
91

t

translation utility
JavaScript
16
star
92

sparkline

Tiny sparkline canvas graphs
JavaScript
15
star
93

value

get / set form input values
JavaScript
15
star
94

noticon

Notification bubble favicons
JavaScript
15
star
95

indexof

Thanks microsoft
JavaScript
15
star
96

thumb

Scale an image or data uri within the given dimensions
JavaScript
14
star
97

flipbox

Double-sided flip box using css transformations.
JavaScript
14
star
98

removed

Invoke a callback when a DOM element is removed from the document
JavaScript
14
star
99

dev

Associate objects with DOM elements in development for easy inspection
JavaScript
14
star
100

matches-selector

Check if an element matches a given selector string
JavaScript
14
star