more binding stuff

pull/31/head
Rich-Harris 8 years ago
parent 263a08f155
commit 2aeaaa24b6

@ -0,0 +1,60 @@
import deindent from '../utils/deindent.js';
export default function createBinding ( node, name, attribute, current, initStatements, updateStatements, teardownStatements, allUsedContexts ) {
const parts = attribute.value.split( '.' );
const deep = parts.length > 1;
const contextual = parts[0] in current.contexts;
if ( contextual ) allUsedContexts.add( parts[0] );
const handler = current.counter( `${name}ChangeHandler` );
let setter;
let eventName = 'change';
if ( node.name === 'input' ) {
const type = node.attributes.find( attr => attr.type === 'Attribute' && attr.name === 'type' );
if ( !type || type.value[0].data === 'text' ) {
// TODO in validation, should throw if type attribute is not static
eventName = 'input';
}
}
if ( deep && contextual ) {
// TODO can we target only things that have changed?
setter = deindent`
var context = this.__context.${parts[0]};
context.${parts.slice( 1 ).join( '.' )} = this.${attribute.name};
component.set({});
`;
} else if ( deep ) {
setter = deindent`
var ${parts[0]} = component.get( '${parts[0]}' );
${parts[0]}.${parts.slice( 1 ).join( '.' )} = this.${attribute.name};
component.set({ ${parts[0]}: ${parts[0]} });
`;
} else if ( contextual ) {
throw new Error( `Reassigning context is not currently supported` );
} else {
setter = `component.set({ ${attribute.value}: ${name}.${attribute.name} });`;
}
initStatements.push( deindent`
var ${name}_updating = false;
function ${handler} () {
${name}_updating = true;
${setter}
${name}_updating = false;
}
${name}.addEventListener( '${eventName}', ${handler}, false );
` );
updateStatements.push( deindent`
if ( !${name}_updating ) ${name}.${attribute.name} = ${contextual ? attribute.value : `root.${attribute.value}`}
` );
teardownStatements.push( deindent`
${name}.removeEventListener( '${eventName}', ${handler}, false );
` );
}

@ -6,6 +6,7 @@ import isReference from './utils/isReference.js';
import contextualise from './utils/contextualise.js';
import counter from './utils/counter.js';
import attributeLookup from './attributes/lookup.js';
import createBinding from './binding/index.js';
function createRenderer ( fragment ) {
return deindent`
@ -235,42 +236,7 @@ export default function generate ( parsed, template ) {
}
else if ( attribute.type === 'Binding' ) {
const parts = attribute.value.split( '.' );
const contextual = parts[0] in current.contexts;
const handler = current.counter( `${name}ChangeHandler` );
let setter;
if ( contextual ) {
allUsedContexts.add( parts[0] );
setter = deindent`
var context = this.__context.${parts[0]};
context.${parts.slice( 1 ).join( '.' )} = this.value;
component.set({ ${current.contextChain[0]}: component.get( '${current.contextChain[0]}' ) });
`;
} else {
setter = `component.set({ ${attribute.value}: ${name}.value });`;
}
initStatements.push( deindent`
var ${name}_updating = false;
function ${handler} () {
${name}_updating = true;
${setter}
${name}_updating = false;
}
${name}.addEventListener( 'input', ${handler}, false );
` );
updateStatements.push( deindent`
if ( !${name}_updating ) ${name}.value = ${contextual ? attribute.value : `root.${attribute.value}`}
` );
teardownStatements.push( deindent`
${name}.removeEventListener( 'input', ${handler}, false );
` );
createBinding( node, name, attribute, current, initStatements, updateStatements, teardownStatements, allUsedContexts );
}
else {

@ -0,0 +1,23 @@
import * as assert from 'assert';
export default {
data: {
foo: true
},
html: `<input type="checkbox">\n<p>true</p>`,
test ( component, target, window ) {
const input = target.querySelector( 'input' );
assert.equal( input.checked, true );
const event = new window.Event( 'change' );
input.checked = false;
input.dispatchEvent( event );
assert.equal( target.innerHTML, `<input type="checkbox">\n<p>false</p>` );
component.set({ foo: true });
assert.equal( input.checked, true );
assert.equal( target.innerHTML, `<input type="checkbox">\n<p>true</p>` );
}
};

@ -0,0 +1,2 @@
<input type='checkbox' bind:checked='foo'>
<p>{{foo}}</p>

@ -0,0 +1,31 @@
import * as assert from 'assert';
export default {
data: {
items: [
'one',
'two',
'three'
]
},
html: `<div><input><p>one</p></div><div><input><p>two</p></div><div><input><p>three</p></div><!--#each items-->`,
test ( component, target, window ) {
const inputs = [ ...target.querySelectorAll( 'input' ) ];
assert.equal( inputs[0].value, 'one' );
const event = new window.Event( 'input' );
inputs[1].value = 'four';
inputs[1].dispatchEvent( event );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>three</p></div><!--#each items-->` );
const items = component.get( 'items' );
items[2] = 'five';
component.set({ items });
assert.equal( inputs[2].value, 'five' );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>five</p></div><!--#each items-->` );
}
};

@ -0,0 +1,3 @@
{{#each items as item}}
<div><input bind:value='item'><p>{{item}}</p></div>
{{/each}}

@ -0,0 +1,31 @@
import * as assert from 'assert';
export default {
data: {
items: [
{ description: 'one' },
{ description: 'two' },
{ description: 'three' }
]
},
html: `<div><input><p>one</p></div><div><input><p>two</p></div><div><input><p>three</p></div><!--#each items-->`,
test ( component, target, window ) {
const inputs = [ ...target.querySelectorAll( 'input' ) ];
assert.equal( inputs[0].value, 'one' );
const event = new window.Event( 'input' );
inputs[1].value = 'four';
inputs[1].dispatchEvent( event );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>three</p></div><!--#each items-->` );
const items = component.get( 'items' );
items[2].description = 'five';
component.set({ items });
assert.equal( inputs[2].value, 'five' );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>five</p></div><!--#each items-->` );
}
};

@ -0,0 +1,3 @@
{{#each items as item}}
<div><input bind:value='item.description'><p>{{item.description}}</p></div>
{{/each}}

@ -2,30 +2,28 @@ import * as assert from 'assert';
export default {
data: {
items: [
{ description: 'one' },
{ description: 'two' },
{ description: 'three' }
]
user: {
name: 'alice'
}
},
html: `<div><input><p>one</p></div><div><input><p>two</p></div><div><input><p>three</p></div><!--#each items-->`,
html: `<input>\n<p>hello alice</p>`,
test ( component, target, window ) {
const inputs = [ ...target.querySelectorAll( 'input' ) ];
const input = target.querySelector( 'input' );
assert.equal( inputs[0].value, 'one' );
assert.equal( input.value, 'alice' );
const event = new window.Event( 'input' );
inputs[1].value = 'four';
inputs[1].dispatchEvent( event );
input.value = 'bob';
input.dispatchEvent( event );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>three</p></div><!--#each items-->` );
assert.equal( target.innerHTML, `<input>\n<p>hello bob</p>` );
const items = component.get( 'items' );
items[2].description = 'five';
const user = component.get( 'user' );
user.name = 'carol';
component.set({ items });
assert.equal( inputs[2].value, 'five' );
assert.equal( target.innerHTML, `<div><input><p>one</p></div><div><input><p>four</p></div><div><input><p>five</p></div><!--#each items-->` );
component.set({ user });
assert.equal( input.value, 'carol' );
assert.equal( target.innerHTML, `<input>\n<p>hello carol</p>` );
}
};

@ -1,3 +1,2 @@
{{#each items as item}}
<div><input bind:value='item.description'><p>{{item.description}}</p></div>
{{/each}}
<input bind:value='user.name'>
<p>hello {{user.name}}</p>

Loading…
Cancel
Save