Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
NodeBind: oneTime.
Browse files Browse the repository at this point in the history
This patch changes the call signature of bind(), which now takes: name, value, oneTime.

oneTime is expected to be true if the caller wishes the bound value updated but doesn't require it to track an observable. if oneTime is true, it is expected that value will NOT be an observable, otherwise it will be.

R=arv
BUG=

Review URL: https://codereview.appspot.com/43700043
  • Loading branch information
rafaelw committed Dec 18, 2013
1 parent 5c904e7 commit b9266f7
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 34 deletions.
85 changes: 51 additions & 34 deletions src/NodeBind.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,16 @@
};
}

Text.prototype.bind = function(name, observable) {
Text.prototype.bind = function(name, value, oneTime) {
if (name !== 'textContent')
return Node.prototype.bind.call(this, name, observable);
return Node.prototype.bind.call(this, name, value, oneTime);

if (oneTime)
return updateText(this, value);

unbind(this, 'textContent');
updateText(this, observable.open(textBinding(this)));
return this.bindings.textContent = observable;
updateText(this, value.open(textBinding(this)));
return this.bindings.textContent = value;
}

function updateAttribute(el, name, conditional, value) {
Expand All @@ -130,19 +133,21 @@
};
}

Element.prototype.bind = function(name, observable) {
Element.prototype.bind = function(name, value, oneTime) {
var conditional = name[name.length - 1] == '?';
if (conditional) {
this.removeAttribute(name);
name = name.slice(0, -1);
}

unbind(this, name);
if (oneTime)
return updateAttribute(this, name, conditional, value);

unbind(this, name);
updateAttribute(this, name, conditional,
observable.open(attributeBinding(this, name, conditional)));
value.open(attributeBinding(this, name, conditional)));

return this.bindings[name] = observable;
return this.bindings[name] = value;
};

var checkboxEventType;
Expand Down Expand Up @@ -269,35 +274,42 @@
}
}

HTMLInputElement.prototype.bind = function(name, observable) {
HTMLInputElement.prototype.bind = function(name, value, oneTime) {
if (name !== 'value' && name !== 'checked')
return HTMLElement.prototype.bind.call(this, name, observable);
return HTMLElement.prototype.bind.call(this, name, value, oneTime);

unbind(this, name);
this.removeAttribute(name);

this.removeAttribute(name);
var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
var postEventFn = name == 'checked' ? checkedPostEvent : noop;
bindInputEvent(this, name, observable, postEventFn);

if (oneTime)
return updateInput(this, name, value, sanitizeFn);

unbind(this, name);
bindInputEvent(this, name, value, postEventFn);
updateInput(this, name,
observable.open(inputBinding(this, name, sanitizeFn)),
value.open(inputBinding(this, name, sanitizeFn)),
sanitizeFn);

return this.bindings[name] = observable;
return this.bindings[name] = value;
}

HTMLTextAreaElement.prototype.bind = function(name, observable) {
HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
if (name !== 'value')
return HTMLElement.prototype.bind.call(this, name, observable);
return HTMLElement.prototype.bind.call(this, name, value, oneTime);

unbind(this, 'value');
this.removeAttribute('value');

bindInputEvent(this, 'value', observable);
if (oneTime)
return updateInput(this, 'value', value);

unbind(this, 'value');
bindInputEvent(this, 'value', value);
updateInput(this, 'value',
observable.open(inputBinding(this, 'value', sanitizeValue)));
value.open(inputBinding(this, 'value', sanitizeValue)));

return this.bindings.value = observable;
return this.bindings.value = value;
}

function updateOption(option, value) {
Expand Down Expand Up @@ -326,16 +338,19 @@
}
}

HTMLOptionElement.prototype.bind = function(name, observable) {
HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
if (name !== 'value')
return HTMLElement.prototype.bind.call(this, name, observable);
return HTMLElement.prototype.bind.call(this, name, value, oneTime);

unbind(this, 'value');
this.removeAttribute('value');

bindInputEvent(this, 'value', observable);
updateOption(this, observable.open(optionBinding(this)));
return this.bindings.value = observable;
if (oneTime)
return updateOption(this, value);

unbind(this, 'value');
bindInputEvent(this, 'value', value);
updateOption(this, value.open(optionBinding(this)));
return this.bindings.value = value;
}

function updateSelect(select, property, value, retries) {
Expand All @@ -356,20 +371,22 @@
}
}

HTMLSelectElement.prototype.bind = function(name, observable) {
HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
if (name === 'selectedindex')
name = 'selectedIndex';

if (name !== 'selectedIndex' && name !== 'value')
return HTMLElement.prototype.bind.call(this, name, observable);
return HTMLElement.prototype.bind.call(this, name, value, oneTime);


unbind(this, name);
this.removeAttribute(name);

bindInputEvent(this, name, observable);
updateSelect(this, name, observable.open(selectBinding(this, name)), 2);
return this.bindings[name] = observable;
if (oneTime)
return updateSelect(this, name, value);

unbind(this, name);
bindInputEvent(this, name, value);
updateSelect(this, name, value.open(selectBinding(this, name)), 2);
return this.bindings[name] = value;
}

// TODO(rafaelw): We should polyfill a Microtask Promise and define it if it isn't.
Expand Down
58 changes: 58 additions & 0 deletions tests/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ suite('Text bindings', function() {
// TODO(rafaelw): Throw on binding to unavailable property?
});

test('oneTime', function() {
var text = document.createTextNode('hi');
text.bind('textContent', 1, true);
assert.strictEqual('1', text.data);
});

test('No Path', function() {
var text = testDiv.appendChild(document.createTextNode('hi'));
var model = 1;
Expand Down Expand Up @@ -127,6 +133,13 @@ suite('Element attribute bindings', function() {
assert.strictEqual('', el.getAttribute('foo'));
});

test('oneTime', function() {
var el = testDiv.appendChild(document.createElement('div'));
var model = {a: '1'};
el.bind('foo', 1, true);
assert.strictEqual('1', el.getAttribute('foo'));
});

test('No path', function() {
var el = testDiv.appendChild(document.createElement('div'));
var model = 1;
Expand Down Expand Up @@ -235,6 +248,12 @@ suite('Form Element Bindings', function() {
inputTextAreaValueTest('input');
});

test('Input.value - oneTime', function() {
var el = testDiv.appendChild(document.createElement('input'));
el.bind('value', 42, true);
assert.strictEqual('42', el.value);
});

test('Input.value - no path', function() {
inputTextAreaNoPath('input');
});
Expand All @@ -247,6 +266,12 @@ suite('Form Element Bindings', function() {
inputTextAreaValueTest('textarea');
});

test('TextArea.value - oneTime', function() {
var el = testDiv.appendChild(document.createElement('textarea'));
el.bind('value', 42, true);
assert.strictEqual('42', el.value);
});

test('TextArea.value - no path', function() {
inputTextAreaNoPath('textarea');
});
Expand Down Expand Up @@ -325,6 +350,14 @@ suite('Form Element Bindings', function() {
assert.isFalse(model.x);
});

test('(Checkbox)Input.checked - oneTime', function() {
var input = testDiv.appendChild(document.createElement('input'));
testDiv.appendChild(input);
input.type = 'checkbox';
input.bind('checked', true, true);
assert.isTrue(input.checked);
});

test('(Checkbox)Input.checked - path unreachable', function() {
var input = testDiv.appendChild(document.createElement('input'));
testDiv.appendChild(input);
Expand Down Expand Up @@ -430,6 +463,13 @@ suite('Form Element Bindings', function() {
assert.isTrue(model.x);
});

test('(Radio)Input.checked - oneTime', function() {
var input = testDiv.appendChild(document.createElement('input'));
input.type = 'radio';
input.bind('checked', true, true);
assert.isTrue(input.checked);
});

test('(Radio)Input.checked - path unreachable', function() {
var input = testDiv.appendChild(document.createElement('input'));
input.type = 'radio';
Expand Down Expand Up @@ -594,6 +634,18 @@ test('(Radio)Input.checked - multiple forms - ShadowRoot', function() {
assert.strictEqual(1, model.val);
});

test('Select.selectedIndex - oneTime', function() {
var select = testDiv.appendChild(document.createElement('select'));
testDiv.appendChild(select);
var option0 = select.appendChild(document.createElement('option'));
var option1 = select.appendChild(document.createElement('option'));
var option2 = select.appendChild(document.createElement('option'));

select.bind('selectedIndex', 2, true);
Platform.performMicrotaskCheckpoint();
assert.strictEqual(2, select.selectedIndex);
});

test('Select.selectedIndex - path NaN', function() {
var select = testDiv.appendChild(document.createElement('select'));
testDiv.appendChild(select);
Expand Down Expand Up @@ -623,6 +675,12 @@ test('(Radio)Input.checked - multiple forms - ShadowRoot', function() {
assert.strictEqual('Hi', option.value);
});

test('Option.value - oneTime', function() {
var option = testDiv.appendChild(document.createElement('option'));
option.bind('value', 42, true);
assert.strictEqual('42', option.value);
});

test('Select.selectedIndex - path unreachable', function() {
var select = testDiv.appendChild(document.createElement('select'));
testDiv.appendChild(select);
Expand Down

0 comments on commit b9266f7

Please sign in to comment.