Skip to content

Commit 6f18eae

Browse files
committed
implement bind:group for radio input groups (#311)
1 parent 7b057e4 commit 6f18eae

File tree

3 files changed

+112
-21
lines changed

3 files changed

+112
-21
lines changed

src/generators/dom/visitors/attributes/addElementBinding.js

+27-21
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ export default function createBinding ( generator, node, attribute, current, loc
1616
const handler = current.getUniqueName( `${local.name}ChangeHandler` );
1717

1818
const isMultipleSelect = node.name === 'select' && node.attributes.find( attr => attr.name.toLowerCase() === 'multiple' ); // TODO use getStaticAttributeValue
19+
const type = getStaticAttributeValue( node, 'type' );
1920
const bindingGroup = attribute.name === 'group' ? getBindingGroup( generator, current, attribute, keypath ) : null;
20-
const value = getBindingValue( generator, local, node, attribute, isMultipleSelect, bindingGroup );
21+
const value = getBindingValue( generator, local, node, attribute, isMultipleSelect, bindingGroup, type );
2122
const eventName = getBindingEventName( node );
2223

2324
let setter = getSetter({ current, name, context: '__svelte', attribute, dependencies, snippet, value });
@@ -26,7 +27,7 @@ export default function createBinding ( generator, node, attribute, current, loc
2627
// <select> special case
2728
if ( node.name === 'select' ) {
2829
if ( !isMultipleSelect ) {
29-
setter = `var selectedOption = ${local.name}.selectedOptions[0] || ${local.name}.options[0];\n` + setter;
30+
setter = `var selectedOption = ${local.name}.selectedOptions[0] || ${local.name}.options[0];\n${setter}`;
3031
}
3132

3233
const value = generator.current.getUniqueName( 'value' );
@@ -54,27 +55,28 @@ export default function createBinding ( generator, node, attribute, current, loc
5455

5556
// <input type='checkbox|radio' bind:group='selected'> special case
5657
else if ( attribute.name === 'group' ) {
57-
const type = getStaticAttributeValue( node, 'type' );
58+
if ( type === 'radio' ) {
59+
setter = deindent`
60+
if ( !${local.name}.checked ) return;
61+
${setter}
62+
component._bindingGroups[${bindingGroup}].forEach( function ( input ) {
63+
input.checked = false;
64+
});`;
65+
}
5866

59-
if ( type === 'checkbox' ) {
60-
local.init.addLine(
61-
`component._bindingGroups[${bindingGroup}].push( ${local.name} );`
62-
);
67+
const condition = type === 'checkbox' ?
68+
`~${snippet}.indexOf( ${local.name}.__value )` :
69+
`${local.name}.__value === ${snippet}`;
6370

64-
local.teardown.addBlock(
65-
`component._bindingGroups[${bindingGroup}].splice( component._bindingGroups[${bindingGroup}].indexOf( ${local.name} ), 1 );`
66-
);
71+
local.init.addLine(
72+
`component._bindingGroups[${bindingGroup}].push( ${local.name} );`
73+
);
6774

68-
updateElement = `${local.name}.checked = ~${snippet}.indexOf( ${local.name}.__value );`;
69-
}
70-
71-
else if ( type === 'radio' ) {
72-
throw new Error( 'TODO' );
73-
}
75+
local.teardown.addBlock(
76+
`component._bindingGroups[${bindingGroup}].splice( component._bindingGroups[${bindingGroup}].indexOf( ${local.name} ), 1 );`
77+
);
7478

75-
else {
76-
throw new Error( `Unexpected bind:group` ); // TODO catch this in validation with a better error
77-
}
79+
updateElement = `${local.name}.checked = ${condition};`;
7880
}
7981

8082
// everything else
@@ -122,7 +124,7 @@ function getBindingEventName ( node ) {
122124
return 'change';
123125
}
124126

125-
function getBindingValue ( generator, local, node, attribute, isMultipleSelect, bindingGroup ) {
127+
function getBindingValue ( generator, local, node, attribute, isMultipleSelect, bindingGroup, type ) {
126128
// <select multiple bind:value='selected>
127129
if ( isMultipleSelect ) {
128130
return `[].map.call( ${local.name}.selectedOptions, function ( option ) { return option.__value; })`;
@@ -135,7 +137,11 @@ function getBindingValue ( generator, local, node, attribute, isMultipleSelect,
135137

136138
// <input type='checkbox' bind:group='foo'>
137139
if ( attribute.name === 'group' ) {
138-
return `${generator.helper( 'getBindingGroupValue' )}( component._bindingGroups[${bindingGroup}] )`;
140+
if ( type === 'checkbox' ) {
141+
return `${generator.helper( 'getBindingGroupValue' )}( component._bindingGroups[${bindingGroup}] )`;
142+
}
143+
144+
return `${local.name}.__value`;
139145
}
140146

141147
// everything else
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const values = [
2+
{ name: 'Alpha' },
3+
{ name: 'Beta' },
4+
{ name: 'Gamma' }
5+
];
6+
7+
export default {
8+
data: {
9+
values,
10+
selected: values[1]
11+
},
12+
13+
'skip-ssr': true, // values are rendered as [object Object]
14+
15+
html: `
16+
<label>
17+
<input type="radio"> Alpha
18+
</label>
19+
20+
<label>
21+
<input type="radio"> Beta
22+
</label>
23+
24+
<label>
25+
<input type="radio"> Gamma
26+
</label>
27+
28+
<p>Beta</p>`,
29+
30+
test ( assert, component, target, window ) {
31+
const inputs = target.querySelectorAll( 'input' );
32+
assert.equal( inputs[0].checked, false );
33+
assert.equal( inputs[1].checked, true );
34+
assert.equal( inputs[2].checked, false );
35+
36+
const event = new window.Event( 'change' );
37+
38+
inputs[0].checked = true;
39+
inputs[0].dispatchEvent( event );
40+
41+
assert.htmlEqual( target.innerHTML, `
42+
<label>
43+
<input type="radio"> Alpha
44+
</label>
45+
46+
<label>
47+
<input type="radio"> Beta
48+
</label>
49+
50+
<label>
51+
<input type="radio"> Gamma
52+
</label>
53+
54+
<p>Alpha</p>
55+
` );
56+
57+
component.set({ selected: values[2] });
58+
assert.equal( inputs[0].checked, false );
59+
assert.equal( inputs[1].checked, false );
60+
assert.equal( inputs[2].checked, true );
61+
62+
assert.htmlEqual( target.innerHTML, `
63+
<label>
64+
<input type="radio"> Alpha
65+
</label>
66+
67+
<label>
68+
<input type="radio"> Beta
69+
</label>
70+
71+
<label>
72+
<input type="radio"> Gamma
73+
</label>
74+
75+
<p>Gamma</p>
76+
` );
77+
}
78+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{{#each values as value}}
2+
<label>
3+
<input type="radio" value="{{value}}" bind:group='selected' /> {{value.name}}
4+
</label>
5+
{{/each}}
6+
7+
<p>{{selected.name}}</p>

0 commit comments

Comments
 (0)