mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Add two way data binding.
All reflected attributes two way bind on SkyElement, so now doing
<sky-element name="sky-input" attributes="value:string"> is enough
to get two way binding on the value attribute so users doing
<sky-input value="{{ inputValue }}"> will get the inputValue property
updated as the user types.
R=abarth@chromium.org, ojan@chromium.org
Review URL: https://codereview.chromium.org/850383002
This commit is contained in:
parent
8f1c3bacfc
commit
5ef6774cd9
@ -25,7 +25,10 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<sky-input id="text" value="Ready" />
|
||||
<sky-box title='Text'>
|
||||
<sky-input id="text" value="{{ inputValue }}" />
|
||||
<div>value = {{ inputValue }}</div>
|
||||
</sky-box>
|
||||
|
||||
<sky-box title='Buttons'>
|
||||
<sky-button id='button' on-click='handleClick'>Button</sky-button>
|
||||
@ -37,7 +40,8 @@
|
||||
<div><sky-checkbox id='checkbox' />Checkbox</div>
|
||||
<div class="output">highlight: {{ myCheckbox.highlight }}</div>
|
||||
<div class="output">checked: {{ myCheckbox.checked }}</div>
|
||||
<div><sky-checkbox id='checkbox' checked='true'/>Checkbox, default checked.</div>
|
||||
<div><sky-checkbox id='checkbox' checked='{{ checked }}'/>Checkbox, default checked.</div>
|
||||
<div class="output">checked: {{ checked }}</div>
|
||||
</sky-box>
|
||||
|
||||
<sky-box title='Radios'>
|
||||
@ -61,6 +65,8 @@ module.exports = class extends SkyElement {
|
||||
this.myCheckbox = null;
|
||||
this.myText = null;
|
||||
this.clickCount = 0;
|
||||
this.inputValue = "Ready";
|
||||
this.checked = false;
|
||||
}
|
||||
attached() {
|
||||
this.myButton = this.shadowRoot.getElementById('button');
|
||||
@ -70,7 +76,8 @@ module.exports = class extends SkyElement {
|
||||
}
|
||||
handleClick(event) {
|
||||
this.clickCount++;
|
||||
this.myText.value = "Moar clicking " + this.clickCount;
|
||||
this.checked = !this.checked;
|
||||
this.inputValue = "Moar clicking " + this.clickCount;
|
||||
}
|
||||
}.register();
|
||||
</script>
|
||||
|
||||
@ -601,6 +601,9 @@ Observer.prototype = {
|
||||
return this.value_;
|
||||
},
|
||||
|
||||
setValue: function(newValue) {
|
||||
},
|
||||
|
||||
close: function() {
|
||||
if (this.state_ != OPENED)
|
||||
return;
|
||||
@ -887,17 +890,12 @@ CompoundObserver.prototype = createObject({
|
||||
|
||||
function identFn(value) { return value; }
|
||||
|
||||
function ObserverTransform(observable, getValueFn, setValueFn,
|
||||
dontPassThroughSet) {
|
||||
function ObserverTransform(observable, getValueFn) {
|
||||
this.callback_ = undefined;
|
||||
this.target_ = undefined;
|
||||
this.value_ = undefined;
|
||||
this.observable_ = observable;
|
||||
this.getValueFn_ = getValueFn || identFn;
|
||||
this.setValueFn_ = setValueFn || identFn;
|
||||
// TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
|
||||
// at the moment because of a bug in it's dependency tracking.
|
||||
this.dontPassThroughSet_ = dontPassThroughSet;
|
||||
}
|
||||
|
||||
ObserverTransform.prototype = {
|
||||
@ -918,6 +916,9 @@ ObserverTransform.prototype = {
|
||||
this.callback_.call(this.target_, this.value_, oldValue);
|
||||
},
|
||||
|
||||
setValue: function(oldValue) {
|
||||
},
|
||||
|
||||
discardChanges: function() {
|
||||
this.value_ = this.getValueFn_(this.observable_.discardChanges());
|
||||
return this.value_;
|
||||
@ -927,12 +928,6 @@ ObserverTransform.prototype = {
|
||||
return this.observable_.deliver();
|
||||
},
|
||||
|
||||
setValue: function(value) {
|
||||
value = this.setValueFn_(value);
|
||||
if (!this.dontPassThroughSet_ && this.observable_.setValue)
|
||||
return this.observable_.setValue(value);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
if (this.observable_)
|
||||
this.observable_.close();
|
||||
@ -941,7 +936,6 @@ ObserverTransform.prototype = {
|
||||
this.observable_ = undefined;
|
||||
this.value_ = undefined;
|
||||
this.getValueFn_ = undefined;
|
||||
this.setValueFn_ = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -160,6 +160,8 @@ class PropertyDirective {
|
||||
node[name] = value;
|
||||
});
|
||||
}
|
||||
if (typeof node.addPropertyBinding == 'function')
|
||||
node.addPropertyBinding(this.name, observable);
|
||||
return observable;
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,6 +160,8 @@ class SkyElement extends HTMLElement {
|
||||
|
||||
createdCallback() {
|
||||
this.isAttached = false;
|
||||
this.propertyBindings = null;
|
||||
this.dirtyPropertyBindings = null;
|
||||
this.created();
|
||||
|
||||
Object.preventExtensions(this);
|
||||
@ -208,6 +210,8 @@ class SkyElement extends HTMLElement {
|
||||
}
|
||||
|
||||
notifyPropertyChanged(name, oldValue, newValue) {
|
||||
if (oldValue == newValue)
|
||||
return;
|
||||
var notifier = Object.getNotifier(this);
|
||||
notifier.notify({
|
||||
type: 'update',
|
||||
@ -217,6 +221,38 @@ class SkyElement extends HTMLElement {
|
||||
var handler = this[name + 'Changed'];
|
||||
if (typeof handler == 'function')
|
||||
handler.call(this, oldValue, newValue);
|
||||
this.schedulePropertyBindingUpdate(name);
|
||||
}
|
||||
|
||||
addPropertyBinding(name, binding) {
|
||||
if (!this.propertyBindings)
|
||||
this.propertyBindings = new Map();
|
||||
this.propertyBindings.set(name, binding);
|
||||
}
|
||||
|
||||
getPropertyBinding(name) {
|
||||
if (!this.propertyBindings)
|
||||
return null;
|
||||
return this.propertyBindings.get(name);
|
||||
}
|
||||
|
||||
schedulePropertyBindingUpdate(name) {
|
||||
if (!this.dirtyPropertyBindings) {
|
||||
this.dirtyPropertyBindings = new Set();
|
||||
Promise.resolve().then(this.updatePropertyBindings.bind(this));
|
||||
}
|
||||
this.dirtyPropertyBindings.add(name);
|
||||
}
|
||||
|
||||
updatePropertyBindings() {
|
||||
for (var name of this.dirtyPropertyBindings) {
|
||||
var binding = this.getPropertyBinding(name);
|
||||
if (binding) {
|
||||
binding.setValue(this[name]);
|
||||
binding.discardChanges();
|
||||
}
|
||||
}
|
||||
this.dirtyPropertyBindings = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
ERROR: Exception caught during observer callback: ouch
|
||||
SOURCE: http://127.0.0.1:8000/sky/framework/sky-element/observe.sky:627
|
||||
SOURCE: http://127.0.0.1:8000/sky/framework/sky-element/observe.sky:630
|
||||
ERROR: Exception caught during observer callback: ouch
|
||||
SOURCE: http://127.0.0.1:8000/sky/framework/sky-element/observe.sky:627
|
||||
SOURCE: http://127.0.0.1:8000/sky/framework/sky-element/observe.sky:630
|
||||
Running 79 tests
|
||||
ok 1 Path constructor throws
|
||||
ok 2 Path path validity
|
||||
|
||||
@ -334,11 +334,8 @@ describe('ObserverTransform', function() {
|
||||
|
||||
function valueFn(value) { return value * 2; }
|
||||
|
||||
function setValueFn(value) { return value / 2; }
|
||||
|
||||
observer = new ObserverTransform(new PathObserver(obj, 'foo'),
|
||||
valueFn,
|
||||
setValueFn);
|
||||
valueFn);
|
||||
observer.open(callback);
|
||||
|
||||
obj.foo = 2;
|
||||
@ -347,11 +344,10 @@ describe('ObserverTransform', function() {
|
||||
assertNoChanges();
|
||||
|
||||
observer.setValue(2);
|
||||
assert.strictEqual(obj.foo, 1);
|
||||
assertPathChanges(2, 4);
|
||||
assert.strictEqual(obj.foo, 2);
|
||||
|
||||
obj.foo = 10;
|
||||
assertPathChanges(20, 2);
|
||||
assertPathChanges(20, 4);
|
||||
|
||||
observer.close();
|
||||
});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
Running 12 tests
|
||||
Running 13 tests
|
||||
ok 1 SkyElement should stamp when the element is inserted
|
||||
ok 2 SkyElement should update isAttached when inserting
|
||||
ok 3 SkyElement should handle parser created elements with attributes
|
||||
@ -8,9 +8,10 @@ ok 6 SkyElement should convert boolean reflected attributes
|
||||
ok 7 SkyElement should convert string reflected attributes
|
||||
ok 8 SkyElement should convert number reflected attributes
|
||||
ok 9 SkyElement should connect data binding
|
||||
ok 10 SkyElement should connect template event handlers
|
||||
ok 11 SkyElement should connect host event handlers
|
||||
ok 12 SkyElement should call shadowRootReady after creating the template instance
|
||||
12 tests
|
||||
12 pass
|
||||
ok 10 SkyElement should two way bind attributes
|
||||
ok 11 SkyElement should connect template event handlers
|
||||
ok 12 SkyElement should connect host event handlers
|
||||
ok 13 SkyElement should call shadowRootReady after creating the template instance
|
||||
13 tests
|
||||
13 pass
|
||||
0 fail
|
||||
|
||||
@ -131,6 +131,43 @@ describe("SkyElement", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it("should two way bind attributes", function(done) {
|
||||
sandbox.appendChild(element);
|
||||
var checkbox = element.shadowRoot.getElementById("checkbox");
|
||||
assert.isFalse(checkbox.checked);
|
||||
assert.isFalse(element.checked);
|
||||
element.checked = true;
|
||||
assert.isTrue(element.checked);
|
||||
assert.isFalse(checkbox.checked);
|
||||
Promise.resolve().then(function() {
|
||||
assert.isTrue(checkbox.checked);
|
||||
checkbox.checked = false;
|
||||
assert.isFalse(checkbox.checked);
|
||||
return Promise.resolve().then(function() {
|
||||
assert.isFalse(element.checked);
|
||||
assert.isFalse(checkbox.checked);
|
||||
checkbox.checked = true;
|
||||
assert.isTrue(checkbox.checked);
|
||||
return Promise.resolve().then(function() {
|
||||
assert.isTrue(element.checked);
|
||||
element.checked = true;
|
||||
assert.isTrue(element.checked);
|
||||
assert.isTrue(checkbox.checked);
|
||||
element.checked = false;
|
||||
assert.isFalse(element.checked);
|
||||
assert.isTrue(checkbox.checked);
|
||||
return Promise.resolve().then(function() {
|
||||
assert.isFalse(checkbox.checked);
|
||||
assert.isFalse(element.checked);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}).catch(function(e) {
|
||||
done(e);
|
||||
});
|
||||
});
|
||||
|
||||
it("should connect template event handlers", function() {
|
||||
sandbox.appendChild(element);
|
||||
var inside = element.shadowRoot.getElementById("inside");
|
||||
@ -160,4 +197,4 @@ describe("SkyElement", function() {
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</sky>
|
||||
</sky>
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
// found in the LICENSE file.
|
||||
-->
|
||||
<import src="/sky/framework/sky-element/sky-element.sky" as="SkyElement" />
|
||||
<import src="/sky/framework/sky-checkbox/sky-checkbox.sky" />
|
||||
|
||||
<sky-element
|
||||
name="test-element"
|
||||
@ -11,6 +12,7 @@
|
||||
on-host-event="handleEvent">
|
||||
<template>
|
||||
<div id="inside" on-test-event="handleEvent" lang="{{ value }}">{{ value }}</div>
|
||||
<sky-checkbox id="checkbox" checked="{{ checked }}" />
|
||||
</template>
|
||||
<script>
|
||||
module.exports = class extends SkyElement {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user