简体   繁体   中英

Radio button group validation. Highlight all unchecked radiogroup buttons questions with alert Reactjs

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> 

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM