npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tendon

v0.1.0

Published

Two way data bindings for Backbone and the DOM

Downloads

4

Readme

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.