Pointers

Syncs a model or collection with an element. Supports two-way syncs. Respects garbage collection and bottom-up rendering. Reactive.

by Benjamin Lupton

HTML

<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.0/backbone-min.js"></script>
<script src="//wzrd.in/bundle/[email protected]"></script>
<header>
    <h1><a href="https://github.com/bevry/pointers" target="_blank">Pointers</a></h1>
    <p>Syncs a model or collection with an element. Supports two-way syncs. Respects garbage collection and bottom-up rendering. Reactive.</p>
</header><hr/>

<div class="app">
    <ul class="people">
        <li class="person">
            <span class="name"></span>
            <button class="edit">Edit</button>
        </li>
    </ul>
    <div class="person-edit-container">
        <div class="person-edit">
            <input type="text" class="name"></input>
            <button class="delete">Delete</button>
        </div>
    </div>
    <div class="person-add-container">
        <div class="person-add">
            <input type="text" class="name"></input>
            <button class="add">Add</button>
        </div>
    </div>
</div>

JavaScript

var Backbone = window.Backbone;
var Pointer = require('pointers').Pointer;

var PersonModel = Backbone.Model;
var PersonCollection = Backbone.Collection.extend({
    model: PersonModel
});

var PointerView = Backbone.View.extend({
    pointers: null, // becomes an array

    point: function(opts){
        var pointer = new Pointer(opts);
        this.pointers = this.pointers || [];
        this.pointers.push(pointer)
        return pointer;
    },

    destroy: function(){
        this.pointers.forEach(function(pointer){
            pointer.destroy();
        });
        this.pointers = null;
        return this.remove();
    }
});

var PersonView = PointerView.extend({
    el: $('.person').remove().first().prop('outerHTML'),

    events: {
        'click .edit': 'onEdit'
    },

    onEdit: function(){
        this.$el.trigger('editPerson', this.model);
        return this;
    },

    render: function(){
        this.point({
            item: this.model,
            itemAttributes: ['name'],
            element: this.$el.find('.name')
        }).bind();
        return this;
    }
});

var PersonEditView = PointerView.extend({
    el: $('.person-edit').remove().first().prop('outerHTML'),

    events: {
        'click .delete': 'onDelete'
    },
    
    onDelete: function(){
        this.model.destroy();
        this.destroy();
        return this;
    },

    render: function(){
        this.point({
            item: this.model,
            itemAttributes: ['name'],
            element: this.$el.find('.name'),
            itemSetter: true
        }).bind();
        return this;
    }
});

var PersonAddView = PointerView.extend({
    el: $('.person-add').remove().first().prop('outerHTML'),

    events: {
        'click .add': 'onAdd'
    },

    onAdd: function(){
        this.$el.trigger('addPerson', this.model);
        return this;
    },

    render: function(){
        this.point({
            item: this.model,
            itemAttributes: ['name'],
        ...