简体   繁体   English

多步无线电验证 Reactjs (StepZilla)

[英]Radio validation in multistep Reactjs (StepZilla)

I'm attempting to add validation for radio buttons and check boxes within the StepZilla wizard.我正在尝试在 StepZilla 向导中添加对单选按钮和复选框的验证。 Essentially not allowing the user to proceed unless a choice has been selected.基本上不允许用户继续,除非选择了一个选项。 The way I have it now the user is allowed to skip the first step without first selecting either yes/no on the first radio question and I can't figure out why.我现在拥有它的方式允许用户跳过第一步,而无需先在第一个无线电问题上选择是/否,我不知道为什么。 I've added choice logic to the functions before rendering, but can't figure out where the issue is occurring.我在渲染之前向函数添加了选择逻辑,但无法弄清楚问题出在哪里。

I'm fairly certain it has something to do with the choiceVal: (data.choice != null) line I added within _validatedata.我相当确定它与我在 _validatedata 中添加的choiceVal: (data.choice != null)行有关。 My thinking is that the validation is such that the choice should not be null, but maybe that's incorrect?我的想法是验证是这样的,选择不应该是 null,但这也许是不正确的?

'use strict';


import React, { Component } from 'react';

export default class Step1 extends Component {
  constructor(props) {
    super(props);

    this.state = {
      email: props.getStore().email,
      name: props.getStore().name,
      choice: props.getStore().choice
    };

    this._validateOnDemand = true; // this flag enables onBlur validation as user fills forms

    this.validationCheck = this.validationCheck.bind(this);
    this.isValidated = this.isValidated.bind(this);
  }

  componentDidMount() {}

  componentWillUnmount() {}

  isValidated() {
    const userInput = this._grabUserInput(); // grab user entered vals
    const validateNewInput = this._validateData(userInput); // run the new input against the validator
    let isDataValid = false;

    // if full validation passes then save to store and pass as valid
    if (Object.keys(validateNewInput).every((k) => { return validateNewInput[k] === true })) {
        if (this.props.getStore().email != userInput.email || this.props.getStore().name != userInput.name || this.props.getStore().choice != userInput.choice) { // only update store of something changed
          this.props.updateStore({
            ...userInput,
            savedToCloud: false // use this to notify step4 that some changes took place and prompt the user to save again
          });  // Update store here (this is just an example, in reality you will do it via redux or flux)
        }

        isDataValid = true;
    }
    else {
        // if anything fails then update the UI validation state but NOT the UI Data State
        this.setState(Object.assign(userInput, validateNewInput, this._validationErrors(validateNewInput)));
    }

    return isDataValid;
  }

  validationCheck() {
    if (!this._validateOnDemand)
      return;

    const userInput = this._grabUserInput(); // grab user entered vals
    const validateNewInput = this._validateData(userInput); // run the new input against the validator

    this.setState(Object.assign(userInput, validateNewInput, this._validationErrors(validateNewInput)));
  }

   _validateData(data) {
    return  {
      nameVal: /^[a-zA-Z]+[a-zA-Z]+$/.test(data.name), // required: anything besides N/A
      emailVal: /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/.test(data.email),
      choiceVal: (data.choice != null) // required: regex w3c uses in html5
    }
  }

  _validationErrors(val) {
    const errMsgs = {
      nameValMsg: val.nameVal ? '' : 'Please enter a full name',
      emailValMsg: val.emailVal ? '' : 'A valid email is required',
      choiceValMsg: val.choiceVal ? '' : 'A choice is required'
    }
    return errMsgs;
  }

  _grabUserInput() {
    return {
      name: this.refs.name.value,
      email: this.refs.email.value,
      choice: this.refs.choice.value
    };
  }

  render() {
    // explicit class assigning based on validation
    let notValidClasses = {};

    if (typeof this.state.nameVal == 'undefined' || this.state.nameVal) {
      notValidClasses.nameCls = 'no-error col-md-8';
    }
    else {
       notValidClasses.nameCls = 'has-error col-md-8';
       notValidClasses.nameValGrpCls = 'val-err-tooltip';
    }

    if (typeof this.state.emailVal == 'undefined' || this.state.emailVal) {
        notValidClasses.emailCls = 'no-error col-md-8';
    }
    else {
       notValidClasses.emailCls = 'has-error col-md-8';
       notValidClasses.emailValGrpCls = 'val-err-tooltip';
    }

    if (typeof this.state.choiceVal == 'undefined' || this.state.choiceVal) {
      notValidClasses.choiceCls = 'no-error col-md-8';
    }
    else {
     notValidClasses.choiceCls = 'has-error col-md-8';
     notValidClasses.choiceValGrpCls = 'val-err-tooltip';
     }

Split the return below so it's easier to view.拆分下面的返回,以便更容易查看。

    return (
      <div className="step step1">
        <div className="row">
          <form id="Form" className="form-horizontal">
            <div className="form-group">
              <label className="col-md-12 control-label">
                <h1>Step 1</h1>
              </label>
            </div>
         
            <div className="row content">
              <div className="col-md-12">
              <legend>Are you happy?</legend>
                <div className={notValidClasses.choiceCls}>
                  <input 
                    ref="choice"
                    type="radio" 
                    id="yes" 
                    name="choice" 
                    value="yes" 
                    onblur={this.validationCheck}
                    defaultValue={this.state.choice}
                    required />
                    <label form="yes">Yes</label>
                  <input 
                    ref="choice"
                    type="radio" 
                    id="no" 
                    name="choice" 
                    value="no"
                    onblur={this.validationCheck}
                    defaultValue={this.state.choic} />
                    <label form="no">No</label>
                </div>
                <div className={notValidClasses.choiceValGrpCls}>{this.state.choiceValMsg}</div>
              </div>
            </div>
            
            <div className="form-group col-md-12 content form-block-holder">
                <label className="control-label col-md-4">
                  Name
                </label>
                <div className={notValidClasses.nameCls}>
                  <input
                    ref="name"
                    autoComplete="off"
                    className="form-control"
                    required
                    defaultValue={this.state.name}
                    onBlur={this.validationCheck} />
                  <div className={notValidClasses.nameValGrpCls}>{this.state.nameValMsg}</div>
                </div>
              </div>
              <div className="form-group col-md-12 content form-block-holder">
                <label className="control-label col-md-4">
                  Email
                </label>
                <div className={notValidClasses.emailCls}>
                  <input
                    ref="email"
                    autoComplete="off"
                    type="email"
                    placeholder="john.smith@example.com"
                    className="form-control"
                    required
                    defaultValue={this.state.email}
                    onBlur={this.validationCheck} />
                  <div className={notValidClasses.emailValGrpCls}>{this.state.emailValMsg}</div>
                </div>
              </div>
          </form>
        </div>
      </div>
    )
  }
}

My guess would be the default store value props.getStore().choice is null. You might need to change the store such that this is defaulted to false or you could default it when you set the initial component state:我的猜测是默认存储值props.getStore().choice是 null。您可能需要更改存储,使其默认为 false,或者您可以在设置初始组件时将其默认为 state:

    this.state = {
      email: props.getStore().email,
      name: props.getStore().name,
      choice: props.getStore().choice ?? false
    };

It could well be true of email and name as well but whilst its not causing a problem (because the regex would return false over null) its also bad practice. email 和名称也很可能是真的,但虽然它不会引起问题(因为正则表达式会返回 false 而不是 null),但它也是不好的做法。 They should be empty string explicitly to begin with.它们一开始应该是明确的空字符串。

Just wanted to update saying that I got the validation to work with these additions, everything in between //**// is new.只是想更新说我得到了使用这些添加的验证, //**//之间的所有内容都是新的。

      this.state = {
      email: props.getStore().email,
      name: props.getStore().name,
      choice: props.getStore().selectedOption
    };

//**// this.onValueChange = this.onValueChange.bind(this); //**//
       this._validateOnDemand = true; // this flag enables onBlur validation as user fills forms

    this.validationCheck = this.validationCheck.bind(this);
    this.isValidated = this.isValidated.bind(this);
  }

  //**//
  onValueChange(event) {
    this.setState({
      selectedOption: event.target.value
    });
  }
  //**//

[...]

  _grabUserInput() {
    return {
      name: this.refs.name.value,
      email: this.refs.email.value,
//**// choice: this.state.selectedOption //**//
    };
  }

After return返回后

 <input 
                    ref="choice"
                    type="radio" 
                    value="no"
          //**//    checked={this.state.selectedOption === "no"}
                    onChange={this.onValueChange}   //**//
                    onBlur={this.validationCheck} 
                    />

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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