0.1.0 • Published 9 years ago

tendon v0.1.0

Weekly downloads
2
License
MIT
Repository
github
Last release
9 years ago

Tendon.js

EXPERIMENTAL LIBRARY - NOT PRODUCTION READY

Two way data binding for Backbone and the DOM

Build Status devDependency Status NPM version

Tired of writing tons of repetative Backbone code to update the DOM? Tired of wiring up DOM listeners to update your Backbone models? Well then this might be what you're looking for!

This library has 3 primary purposes.

  1. Update the DOM when the JS data changes
  2. Update the JS when the DOM data changes
  3. Handle templating because thats how I roll

This library takes a slightly different approach for data bindings, in that nearly all of the configuration is in the HTML markup itself. The primary reason for this is that it provided me a way to keep my JS very lightweight and straight forward, as well as allow me to rapidly change the UI/UX of an application without having to rewrite my entire JS app.

It may be somewhat of an anti-pattern to put so much control in HTML, but hey, this library is mainly about rendering and keeping state in sync. If you prefer putting your HTML inside your JS and then embedded JS inside of that HTML, maybe React is for you.

Table of Contents

Install

This is a client side library, but still exports to Node.js if you are doing some fancy backend stuff. Writing the obligatory install steps, but if you've made it this far you probably know what to do here.

With npm

npm install tendon

With bower

bower install tendon

Or download the JS files directly and load into the browser.

<script src="path/to/tendon.js"></script>

Usage

Generally, there shouldn't be much JS interaction with the library, it is meant to do as much as it can automatically for you based on the HTML markup it finds within the element you give it.

var Model = Backbone.Model.extend({
  defaults: {
    menuItems: ['Home', 'About', 'Contact']
  , activeItem: null
  , locked: 'locked'
  }
})

var View = Backbone.View.extend({
  el: 'body'
  
, initialize: function() {
    // All template rendering will use this, as well as any subscribe
    // or publish events specified in the markup
    var context = {
      view: this
    , model: this.model
    , store: window.localStorage
    }

    // The initialize of Tendon is going to find all relevant elements and do 
    // what the markup attributes say
    this.tendon = new Tendon(this.$el, context)

    // You can also listen for specific changes if you want to some extra fancy
    // stuff. Think of things like JSONView or Highlight.js calls.
    this.tendon.on('updateElement:elementID', function($element, content) {

    })

    // Backbone-esque version of above
    this.tendon.on('updateElement', function($element, content) {

    })
  }
})

// Such app creation, new models, wow.
var app = new View({
  model: new Model()
})

Examples

1 Way Binding - Contents

Lets start off small and create a 1 way binding to update the contents of a div. We need to know what events trigger a DOM update, and how to get the data for the update.

  1. Listen to the model event change:username
  2. Update content with the model username property
<div 
  tendon-subscribe="model.change:username" 
  tendon-set="model:username"
></div>

You can specify multiple subscriptions, and use custom functions for the value

<div 
  tendon-subscribe="model.sync,model.change:property,state.change:username change:group" 
  tendon-set="model:someFunction"
></div>

1 Way Binding - Attribute

Here we have an element that we only want to update the class attribute on. This is a one-way binding as there is no tendon-publish attribute and no way to get a value. The attributes here do the following:

  1. Bind content changes to an attribute, class in this case, using tendon-set-attribute
  2. Use the specific value, model:status aka model.get('status'), using tendon-set
  3. Trigger a content update on model.change:locked aka model.on('change:status')

Using this setup, anytime the model's status attribute changes, the DOM element will update its class attribute with that value.

<div 
  tendon-set="model:status" 
  tendon-set-attribute="class"
  tendon-subscribe="model.change:status"
/>

Here we are going to use templates and publishing to setup two way binding.

  1. Listen to the model events; sync, change:menuItems, and change:activeItem
  2. Update the innerHTML content using the results of the template from tendon-template attribute. The template will be called with the context provided on initialization
  3. Listen to all child li element changes via the tendon-listen attribute
  4. Publish any changes found by setting the model:activeItem prop, aka model.set('activeItem')
  5. Trigger an render on initialization via tendon-auto-render
<script type="text/html" id="my-list-template">
  {{ var active = __.model.get('activeItem') }}

  {{ __.model.get('menuItems').forEach(function(item) { }}
    <li {{= (item === cur) ? 'class="active"' : '' }}>{{ item }}</li>
  {{ }) }}
</script>

<ul 
  tendon-subscribe="model.sync, model.change:menuItems, model.change:activeItem"
  tendon-auto-render="true"
  tendon-template="script#my-list-template"
  tendon-listen="li"
  tendon-publish="model:activeItem"
><!-- filed in by Tendon using template above --></ul>

HTML Attribute Options

  • tendon-subscribe {String} list of model events to listen to in the format model.event:property
  • tendon-publish {String} list of model events to publish HTML changes to
  • tendon-auto-render {Boolean} flag to signal initial rendering call, use if the data already exists in the context and the page was not bootstrapped with content
  • tendon-template {String} jQuery selector for underscore template, if set this will be run with the provided context and set as the inner HTML
  • tendon-set {String} model attribute or method to be used as direct value
  • tendon-set-attribute {String} element attribute to assign value to, innerHTML is set if not specified
  • tendon-listen {String} jQuery selector to specify a child element(s) to listen for changes, instead of the current element
  • tendon-uuid {String} internally set UUID to identify source of HTML update events

Events

  • init:before
  • init
  • init:after
  • change:html:id
  • change:html
  • change:js

API

The methods here are mainly all to be used internally, they aren't named fairly well at the moment and will most likely be changed.

new Tendon($selector, context, options)

Class constructor, automatically creates all DOM and Backbone event bindings for any element found within the $selector containing tendon- attributes.

  • $selector - jQuery element or string selector, this is the DOM context
  • context - JS context object
  • options - option overrides, merged with Tendon.defaults described below
var App = Backbone.View.extend({
  initialize: function() {
    var context = {
      view: this
    }
    this.tendon = new Tendon(this.$el, context)
  }
})

Tendon.defaults

  • prefix - HTML attribute prefix
  • debug - boolean to toggle debug logging
  • templateSettings - custom underscore template settings
{
  prefix: 'tendon-'
, debug: false
, templateSettings: {
    variable:    '__'
  , evaluate:    /\{\{(.+?)\}\}/g
  , interpolate: /\{\{=(.+?)\}\}/g
  , escape:      /\{\{-(.+?)\}\}/g
  }
}

instance.setup()

Search the current $selector for any elements with attributes matching any of the adapters in use. Create event proxies to run the adapters.

tendon.setup()

Adapters

get

  • event - init:before
  • attr - tendon-get
  • value - any jQuery object method or prop. (ex: data, val, html)
  • autoload - true

Description

template

  • event - change:js
  • attr - tendon-template
  • value - jQuery selector of underscore template
  • autoload - true

Description

set

  • event - change:js
  • related - set-attribute
  • attr - tendon-set
  • value - context method or property (ex: obj.prop, model.get:prop, obj.method:arg1,arg2)
  • autoload - true

Description

set-attribute

  • required adapter - set
  • event - change:js
  • attr - tendon-set
  • value - element attribute (ex: class, data-thing, selected)
  • autoload - true

Description

auto-render

  • required adapter - set or template
  • event - init:after
  • attr - tendon-auto-render

Description

uuid

  • event - init
  • attr - tendon-uuid
  • value - underscore uuid with prefix (ex: tendon-25)
  • autoload - true

Description

publish

  • event - change:html
  • attr - tendon-publish
  • value -

Description

listen

  • event - init
  • attr - tendon-listen
  • value - child element selector (optional, default current el)
  • autoload - true

Description

subscribe

  • event - init
  • attr - tendon-subscribe
  • value -
  • autoload - true

Description

License

(The MIT License)

Copyright (c) 2015 Beau Sorensen

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.