basic two-way binding

pull/31/head
Rich-Harris 8 years ago
parent 3560bbe85e
commit fa60968ae1

@ -234,6 +234,33 @@ export default function generate ( parsed, template ) {
` );
}
else if ( attribute.type === 'Binding' ) {
if ( attribute.value in current.contexts ) {
throw new Error( `Can only bind top-level properties` );
}
const handler = current.counter( `${name}ChangeHandler` );
initStatements.push( deindent`
var ${name}_updating = false;
function ${handler} () {
${name}_updating = true;
component.set({ ${attribute.value}: ${name}.value });
${name}_updating = false;
}
${name}.addEventListener( 'input', ${handler}, false );
` );
updateStatements.push( deindent`
if ( !${name}_updating ) ${name}.value = root.${attribute.value};
` );
teardownStatements.push( deindent`
${name}.removeEventListener( 'input', ${handler}, false );
` );
}
else {
throw new Error( `Not implemented: ${attribute.type}` );
}

@ -41,3 +41,26 @@ export function readEventHandlerDirective ( parser, start, name ) {
expression
};
}
export function readBindingDirective ( parser, start, name ) {
const quoteMark = (
parser.eat( `'` ) ? `'` :
parser.eat( `"` ) ? `"` :
null
);
const value = parser.read( /[a-zA-Z_$][a-zA-Z0-9_$]*/ ); // TODO keypaths? /([a-zA-Z_$][a-zA-Z0-9_$]*)(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*/
if ( !value ) parser.error( `Expected valid property name` );
if ( quoteMark ) {
parser.eat( quoteMark, true );
}
return {
start,
end: parser.index,
type: 'Binding',
name,
value
};
}

@ -1,7 +1,7 @@
import readExpression from '../read/expression.js';
import readScript from '../read/script.js';
import readStyle from '../read/style.js';
import { readEventHandlerDirective } from '../read/directives.js';
import { readEventHandlerDirective, readBindingDirective } from '../read/directives.js';
import { trimStart, trimEnd } from '../utils/trim.js';
const validTagName = /^[a-zA-Z]{1,}:?[a-zA-Z0-9\-]*/;
@ -144,6 +144,11 @@ function readAttribute ( parser ) {
return readEventHandlerDirective( parser, start, name.slice( 3 ) );
}
if ( /^bind:/.test( name ) ) {
parser.eat( '=', true );
return readBindingDirective( parser, start, name.slice( 5 ) );
}
const value = parser.eat( '=' ) ? readAttributeValue( parser ) : true;
return {

@ -0,0 +1,23 @@
import * as assert from 'assert';
export default {
data: {
name: 'world'
},
html: `<input>\n<p>hello world</p>`,
test ( component, target, window ) {
const input = target.querySelector( 'input' );
assert.equal( input.value, 'world' );
const event = new window.Event( 'input' );
input.value = 'everybody';
input.dispatchEvent( event );
assert.equal( target.innerHTML, `<input>\n<p>hello everybody</p>` );
component.set({ name: 'goodbye' });
assert.equal( input.value, 'goodbye' );
assert.equal( target.innerHTML, `<input>\n<p>hello goodbye</p>` );
}
};

@ -0,0 +1,2 @@
<input bind:value='name'>
<p>hello {{name}}</p>

@ -0,0 +1 @@
<input bind:value='name'>

@ -0,0 +1,27 @@
{
"html": {
"start": 0,
"end": 25,
"type": "Fragment",
"children": [
{
"start": 0,
"end": 25,
"type": "Element",
"name": "input",
"attributes": [
{
"start": 7,
"end": 24,
"type": "Binding",
"name": "value",
"value": "name"
}
],
"children": []
}
]
},
"css": null,
"js": null
}
Loading…
Cancel
Save