correctly bind one-way select value attributes (#423)

pull/457/head
Rich Harris 8 years ago
parent 20298b1a0a
commit 93e51d6ef3

@ -46,9 +46,38 @@ export default function visitAttribute ( generator, block, state, node, attribut
const last = `last_${state.parentNode}_${name.replace( /-/g, '_')}`;
block.builders.create.addLine( `var ${last} = ${value};` );
const updater = propertyName ?
`${state.parentNode}.${propertyName} = ${last};` :
`${generator.helper( method )}( ${state.parentNode}, '${name}', ${last} );`;
const isSelectValueAttribute = name === 'value' && state.parentNodeName === 'select';
let updater;
if ( isSelectValueAttribute ) {
// annoying special case
const isMultipleSelect = node.name === 'select' && node.attributes.find( attr => attr.name.toLowerCase() === 'multiple' ); // TODO use getStaticAttributeValue
const i = block.getUniqueName( 'i' );
const option = block.getUniqueName( 'option' );
const ifStatement = isMultipleSelect ?
deindent`
${option}.selected = ~${last}.indexOf( ${option}.__value );` :
deindent`
if ( ${option}.__value === ${last} ) {
${option}.selected = true;
break;
}`;
updater = deindent`
var ${last} = ${last};
for ( var ${i} = 0; ${i} < ${state.parentNode}.options.length; ${i} += 1 ) {
var ${option} = ${state.parentNode}.options[${i}];
${ifStatement}
}
`;
} else if ( propertyName ) {
updater = `${state.parentNode}.${propertyName} = ${last};`;
} else {
updater = `${generator.helper( method )}( ${state.parentNode}, '${name}', ${last} );`;
}
block.builders.create.addLine( updater );
block.builders.update.addBlock( deindent`

@ -39,6 +39,7 @@ export default function visitElement ( generator, block, state, node ) {
const childState = Object.assign( {}, state, {
isTopLevel: false,
parentNode: name,
parentNodeName: node.name,
namespace: node.name === 'svg' ? 'http://www.w3.org/2000/svg' : state.namespace,
allUsedContexts: []
});
@ -55,9 +56,18 @@ export default function visitElement ( generator, block, state, node ) {
block.builders.create.addLine( `${generator.helper( 'setAttribute' )}( ${name}, '${generator.cssId}', '' );` );
}
let selectValueAttribute;
node.attributes
.sort( ( a, b ) => order[ a.type ] - order[ b.type ] )
.forEach( attribute => {
// <select> value attributes are an annoying special case — it must be handled
// *after* its children have been updated
if ( attribute.type === 'Attribute' && attribute.name === 'value' && node.name === 'select' ) {
selectValueAttribute = attribute;
return;
}
visitors[ attribute.type ]( generator, block, childState, node, attribute );
});
@ -106,6 +116,10 @@ export default function visitElement ( generator, block, state, node ) {
node.children.forEach( child => {
visit( generator, block, childState, child );
});
if ( selectValueAttribute ) {
visitAttribute( generator, block, childState, node, selectValueAttribute );
}
}
function getRenderStatement ( generator, namespace, name ) {

@ -0,0 +1,24 @@
const items = [ {}, {} ];
export default {
skip: true, // JSDOM quirks
'skip-ssr': true,
data: {
foo: items[0],
items
},
test ( assert, component, target ) {
const options = target.querySelectorAll( 'option' );
assert.equal( options[0].selected, true );
assert.equal( options[1].selected, false );
component.set( { foo: items[1] } );
assert.equal( options[0].selected, false );
assert.equal( options[1].selected, true );
}
};

@ -0,0 +1,4 @@
<select value="{{foo}}">
<option value='{{items[0]}}'>a</option>
<option value='{{items[1]}}'>b</option>
</select>
Loading…
Cancel
Save