diff --git a/History.md b/History.md index 40d9ca7..4677a86 100644 --- a/History.md +++ b/History.md @@ -2,6 +2,7 @@ unreleased ========== * unpatch old array when changing arrays + * remove Reactive#bind in favor of passing `bindings` option 1.0.1 / 2014-03-06 ========== diff --git a/lib/each.js b/lib/each.js index 0ccdc95..54fb326 100644 --- a/lib/each.js +++ b/lib/each.js @@ -33,7 +33,8 @@ module.exports = function(el, val) { function childView(el, model) { return Reactive(el, model, { delegate: self.view, - adapter: self.reactive.opt.adapter + adapter: self.reactive.opt.adapter, + bindings: self.reactive.bindings }); } diff --git a/lib/index.js b/lib/index.js index 0fe1212..3db6ac3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -43,9 +43,9 @@ function Reactive(el, model, opt) { self.model = model || {}; self.adapter = (opt.adapter || Adapter)(self.model); self.el = el; - self.view = opt.delegate || {}; + self.view = opt.delegate || Object.create(null); - self.bindings = {}; + self.bindings = opt.bindings || Object.create(null); // TODO undo this crap and just export bindings regularly // not that binding order matters!! @@ -72,6 +72,15 @@ Emitter(Reactive.prototype); Reactive.prototype.sub = function(prop, fn){ var self = this; + debug('subscribe %s', prop); + + // if we have parts, we need to subscribe to the parent as well + // TODO (defunctzombie) multiple levels of properties + var parts = prop.split('.'); + if (parts.length > 1) { + self.sub(parts[0], fn); + } + // for when reactive changes the property this.on('change ' + prop, fn); @@ -257,37 +266,6 @@ Reactive.prototype._bind = function() { }); }; -/** - * Bind `name` to `fn`. - * - * @param {String|Object} name or object - * @param {Function} fn - * @api public - */ - -Reactive.prototype.bind = function(name, fn) { - var self = this; - if ('object' == typeof name) { - for (var key in name) { - this.bind(key, name[key]); - } - return; - } - - var els = query.all('[' + name + ']', this.el); - if (this.el.hasAttribute && this.el.hasAttribute(name)) { - els = [].slice.call(els); - els.unshift(this.el); - } - if (!els.length) return; - - debug('bind [%s] (%d elements)', name, els.length); - for (var i = 0; i < els.length; i++) { - var binding = new Binding(name, this, els[i], fn); - binding.bind(); - } -}; - /** * Destroy the binding * - Removes the element from the dom (if inserted) diff --git a/test/bindings.js b/test/bindings.js index 05d53e8..f1c00aa 100644 --- a/test/bindings.js +++ b/test/bindings.js @@ -6,25 +6,47 @@ var reactive = require('../'); describe('reactive.bind(name, fn)', function(){ it('should define a new binding', function(done){ var el = domify('

Title

'); - var react = reactive(el, {}); - react.bind('data-editable', function(el, url){ - el.setAttribute('contenteditable', 'true'); - assert('/item/12' == url); + var react = reactive(el, {}, { + bindings: { + 'data-editable': function(el, url){ + el.setAttribute('contenteditable', 'true'); + assert('/item/12' == url); + } + } }); assert(el.children[0].getAttribute('contenteditable')); done(); }) + + it('should pass bindings onto each', function(done){ + var el = domify('

'); + var model = { + todos: [ { title: 'test title' } ] + }; + var react = reactive(el, model, { + bindings: { + 'lowercase': function(el, prop){ + var binding = this; + assert(prop === 'title'); + var val = binding.value(prop); + assert(val === 'test title'); + done(); + } + } + }); + }); }) describe('reactive.bind(obj)', function(){ it('should define several bindings', function(done){ var el = domify('

Title

'); - var react = reactive(el, {}); - react.bind({ - hello: function(el, val){ - assert('world' == val); - done(); + var react = reactive(el, {}, { + bindings: { + 'hello': function(el, val){ + assert('world' == val); + done(); + } } }); }) @@ -33,34 +55,24 @@ describe('reactive.bind(obj)', function(){ describe('Reactive#bind(name, fn)', function(){ it('should initialize a view-specific binding', function(done){ var el = domify(''); - var view = reactive(el, {}); - - view.bind('removable', function(el){ - assert('LI' == el.nodeName); - done(); + var view = reactive(el, {}, { + bindings: { + 'removable': function(el){ + assert('LI' == el.nodeName); + done(); + } + } }); }) it('should support root-level bindings', function(done){ var el = domify(''); - var view = reactive(el, {}); - - view.bind('removable', function(el){ - assert('UL' == el.nodeName); - done(); - }); - }) -}) - -describe('Reactive#bind(obj)', function(){ - it('should initialize several view-specific bindings', function(done){ - var el = domify('
'); - var view = reactive(el, {}); - - view.bind({ - autosubmit: function(el){ - assert('/login' == el.getAttribute('action')); - done(); + var view = reactive(el, {}, { + bindings: { + 'removable': function(el){ + assert('UL' == el.nodeName); + done(); + } } }); }) diff --git a/test/text-interpolation.js b/test/text-interpolation.js index ea2f673..a80f993 100644 --- a/test/text-interpolation.js +++ b/test/text-interpolation.js @@ -134,4 +134,13 @@ describe('text interpolation', function(){ var view = reactive('

Hello {name.slice(0,4)}

', model); assert('Hello Some' == view.el.textContent); }) + + it('should support setting base property', function(){ + var model = { name: { first: 'foobar' } }; + var view = reactive('

{name.first}

', model); + assert('foobar' == view.el.textContent); + + view.set('name', { first: 'baz' }); + assert('baz' == view.el.textContent); + }) })