Sai
Sai

Reputation: 71

React - restrict to numeric/alphanumeric/hexadecimal symbols in text field with max length

I'm working on React.js project. I wanna create 3 Text Fields where:

1st Text Field - I want to insert only hexadecimal values. It should accept the numbers from 0-9 & letters from A-F & colon. It should accept only 23 chars(numbers, letters from A-F & colon).

2nd Text Field- It should only Hexadecimal values.

3rd Text Field- It should accept only Alpha Numeric values (only Numbers &
letters).

4th Text Field- Only Letters.

Note: Special Characters should not be accepted.

Please help me out to solve this.

Sample Code:

constructor(props) {
  super(props);this.state = {showModal: true};
  this.modalFooter = this.modalFooter.bind(this);
  this.modalBody = this.modalBody.bind(this); this.updateState = this.updateState.bind(this);     
};

modalFooter() {
  return (
    <div>
      <BButton name="Cancel" onClickHandler={() => { this.setState({ showModal: false }) }} />
    </div>);
}

modalBody() {
  return (
    <div>
      <br className="Class">
        <br> Hex Value: <input type="text" className="Class" formnovalidate="return isNumber(event)"
          maxLength="23" placeholder="" /></br>
        <br> Addr:  <input type="text" className="Class" maxLength="6" name=""
          placeholder="" /></br><br> Name: <input type="text" className="Class" id="Number"
            maxLength="64"
            name="" placeholder="" /></br>
      </br>
    </div>
  );
}

updateState(e) {
  this.setState({data: e.target.value});
}

render() {
  let body = this.modalBody();
  let footer = this.modalFooter();
  let modal = <BModal header="Add Message"
    body={body}
    footer={footer} />
  return (
    <div className="page-title">
      <center>
        <h3> Sample Program </h3>
      </center>
      <hr className="horizontal-line"></hr>

      <div>  <font color="grey"><input type="text" value={this.state.data}
        onClick={() => { this.setState({ showModal: true }) }} /></font>
        {this.state.showModal ? modal : ""}
      </div>
    </div>);

}

Upvotes: 7

Views: 40015

Answers (3)

vsync
vsync

Reputation: 130065

Below is a working demo of a complete highly-generic Input component which has its smarts by utilizing the HTML5 pattern attribute and blocking undesired content according to the regular expression defined in the pattern prop:

const Input = (props) => {
  const onKeyPress = e => {
    // normalize the pattern as a Regular expression
    const pattern = props.pattern instanceof RegExp ? props.pattern : new RegExp(props.pattern)
    
    // if the currently typed character is not in the regular expression, do not allow it (to be rendered)
    // if the length of the input will exceed, do not allow
    if( !pattern.test(e.key) || e.target.value.length + 1 > (props.max||Infinity))
      e.preventDefault()

    // if also has "onKeyPress" prop, fire it now
    props.onKeyPress && props.onKeyPress(e) 
  }
  
  // prevent invalid content pasting
  const onPaste = e => {
    // get the pattern with midifications for testig a whole string rather than a single character
    const pattern = props.pattern instanceof RegExp ? props.pattern : new RegExp(`^${props.pattern}+$`)
    
    // get pasted content as string
    const paste = (e.clipboardData || window.clipboardData).getData('Text')
    
    // vaildate
    if( !pattern.test(paste) || paste.length > (props.max||Infinity))
      e.preventDefault()
      
    // if also has "onPaste" prop, fire it now
    props.onPaste && props.onPaste(e) 
  }
    
  return <input {...props} onKeyPress={onKeyPress} onPaste={onPaste} />
}


ReactDOM.render((
<form>
  <Input pattern="[0-9a-fA-F:]" max={6} placeholder="Only HEXA & colon, up to 6 chars"/>
  <Input pattern="[0-9a-fA-F]" placeholder="Only HEXA"/>
  <Input pattern="[0-9a-zA-Z]" placeholder="Only Alphanumeric"/>
  <Input pattern="[a-zA-Z]" placeholder="Only (Latin) Letters"/>
  <Input placeholder="Everything allowed"/>
</form>)
, root)
input{
  display: block;
  width: 220px;
  margin: 5px;
  padding: 5px;
}
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>

Upvotes: 0

Andreyco
Andreyco

Reputation: 22862

Actually, it's not complicated at all. You only need to take care of extracting allowed characters from text input, limit its length, ... To be honest, this is not related to React in any way - it's pure Javascript.

Let's begin with simple function which extracts substring from string by pattern

const extract = (str, pattern) => (str.match(pattern) || []).pop() || '';
// Usage
extract("01az", "[0-9a-fA-F]+") // "01a"

Then, wrap this function into functions which solve pattern problems for 1, 2, 3 and 4

const extractHexadecimalWithColon = (str) => extract(str, "[0-9a-fA-F:]+");

const extractHexadecimal = (str) => extract(str, "[0-9a-fA-F]+");

const extractAlphanum = (str) => extract(str, "[0-9a-zA-Z]+");

const extractAlpha = (str) => extract(str, "[a-zA-Z]+");

It super easy to limit length of string

const limitLength = (str, length) => str.substring(0, length);

Then, create your text inputs components, listen for changes and update state as required.

var App = React.createClass({
  getInitialState() {
    return {};
  },

  setA(e) {
    this.setState({
      a: limitLength(extractHexadecimalWithColon(e.target.value), 23),
    });
  },

  setB(e) {
    this.setState({
      b: extractHexadecimal(e.target.value),
    });
  },

  setC(e) {
    this.setState({
      c: extractAlphanum(e.target.value),
    });
  },

  setD(e) {
    this.setState({
      d: extractAlpha(e.target.value),
    });
  },

  render() {
    return (
      <div>
        <div>
          Hexadecimal, max 23 chars, colon allowed<br/>
          <textarea value={this.state.a} onChange={this.setA} />
        </div>

        <div>
          Hexadecimal only, no length restriction<br/>
          <textarea value={this.state.b} onChange={this.setB} />
        </div>

        <div>
          Alphanumeric<br/>
          <textarea value={this.state.c} onChange={this.setC} />
        </div>

        <div>
          Letters only<br/>
          <textarea value={this.state.d} onChange={this.setD} />
        </div>
      </div>
    )
  }
});

Full working fiddle here https://jsfiddle.net/69z2wepo/36536/

Original solution split into separate components https://jsfiddle.net/69z2wepo/36654/

Upvotes: 4

Anastasia Abakumova
Anastasia Abakumova

Reputation: 1111

I suggest you to use React onKeyPress event with regexp verifications (see example below and the jsbin link)

var Form = React.createClass({

  firstMethod(e) {
    const re = /[0-9A-F:]+/g;
    if (!re.test(e.key)) {
      e.preventDefault();
    }
  },

  secondMethod(e) {
    const re = /[0-9A-F]+/g;
    if (!re.test(e.key)) {
      e.preventDefault();
    }
  },

  thirdMethod(e) {
    const re = /[0-9a-fA-F]+/g;
    if (!re.test(e.key)) {
      e.preventDefault();
    }
  },

  fourthMethod(e) {
    const re = /[a-fA-F]+/g;
    if (!re.test(e.key)) {
      e.preventDefault();
    }
  },

  render() {
    return (
      <form>
        <input ref="first" onKeyPress={(e) => this.firstMethod(e)} />
        <input ref="second" onKeyPress={(e) => this.secondMethod(e)} />
        <input ref="third" onKeyPress={(e) => this.thirdMethod(e)} />
        <input ref="fourth" onKeyPress={(e) => this.fourthMethod(e)} />
      </form>
    );
  }
});

ReactDOM.render(
  <Form />,
  document.getElementById('example')
);

http://jsbin.com/juyakaqawe/edit?html,js,output

Upvotes: 20

Related Questions