I have an input where users are going to type identification numbers and I would like to mask that input so that it always has this format : XX-XXXXXXXX-X
The X's can only be numbers and the dashes need to be always in those positions.
Here is what I got so far:
import React from "react";
import { Inertia } from "@inertiajs/inertia";
import {useForm, usePage} from "@inertiajs/inertia-react";
import ErrorForm from "./ErrorForm"
function Login() {
const{data , setData , processing ,reset} = useForm({
cuit: '',
password: ''
})
const errors = usePage().props.errors
function submit(e){
e.preventDefault()
Inertia.post(route('login'),data,{
one rror:() => reset('password')
})
}
function handleChange(e){
if(e.target.value.length === 11){
e.target.value = [e.target.value.slice(0,11),'-'].join('')
}else if(e.target.value.length >= 2){
if(!e.target.value.includes('-')){
e.target.value = [e.target.value.slice(0,2),'-',e.target.value.slice(2)].join('')
}
}
setData('cuit',e.target.value)
}
function handleKeyDown(e){
if(e.key === "0" || e.key === "1" || e.key === "2" || e.key === "3" || e.key === "4" || e.key === "5" || e.key === "6" || e.key === "7" || e.key === "8" || e.key === "9"){
handleChange(e)
}
}
return(
<div className="ContenedorLogin">
<form onSubmit={submit}>
<input
name="cuit"
type="text"
placeholder="C.U.I.T."
className="input"
onKeyDown={handleKeyDown}
maxLength="13"
/>
{errors.cuit &&
<ErrorForm
content={errors.cuit}
/>
}
<input
name="password"
type="Password"
placeholder="Contraseña"
className="input"
value={data.password}
onChange={e => setData('password',e.target.value)}
/>
<button className="btn-consejo" type="submit" disabled={processing}>INGRESAR</button>
</form>
</div>
)
}
export default Login
Basically I'm capturing a keyDown event, then changing the input only if the user typed a number and finally in the handleChange function I try to mask and set the value.
It kind of work but not for all cases, for example if I'm in the middle of typing and I already have the first dash and I add a number before the first dash its going to allow it leaving me with something like this : XXXX-XXXXX
I imagine I can achieve the result using regular expressions or something like that but I'm not familiar at all with them
Thanks in advance!
CodePudding user response:
It would work if you bind event on whole input value instead of each key press.
Here, instead of using event "onKeyDown", use "onChange". Also You can use html pattern for accepting number only.
<input
name="cuit"
type="number"
pattern="[0-9\/]*"
placeholder="C.U.I.T."
className="input"
onChange={handleChange} // calling handleChange on input change directly
maxLength="13"
/>
You already have working handleChange() function, which will work perfectly.
CodePudding user response:
Instead of using JS to do this use the in-build HTML form validation.
Add a required attribute and a regex pattern to the input. The form won't submit until the input has been validated. And you'll also get little tool-tips to explain what the issue is if you try to submit and the input isn't validated.
In this case the regex reads:
^ - start of the value
[0-9]{2}- two numbers followed by a dash
[0-9]{8}- eight numbers followed by a dash
[0-9] - one number
$ - end of the value
const { useState } = React;
function Example() {
const [input, setInput] = useState('');
function handleSubmit(e) {
e.preventDefault();
console.log(`Submitted: ${input}`);
}
function handleChange(e) {
setInput(e.target.value);
}
return (
<form onSubmit={handleSubmit}>
<input
onChange={handleChange}
placeholder="dd-dddddddd-d"
pattern="^[0-9]{2}-[0-9]{8}-[0-9]$"
value={input}
required
/>
<button type="submit">Submit</button>
</form>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
input, button { font-size: 1.2em; }
input { padding: 0.3em; }
input:invalid { border: 1px solid red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>