Skip to content

Commit

Permalink
Merge pull request #457 from sveltejs/gh-423
Browse files Browse the repository at this point in the history
correctly bind one-way select value attributes (#423)
  • Loading branch information
Rich-Harris authored Apr 10, 2017
2 parents 20298b1 + 93e51d6 commit e82c478
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 3 deletions.
35 changes: 32 additions & 3 deletions src/generators/dom/visitors/Element/Attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
14 changes: 14 additions & 0 deletions src/generators/dom/visitors/Element/Element.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: []
});
Expand All @@ -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 );
});

Expand Down Expand Up @@ -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 ) {
Expand Down
24 changes: 24 additions & 0 deletions test/runtime/samples/select-one-way-bind-object/_config.js
Original file line number Diff line number Diff line change
@@ -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 );
}
};
4 changes: 4 additions & 0 deletions test/runtime/samples/select-one-way-bind-object/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<select value="{{foo}}">
<option value='{{items[0]}}'>a</option>
<option value='{{items[1]}}'>b</option>
</select>

0 comments on commit e82c478

Please sign in to comment.