In React Native using <TextInput/>
, I am trying to make /
appear only when the <TextInput/>
is focused, and would stay there if another input is entered in. Currently, the format is MM/YY
, so when the user types the third digit, it would go after the /
, and if the user were to press back, it would delete the digit before the /
.
So what would be the right approach to implementing previously mentioned? Thank you and will be sure to accept the answer.
I tried the following but getting an error with length, and this is only adding /
after two digits have been entered:
_changeCardExpiry(value) {
if (value.indexOf('.') >= 0 || value.length > 5) {
return;
}
if (value.length === 2 && this.state.cardExpiry.length === 1) {
value += '/'
}
//then update state cardExpiry
}
...
<TextInput
onChangeText={this._changeCardExpiry.bind(this)}
placeholder='MM/YY'
value={cardExpiry}
/>
In React Native using <TextInput/>
, I am trying to make /
appear only when the <TextInput/>
is focused, and would stay there if another input is entered in. Currently, the format is MM/YY
, so when the user types the third digit, it would go after the /
, and if the user were to press back, it would delete the digit before the /
.
So what would be the right approach to implementing previously mentioned? Thank you and will be sure to accept the answer.
I tried the following but getting an error with length, and this is only adding /
after two digits have been entered:
_changeCardExpiry(value) {
if (value.indexOf('.') >= 0 || value.length > 5) {
return;
}
if (value.length === 2 && this.state.cardExpiry.length === 1) {
value += '/'
}
//then update state cardExpiry
}
...
<TextInput
onChangeText={this._changeCardExpiry.bind(this)}
placeholder='MM/YY'
value={cardExpiry}
/>
Share
Improve this question
asked Oct 28, 2016 at 22:00
user3259472user3259472
2
- What is the error with length? – PaulBGD Commented Oct 28, 2016 at 23:37
-
@PaulBGD I get an error at
if (text.length === 2 && this.state.cardExpiry.length === 1)
sayingCannot read property 'length' of undefined.
– user3259472 Commented Nov 2, 2016 at 22:08
4 Answers
Reset to default 5 +25In formattion you actually need 3 functions one is for formatting the actual value, second is for converting formatted value back to actual value and the third is needed for checking whether the entered input so far can be valid or not. For example for a date input entered letter inputs should be disregarded, but at the same time 99 should be disregarded as it is not a valid input for a month. So for your specific case the following structure should work for you (in this example inputToValue
function both checks whether the entered input is valid and set state according to it):
formatFunction(cardExpiry = ""){
//expiryDate will be in the format MMYY, so don't make it smart just format according to these requirements, if the input has less than 2 character return it otherwise append `/` character between 2nd and 3rd letter of the input.
if(cardExpiry.length < 2){
return cardExpiry;
}
else{
return cardExpiry.substr(0, 2) + "/" + (cardExpiry.substr(2) || "")
}
}
inputToValue(inputText){
//if the input has more than 5 characters don't set the state
if(inputText.length < 6){
const tokens = inputText.split("/");
// don't set the state if there is more than one "/" character in the given input
if(tokens.length < 3){
const month = Number(tokens[1]);
const year = Number(tokens[2]);
//don't set the state if the first two letter is not a valid month
if(month >= 1 && month <= 12){
let cardExpiry = month + "";
//I used lodash for padding the month and year with zero
if(month > 1 || tokens.length === 2){
// user entered 2 for the month so pad it automatically or entered "1/" convert it to 01 automatically
cardExpiry = _.padStart(month, 2, "0");
}
//disregard changes for invalid years
if(year > 1 && year <= 99){
cardExpiry += year;
}
this.setState({cardExpiry});
}
}
}
}
render(){
let {cardExpiry} = this.state;
return <TextInput
value = {this.formatFunction(cardExpiry)}
onChangeText={this.inputToValue.bind(this)}/>;
}
Not plete solution, but it solves similar problem - masking bluetooth address
AB
AB:C
AB:CD:EF:GH:IJ:KL
/*
Usage:
import { TextInput } from '../utils/input'
const MaskedTextInput = withMask(TextInput)
<MaskedTextInput
placeholder="Printer address"
value={ printerId }
onChange={this.handlePrinterAddressChange}
/>
*/
import React, { Component } from 'react'
import { View } from 'react-native'
export const withMask = (WrappedComponent) => class Wrapper extends Component {
constructor(props) {
super()
this.state = { value: props.value }
}
onChange(event) {
let value = event.nativeEvent.text
const rawValue = event.nativeEvent.text.replace(/:/g, '')
if (rawValue) {
value = rawValue.match(/.{1,2}/g).join(':').toUpperCase()
}
this.setState({value})
if (this.props.onChange) {
event.nativeEvent.text = value
this.props.onChange(event)
}
}
render() {
return <WrappedComponent
{...this.props}
onChange={(event) => this.onChange(event)}
value={this.state.value}
/>
}
}
you can use this func from onChangeText;
Don't forget to bind method inside constructor ;
this.fixCardText = this.fixCardText.bind(this)
fixCardText(text){
if(text.length == 2 && this.state.text.length == 1){
text += '/'
}else if(text.length == 2 && this.state.text.length == 3){
text = text.substring(0 , text.length-1)
}
this.setState({text:text})
}
your text input should be like;
<TextInput
value = {this.state.text}
onChangeText={(text)=>{this.fixCardText(text)}}
/>
You can use 2 TextInputs instead on one to keep it simple.
here's a way of doing that.
<TextInput
onChangeText={() => alert('update state with Expiry Month')}
placeholder="MM/"
value={expiryMonth}
/>
{expiryMonth.length > 0 && <Text> / </Text>}
<TextInput
onChangeText={() => alert('update state with Expiry Year or do whatever you need with whole Expiry Date')}
placeholder="YY"
value={expiryYear}
/>