binding dependencies

pull/1746/head
Rich Harris 8 years ago
parent 10255a980e
commit ddf09b9edf

@ -22,6 +22,7 @@ export default class BindingWrapper {
element: ElementWrapper; element: ElementWrapper;
binding: Binding; binding: Binding;
events: string[]; events: string[];
dependencies: Set<string>;
usesStore: boolean; usesStore: boolean;
needsLock: boolean; needsLock: boolean;
@ -39,7 +40,7 @@ export default class BindingWrapper {
isReadOnly: boolean; isReadOnly: boolean;
isReadOnlyMediaAttribute: boolean; isReadOnlyMediaAttribute: boolean;
constructor(element: ElementWrapper, binding: Binding) { constructor(block: Block, element: ElementWrapper, binding: Binding) {
this.element = element; this.element = element;
this.binding = binding; this.binding = binding;
@ -48,6 +49,24 @@ export default class BindingWrapper {
this.isReadOnly = false; this.isReadOnly = false;
this.needsLock = false; this.needsLock = false;
this.events = []; this.events = [];
this.dependencies = new Set(this.binding.value.dependencies);
// special case: if you have e.g. `<input type=checkbox bind:checked=selected.done>`
// and `selected` is an object chosen with a <select>, then when `checked` changes,
// we need to tell the component to update all the values `selected` might be
// pointing to
// TODO should this happen in preprocess?
this.binding.value.dependencies.forEach((prop: string) => {
const indirectDependencies = this.element.renderer.component.indirectDependencies.get(prop);
if (indirectDependencies) {
indirectDependencies.forEach(indirectDependency => {
this.dependencies.add(indirectDependency);
});
}
});
block.addDependencies(this.dependencies);
} }
fromDom() { fromDom() {
@ -128,24 +147,11 @@ export default class BindingWrapper {
const { name } = getObject(this.binding.value.node); const { name } = getObject(this.binding.value.node);
const { snippet } = this.binding.value; const { snippet } = this.binding.value;
// special case: if you have e.g. `<input type=checkbox bind:checked=selected.done>`
// and `selected` is an object chosen with a <select>, then when `checked` changes,
// we need to tell the component to update all the values `selected` might be
// pointing to
// TODO should this happen in preprocess?
const dependencies = new Set(this.binding.value.dependencies);
this.binding.value.dependencies.forEach((prop: string) => {
const indirectDependencies = this.element.renderer.component.indirectDependencies.get(prop);
if (indirectDependencies) {
indirectDependencies.forEach(indirectDependency => {
dependencies.add(indirectDependency);
});
}
});
// view to model // view to model
const valueFromDom = this.fromDom(); const valueFromDom = this.fromDom();
const handler = getEventHandler(this.binding, this.element.renderer, block, name, snippet, dependencies, valueFromDom); const handler = getEventHandler(this.binding, this.element.renderer, block, name, snippet, this.dependencies, valueFromDom);
// model to view // model to view
let updateDom = this.toDom(); let updateDom = this.toDom();

@ -2,6 +2,7 @@ import Binding from '../../../../nodes/Binding';
import Element from '../../../../nodes/Element'; import Element from '../../../../nodes/Element';
import ElementWrapper from '..'; import ElementWrapper from '..';
import BindingWrapper from './Binding'; import BindingWrapper from './Binding';
import Block from '../../../Block';
export default class InputCheckboxBinding extends BindingWrapper { export default class InputCheckboxBinding extends BindingWrapper {
events = ['change']; events = ['change'];
@ -19,10 +20,11 @@ export default class InputCheckboxBinding extends BindingWrapper {
} }
constructor( constructor(
block: Block,
element: ElementWrapper, element: ElementWrapper,
binding_lookup: Record<string, Binding> binding_lookup: Record<string, Binding>
) { ) {
super(element, binding_lookup.checked); super(block, element, binding_lookup.checked);
} }
fromDom() { fromDom() {

@ -24,6 +24,6 @@ export default class InputCheckboxGroupBinding extends InputRadioGroupBinding {
toDom() { toDom() {
const condition = `~${this.binding.value.snippet}.indexOf(${this.element.var}.__value)`; const condition = `~${this.binding.value.snippet}.indexOf(${this.element.var}.__value)`;
return `${this.element.var}.checked = ${condition};` return `${this.element.var}.checked = ${condition};`;
} }
} }

@ -2,6 +2,7 @@ import Binding from '../../../../nodes/Binding';
import Element from '../../../../nodes/Element'; import Element from '../../../../nodes/Element';
import ElementWrapper from '..'; import ElementWrapper from '..';
import BindingWrapper from './Binding'; import BindingWrapper from './Binding';
import Block from '../../../Block';
export default class InputNumberBinding extends BindingWrapper { export default class InputNumberBinding extends BindingWrapper {
events = ['input']; events = ['input'];
@ -19,10 +20,11 @@ export default class InputNumberBinding extends BindingWrapper {
} }
constructor( constructor(
block: Block,
element: ElementWrapper, element: ElementWrapper,
binding_lookup: Record<string, Binding> binding_lookup: Record<string, Binding>
) { ) {
super(element, binding_lookup.value); super(block, element, binding_lookup.value);
this.needsLock = true; this.needsLock = true;
} }

@ -25,10 +25,11 @@ export default class InputRadioGroupBinding extends BindingWrapper {
} }
constructor( constructor(
block: Block,
element: ElementWrapper, element: ElementWrapper,
binding_lookup: Record<string, Binding> binding_lookup: Record<string, Binding>
) { ) {
super(element, binding_lookup.group); super(block, element, binding_lookup.group);
// TODO handle cases involving computed member expressions // TODO handle cases involving computed member expressions
const { parts } = flattenReference(this.binding.value.node); const { parts } = flattenReference(this.binding.value.node);

@ -2,6 +2,7 @@ import Binding from '../../../../nodes/Binding';
import Element from '../../../../nodes/Element'; import Element from '../../../../nodes/Element';
import ElementWrapper from '..'; import ElementWrapper from '..';
import BindingWrapper from './Binding'; import BindingWrapper from './Binding';
import Block from '../../../Block';
export default class InputTextBinding extends BindingWrapper { export default class InputTextBinding extends BindingWrapper {
events = ['input']; events = ['input'];
@ -21,10 +22,11 @@ export default class InputTextBinding extends BindingWrapper {
} }
constructor( constructor(
block: Block,
element: ElementWrapper, element: ElementWrapper,
binding_lookup: Record<string, Binding> binding_lookup: Record<string, Binding>
) { ) {
super(element, binding_lookup.value); super(block, element, binding_lookup.value);
this.needsLock = true; this.needsLock = true;
} }

@ -17,10 +17,11 @@ export default class SelectBinding extends BindingWrapper {
} }
constructor( constructor(
block: Block,
element: ElementWrapper, element: ElementWrapper,
binding_lookup: Record<string, Binding> binding_lookup: Record<string, Binding>
) { ) {
super(element, binding_lookup.value); super(block, element, binding_lookup.value);
this.events = ['change']; this.events = ['change'];
this.needsLock = true; this.needsLock = true;

@ -170,7 +170,7 @@ export default class ElementWrapper extends Wrapper {
.filter(Binding => { .filter(Binding => {
return Binding.filter(this.node, binding_lookup, type); return Binding.filter(this.node, binding_lookup, type);
}) })
.map(Binding => new Binding(this, binding_lookup)); .map(Binding => new Binding(block, this, binding_lookup));
// TODO remove this, it's just useful during refactoring // TODO remove this, it's just useful during refactoring
if (has_bindings && !this.bindings.length) { if (has_bindings && !this.bindings.length) {

@ -4,4 +4,4 @@
</label> </label>
{/each} {/each}
<p>{selected.map( function ( value ) { return value.name; }).join( ', ' ) }</p> <p>{selected.map(v => v.name).join(', ')}</p>
Loading…
Cancel
Save