Reputation: 581
How can I achieve radio group validation
onClick
submit button it should highlight the radio group or question which is not answered with red color or some kind of validation.
For example onClick submit button if the any of the radio group buttons are not selected change ListGroupItem bsStyle="danger" to the particular list.
ORIGINAL CODE https://stackblitz.com/edit/react-ujmlcz
//array of cards coming from the backend
const data = [
{
cardName: 'Do you want sugar in your coffee',
options: [
{ radioName: 'Yes',radioValue: '1', selected: false },
{ radioName: 'No',radioValue: '2', selected: false }]
},
{
cardName: 'Do you want milk in your coffee',
options: [
{ radioName: 'Yes',radioValue: '1', selected: false },
{ radioName: 'No',radioValue: '2', selected: false }]
},
{
cardName: 'Do you want low-fat-milk in your coffee',
options: [
{ radioName: 'Yes',radioValue: '1', selected: false },
{ radioName: 'No',radioValue: '2', selected: false }]
}
];
class CardsList extends React.Component {
constructor(props) {
super(props);
this.state = {
cards: [],
};
}
componentDidMount() {
setTimeout(() => {
// mimic an async server call
this.setState({ cards: data });
}, 1000);
}
onInputChange = ({ target }) => {
const { cards } = this.state;
const nexState = cards.map(card => {
if (card.cardName !== target.name) return card;
return {
...card,
options: card.options.map(opt => {
const checked = opt.radioName === target.value;
return {
...opt,
selected: checked
}
})
}
});
this.setState({ cards: nexState })
}
onSubmit = () => {
let unselectedCards = this.state.cards.filter((card) => {
return !card.options[0].selected && !card.options[1].selected
});
console.log("Please answer all the questions"+unselectedCards);
console.log(this.state.cards.map(({ cardName, options }) => {
const option = options.filter(({ selected }) => selected)[0]
return ` ${option.radioValue}`
}))
};
onReset = () => {
this.setState({cards:[]});
}
render() {
const { cards } = this.state;
return (
<div>
{
cards.length < 1 ? "Loading..." :
<div>
{cards.map((card, idx) => (
<ul>
{card.cardName}
{card.options.radioName}
{
card.options.map((lo, idx) => {
return <input
key={idx}
type="radio"
name={card.cardName}
value={lo.radioName}
checked={!!lo.selected}
onChange={this.onInputChange}
/>
})
}
</ul>
))
}
< button onClick={this.onSubmit}>Submit</button>
< button onClick={this.onReset}>Clear</button>
</div>
}
</div>
);
}
}
ReactDOM.render(<CardsList />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Upvotes: 1
Views: 495
Reputation: 30390
To achieve this you will first need to extend your state model. Consider tracking your "bsStyle" in each card by adding the following:
const data = [
{
cardName: 'Tires are inflated and free of excessive wear or damage. Nuts are tight.',
style : 'info', // <-- this is your bsStyle tracked for each card
options: [
{ radioName: 'Yes', radioValue: '1', selected: false },
{ radioName: 'No', radioValue: '2', selected: false }]
},
...,
...
]
This allows your component to know about, and render, the appropriate bsStyle for each card idenpendantly. With this, you can now update your onSubmit
function by adding the following "validation" functionality:
onSubmit = () => {
this.state.cards.forEach((card) => {
// If no option in the card is selected, consider the card invalid
var invalid = card.options.every(option => !option.selected)
if(invalid) {
card.style='danger'
}
else {
card.style='info'
}
});
// Cause form to re-render to display "danger" or "info" bsStyle
this.setState({ cards : this.state.cards })
...
...
}
You'll also want to ensure validation is updated when the user changes input. You could achieve that by the following:
onInputChange = ({ target }) => {
const { cards } = this.state;
const { options } = this.state;
const nexState = cards.map(card => {
if (card.cardName !== target.name) return card;
const options = card.options.map(opt => {
const checked = opt.radioName === target.value;
return {
...opt,
selected: checked
}
})
const style = options.every(option => !option.selected) ? 'danger' : 'info'
return {
...card,
style,
options
}
});
this.setState({ cards: nexState })
}
Lastly, you'll want to update the way your rendering of ListGroupItem
works by making the following adjustment:
{cards.map((card, idx) => (
<ListGroup bsStyle="custom">
<ListGroupItem bsStyle={ card.style }>
...
</ListGroupItem>
</ListGroup>)}
For more detail, you can find a complete working example here: https://stackblitz.com/edit/react-immp6b?file=index.js
Upvotes: 2