From 7203c105c194d4d554c4f567c78c437dea8726eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Am=C3=A9rico?= Date: Sun, 7 Aug 2016 19:31:01 -0300 Subject: [PATCH] Change scope resolution to use prototype like approach instead of copying properties to children scopes. Use $index instead of index for iteration index property name --- lib/sightglass.js | 23 ++++++++++++++++++++++- spec/rivets/binders.js | 14 ++++++++++++-- spec/rivets/functional.js | 19 ++++--------------- src/binders.js | 36 +++++++++++++++--------------------- 4 files changed, 53 insertions(+), 39 deletions(-) diff --git a/lib/sightglass.js b/lib/sightglass.js index cc30657fc..7eee3e214 100644 --- a/lib/sightglass.js +++ b/lib/sightglass.js @@ -11,12 +11,12 @@ function Observer(obj, keypath, callback, options) { this.options = options || {} this.options.adapters = this.options.adapters || {} - this.obj = obj this.keypath = keypath this.callback = callback this.objectPath = [] this.update = this.update.bind(this) this.parse() + this.obj = this.getRootObject(obj) if (isObject(this.target = this.realize())) { this.set(true, this.key, this.target, this.callback) @@ -191,6 +191,27 @@ this.set(false, this.key, this.target, this.callback) } } + // traverse the scope chain to find the scope which has the root property + // if the property is not found in chain, returns the root scope + Observer.prototype.getRootObject = function (obj) { + var rootProp, current; + if (!obj.$parent) { + return obj; + } + + if (this.tokens.length) { + rootProp = this.tokens[0].path + } else { + rootProp = this.key.path + } + + current = obj; + while (current.$parent && (current[rootProp] === undefined)) { + current = current.$parent + } + + return current; + } // Check if a value is an object than can be observed. function isObject(obj) { diff --git a/spec/rivets/binders.js b/spec/rivets/binders.js index af61d6b70..b8093ab83 100644 --- a/spec/rivets/binders.js +++ b/spec/rivets/binders.js @@ -125,8 +125,8 @@ describe("Rivets.binders", function() { }; }); - it.skip("lets you access index from current and parent scope", function() { - nestedEl.textContent = '{%item%}-{%nested%}'; + it("lets you access index from current and parent scope", function() { + nestedEl.textContent = '{$parent.$index}-{$index}'; var view = rivets.bind(el, model); Should(fragment.childNodes[1].childNodes[1].textContent).be.exactly('0-0'); @@ -151,6 +151,16 @@ describe("Rivets.binders", function() { Should(fragment.childNodes[1].childNodes[2].textContent).be.exactly('New!Level 1 - 0'); Should(fragment.childNodes[2].childNodes[2].textContent).be.exactly('New!Level 1 - 1'); }); + + it("reflects changes when an undefined property is set in root scope", function() { + nestedEl.textContent = '{unset}'; + var view = rivets.bind(el, model); + model.unset = 'NotUndefined'; + Should(fragment.childNodes[1].childNodes[1].textContent).be.exactly('NotUndefined'); + Should(fragment.childNodes[1].childNodes[2].textContent).be.exactly('NotUndefined'); + Should(fragment.childNodes[2].childNodes[2].textContent).be.exactly('NotUndefined'); + }); + }); describe("if", function() { diff --git a/spec/rivets/functional.js b/spec/rivets/functional.js index 7b2874f6d..6993baf5b 100644 --- a/spec/rivets/functional.js +++ b/spec/rivets/functional.js @@ -232,28 +232,17 @@ describe('Functional', function() { el.getElementsByTagName('li')[3].textContent.should.equal('last') }); - it.skip('should allow binding to the iterated element index', function() { - listItem.setAttribute('data-index', 'index'); + it('should allow binding to the iterated element index', function() { + listItem.setAttribute('data-index', '$index'); rivets.bind(el, bindData); el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0'); el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1') }); - it.skip('should allow binding to the iterated element index by using the %item% syntax', function() { - listItem.setAttribute('data-index', '%item%'); - rivets.bind(el, bindData); - el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0'); - el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1') - }); - - it.skip('should allow the developer to configure the index attribute available in the iteration', function() { - rivets.configure({ - iterationAlias : function(modelName) { - return modelName + 'Index'; - } - }); + it('should allow the developer to configure the index attribute available in the iteration', function() { listItem.setAttribute('data-index', 'itemIndex'); + listItem.setAttribute('index-property', 'itemIndex'); rivets.bind(el, bindData); el.getElementsByTagName('li')[0].getAttribute('index').should.equal('0'); el.getElementsByTagName('li')[1].getAttribute('index').should.equal('1') diff --git a/src/binders.js b/src/binders.js index e2314a230..278226e3a 100644 --- a/src/binders.js +++ b/src/binders.js @@ -143,7 +143,7 @@ const binders = { priority: 4000, bind: function(el) { - if (!defined(this.marker)) { + if (!this.marker) { let attr = [this.view.prefix, this.type].join('-').replace('--', '-') let declaration = el.getAttribute(attr) @@ -157,7 +157,7 @@ const binders = { }, unbind: function() { - if (defined(this.nested)) { + if (this.nested) { this.nested.unbind() this.bound = false } @@ -166,16 +166,11 @@ const binders = { routine: function(el, value) { if (!!value === !this.bound) { if (value) { - let models = {} - Object.keys(this.view.models).forEach(key => { - models[key] = this.view.models[key] - }) - - if (defined(this.nested)) { + if (this.nested) { this.nested.bind() } else { - this.nested = rivets.bind(el, models, this.view.options()) + this.nested = rivets.bind(el, this.view.models, this.view.options()) } this.marker.parentNode.insertBefore(el, this.marker.nextSibling) @@ -189,7 +184,7 @@ const binders = { }, update: function(models) { - if (defined(this.nested)) { + if (this.nested) { this.nested.update(models) } } @@ -224,13 +219,13 @@ const binders = { priority: 1000, unbind: function(el) { - if (defined(this.handler)) { + if (this.handler) { unbindEvent(el, this.args[0], this.handler) } }, routine: function(el, value) { - if (defined(this.handler)) { + if (this.handler) { unbindEvent(el, this.args[0], this.handler) } @@ -245,7 +240,7 @@ const binders = { priority: 4000, bind: function(el) { - if (!defined(this.marker)) { + if (!this.marker) { let attr = [this.view.prefix, this.type].join('-').replace('--', '-') this.marker = document.createComment(` rivets: ${this.type} `) this.iterated = [] @@ -261,7 +256,7 @@ const binders = { }, unbind: function(el) { - if (defined(this.iterated)) { + if (this.iterated) { this.iterated.forEach(view => { view.unbind() }) @@ -271,6 +266,7 @@ const binders = { routine: function(el, collection) { let modelName = this.args[0] let collection = collection || [] + let indexProp = el.getAttribute('index-property') || '$index' if (this.iterated.length > collection.length) { times(this.iterated.length - collection.length, () => { @@ -281,15 +277,11 @@ const binders = { } collection.forEach((model, index) => { - let data = {index: index, $parent: this.model} + let data = {$parent: this.view.models} + data[indexProp] = index data[modelName] = model - if (!defined(this.iterated[index])) { - Object.keys(this.view.models).forEach(key => { - if (!defined(data[key])) { - data[key] = this.view.models[key] - } - }) + if (!this.iterated[index]) { let previous = this.marker @@ -321,6 +313,8 @@ const binders = { update: function(models) { let data = {} + //todo: add test and fix if necessary + Object.keys(models).forEach(key => { if (key !== this.args[0]) { data[key] = models[key]