Open compojoom opened 7 years ago
I just solved this in the most stupid way possible. I would be happy if someone could point me to a better solution.
So I have my enums property and I made a transformer. Whenever the parsed value is an array I just get the first item. This way if I have more than 1 item in the array the validation still doesn't complain. I don't use the returned struct from .getValue(), but directly use my state to save the object, so I end up with the correct data, but that is really not a good solution. I would be happy if someone could shed some light on this. I've re-read the docs like 100 times and I just don't understand how one is supposed to do a multiple selection list.
Seems like this is a dupe of #131? (Also - that you actually want to use a t.list(t.String)
and not a t.enum()
.)
@compojoom , were you ever able to validate your values when more than one is selected? I am having the same issue.
const Choices = {
'First': 'First choice',
'Second': 'Second choice',
}
const EnumChoices = t.enums(Choices, 'Choices');
const Model = t.struct({
choices: EnumChoices,
});
const options = {
label: "The Choice Maker",
auto: 'placeholders',
fields: {
choices: {
label: 'Make a choice',
choices: Choices,
factory: MultiSelectExample,
}
}
};
In the MultiSelectExample component I have assigned as factory
class MultiSelectExample extends t.form.Select {
constructor(props) {
super(props)
this.choices = props.options.choices
}
onSelectedItemsChange = selectedItems => {
this.setState({ selectedItems })
this.props.onChange(selectedItems);
}
getChoices() {
let choices = []
for (const choice in this.choices) {
choices.push({id: choice, name: this.choices[choice]})
}
return choices
}
getTemplate() {
let self = this;
return function(locals) {
return (
<View>
<MultiSelect
items={self.getChoices()}
/* lots of props for react-native-multiple-select */
/>
</View>
);
}
}
}
Then the default component:
export default class App extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
options: options,
value: {}
};
}
handleSubmit = () => {
const value = this._form.getValue();
console.log('value: ', value);
}
onSubmit(event) {
event.preventDefault();
const value = this._form.getValue();
console.log(value);
}
onChange(value, path) {
console.log(value);
}
render() {
return (
<View style={styles.container}>
<Form
ref = {component => this._form = component}
type={Model}
options={options}
value={this.state.value}
onChange={this.onChange}
/>
<TouchableHighlight style={styles.button} onPress={this.handleSubmit} underlayColor='#99d9f4'>
<Text style={styles.buttonText}>Save</Text>
</TouchableHighlight>
</View>
);
}
}
When I only select one value, onChange() and onSubmit() give this.
[18:14:38] Object {
[18:14:38] "choices": Array [
[18:14:38] "First",
[18:14:38] ],
[18:14:38] }
[18:14:39] value: Struct {
[18:14:39] "choices": "First",
[18:14:39] }
If I select two, I get null value for the form onSubmit.
[18:14:40] Object {
[18:14:40] "choices": Array [
[18:14:40] "First",
[18:14:40] "Second",
[18:14:40] ],
[18:14:40] }
[18:14:41] value: null
I "solved" it with this:
export const MultiSelectTransformer = {
format: value => {
return Array.isArray(value) ? value : []
},
parse: value => {
// Just trick it here by returning the first value. Otherwise validation won't work
// TODO: find better way to do this: https://github.com/gcanti/tcomb-form-native/issues/382
return value ? value[0] : []
}
}
I feel/know that this is not the correct solution, but I still fail to understand how to do it correctly...
@compojoom seems like I was able to solve my issues by not extending from the t.form.Select component and by providing a t.list(t.String) comb to my Model instead of enum. So this seems to remove the need for validating enums.
import MultiSelect from 'react-native-multiple-select';
import t from 'tcomb-form-native';
const Form = t.form.Form;
const Choices = {
'First': 'First choice',
'Second': 'Second choice',
}
const Model = t.struct({
choices: t.list(t.String),
});
class MultiSelectExample extends t.form.Component {
constructor(props) {
super(props)
this.choices = props.options.choices
}
onSelectedItemsChange = selectedItems => {
this.setState({ selectedItems })
this.props.onChange(selectedItems);
}
getChoices() {
let choices = []
for (const choice in this.choices) {
choices.push({id: choice, name: this.choices[choice]})
}
return choices
}
getTemplate() {
let self = this;
return function(locals) {
return (
<View>
<Text>
MultiSelect Sample
</Text>
<MultiSelect
...
items={self.getChoices()}
ref={(component) => { self.multiSelect = component }}
onSelectedItemsChange={self.onSelectedItemsChange}
selectedItems={self.state.selectedItems}
...
/>
<View>
{self.multiSelect && self.multiSelect.getSelectedItemsExt(self.state.selectedItems)}
</View>
</View>
);
}
}
}
const options = {
label: "The Choice Maker",
auto: 'placeholders',
fields: {
choices: {
label: 'Make a choice',
choices: Choices,
factory: MultiSelectExample,
}
}
};
type Props = {}
type State = {
value: Object,
options: Object
}
export default class App extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
options: options,
value: {}
};
}
handleSubmit = () => {
const value = this._form.getValue();
console.log('fn0rd! value: ', value);
}
onChange(value, path) {
console.log(value);
}
render() {
return (
<View style={styles.container}>
<Form
ref = {component => this._form = component}
type={Model}
options={options}
value={this.state.value}
onChange={this.onChange}
/>
<TouchableHighlight style={styles.button} onPress={this.handleSubmit} underlayColor='#99d9f4'>
<Text style={styles.buttonText}>Save</Text>
</TouchableHighlight>
</View>
);
}
}
I created a template using this component: https://github.com/ataomega/react-native-multiple-select-list I used enums to display the list of options and I'm able to select several options. However I'm faling at the validation. It seems that enums are just a single value, where in my case I'm getting an array of values => so tcomb is failing the form validation.
I thought of doing a refinement to check if the value is an array and then return true, but when I did:
used like this
I'm getting invalid value supplied to enums?
Can I do a refinement for enums? Or what would be the correct type to use in order to create a multi-select component?