-
Notifications
You must be signed in to change notification settings - Fork 136
List with Dynamic Items (Different structs based on selected value) #297
Comments
Hi @sockhead, Lists of different types are not supported at the moment and technically they won't be supported, ever. This is because a const AccountType = t.enums.of([
'type 1',
'type 2',
'other'
], 'AccountType')
const KnownAccount = t.struct({
type: AccountType
}, 'KnownAccount')
// UnknownAccount extends KnownAccount so it owns also the type field
const UnknownAccount = KnownAccount.extend({
label: t.String,
}, 'UnknownAccount')
// the union
const Account = t.union([KnownAccount, UnknownAccount], 'Account')
// the final form type
const Type = t.list(Account) Generally // if account type is 'other' return the UnknownAccount type
Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount A complete example: import React from 'react'
import t from 'tcomb-form'
const AccountType = t.enums.of([
'type 1',
'type 2',
'other'
], 'AccountType')
const KnownAccount = t.struct({
type: AccountType
}, 'KnownAccount')
const UnknownAccount = KnownAccount.extend({
label: t.String,
}, 'UnknownAccount')
const Account = t.union([KnownAccount, UnknownAccount], 'Account')
Account.dispatch = value => value && value.type === 'other' ? UnknownAccount : KnownAccount
const Type = t.list(Account)
const App = React.createClass({
onSubmit(evt) {
evt.preventDefault()
const v = this.refs.form.getValue()
if (v) {
console.log(v)
}
},
render() {
return (
<form onSubmit={this.onSubmit}>
<t.form.Form
ref="form"
type={Type}
/>
<div className="form-group">
<button type="submit" className="btn btn-primary">Save</button>
</div>
</form>
)
}
}) There's a draft implementation on this branch https://github.com/gcanti/tcomb-form/tree/297 |
Thanks for the quick response. I'm currently using an older version of tcomb-form (0.5) but plan on updating to the latest version in the near future when I have time to go through and update everywhere that I use the forms. Since I'm using an older version, the changes that you made to enable union support is quite substantial between 0.5 and 0.8. I tried a quick crack at updating components.js but it broke my struct templates so I just reverted back to 0.5. I look forward to trying this out when I update to 0.8. Thanks again! |
v0.5 is quite old, you are still using However, after scanning the changelog, upgrading to 0.8 shouldn't be too painful ( Breaking changes per version:
If you decide to upgrade let me know how it goes and if you need some help. Cheers, |
I needed exactly same functionality. Wanted to know if unions are supported in v0.8.1. |
@amrut-bawane Currently there's a candidate implementation in the https://github.com/gcanti/tcomb-form/tree/297 branch if you want to give it a whirl. If everything's ok (needs tests) I'll write some documentation and then release v0.8.2 |
Yeah i tried that version. Lib folder is missing hence module is not being imported. Not perfectly sure about this issue, maybe you can look into it. |
@amrut-bawane after cloning the repo npm install
npm run build should build the |
@amrut-bawane Nevermind, just released version https://github.com/gcanti/tcomb-form/releases/tag/v0.8.2 |
Ohh that's cool! |
const Type = t.list(Account)
const App = React.createClass({
getInitialState() {
return {
value: []
}
},
onChange(value) {
this.setState({value})
},
addItem() {
// adds a new item
this.setState({ value: this.state.value.concat(undefined) })
},
render() {
return (
<div>
<t.form.Form
ref="form"
type={Type}
value={this.state.value}
onChange={this.onChange}
/>
<div className="form-group">
<button type="button" className="btn btn-primary" onClick={this.addItem}>Add item</button>
</div>
</div>
)
}
}) |
I wish to create a list that can accept fields of two types - FilterField and MetaField.
Upon receiving a click event, I am calling the addItem method of the list -
The issue is each time the list gets updated, only the latest entry i.e. either FilterField or MetaField gets added to the list. |
@amrut-bawane relying on const FilterField = t.enums.of([
'Pattern',
'Color',
'Brand'
], 'FilterField')
const MetaField = t.struct({
title: t.String,
identifier: t.String,
canCreateconstiant: t.Boolean,
required: t.Boolean,
isSearchFilter: t.Boolean,
type: t.String,
unit: t.String,
minVal: t.String,
maxVal: t.String,
toolTip: t.String,
placeHolder: t.String
}, 'MetaField')
const Field = t.union([FilterField, MetaField], 'Field')
Field.dispatch = value => {
if (t.Object.is(value)) {
return MetaField
}
return FilterField
}
const Fields = t.list(Field)
const Schema = t.struct({
title: t.String,
description: t.String,
longName: t.String,
canAddProducts: t.Boolean,
formfields: Fields
})
const App = React.createClass({
getInitialState() {
return {
value: {
formfields: []
}
}
},
onChange(value) {
this.setState({value})
},
addFilter() {
this.setState({
// here I'm using the tcomb immutability helpers, use what you want but be sure to change the `value` reference
// otherwise tcomb-form won't detect any change
value: t.update(this.state.value, {
formfields: {
$push: [undefined]
}
})
})
},
addMeta() {
this.setState({
value: t.update(this.state.value, {
formfields: {
$push: [{}]
}
})
})
},
onSubmit(evt) {
evt.preventDefault()
const v = this.refs.form.getValue()
if (v) {
console.log(v) // eslint-disable-line
}
},
render() {
return (
<form onSubmit={this.onSubmit}>
<t.form.Form
ref="form"
type={Schema}
value={this.state.value}
onChange={this.onChange}
/>
<div className="form-group">
<button type="button" className="btn btn-primary" onClick={this.addFilter}>Add filter</button>
<button type="button" className="btn btn-primary" onClick={this.addMeta}>Add meta</button>
<button type="submit" className="btn btn-primary">Save</button>
</div>
</form>
)
}
}) |
Cool, that lets me add fields of both types. But as the dispatch method of the union field is called, all the list items update to the same type- all turn to FilterFields or MetaFields. I guess that's how a union works, but any workaround to get me the required functionality?
|
Weird, that is not the result I see when I run the example above. |
Is it possible to create a list of dynamic items? I'm not sure how to pass value to Account in order for it appropriately return the correct structure. Essentially I have a predefined group of account types but I allow the user to select Other if the account type they want is not available. If they select other they should then be given a textbox where they can input the name of the account type, but this should only happen if they select Other.
For example a list of accounts where the account has a structure of:
However I want to be able to make the Account have a dynamic structure depending on the value of type. I want a new field to appear if and only if the item's type == 'Other' like below.
I also tried creating a list where Account was a function, but I have no clue if it's possible to do what I'm hoping for and have no clue how to return the struct associated with each item.
How would I go about passing the correct item's value to each item in the list in order to dynamically generate the appropriate type for the required structure? Is it possible to do some sort of foreach on the list values and then generate the appropriate structures and concatenate them together in a list? Is this even a possibility with how List works?
The text was updated successfully, but these errors were encountered: