nosir / cleave.js

Format input text content when you are typing...
http://nosir.github.io/cleave.js
Apache License 2.0
17.96k stars 1.62k forks source link

Is there a way to use this with Redux Form? #159

Closed firaskrichi closed 7 years ago

andys8 commented 7 years ago

Like any react component you can connect it to Redux and dispatch actions when events occur. Can you be more specific?

kristiehoward commented 7 years ago

I'd love to use this in my React + Redux project in Redux-Forms - however, we use specific, custom components and redux form controls the value of the <input> in our form. Thus all we can do is format the data for display.... I would love to use the functions from Cleave using something like:

      <Input
        {...phone}
        errorText={phone.touched && phone.error}
        id={'phone'}
        placeholder="Phone Number"
        value={Cleave.getFormattedValue(value)}
      />

Does that make sense? We can't adopt the entire <Cleave> input component because we need to preserve the behavior of our forms. Any suggestions?

Great work!

nosir commented 7 years ago

Thanks guys, let me have a look at redux-form first

marcin-krysiak commented 7 years ago

I'm having problem with cleave and redux-form handling backspace. It works like double backspace. Problem described here - https://github.com/nosir/cleave.js/issues/129 It would be great to see them working properly together.

firaskrichi commented 7 years ago

@nosir any news on this one?

hielfx commented 7 years ago

Hey guys I got it working like this:

First I installed cleave.js via npm, then added the cleave.min.js to my index.html (not the react one, but the common js one) <script src='%PUBLIC_URL%/cleave.min.js'></script>

Then I created my componente like any react component with redux form could be:

import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { reduxForm, Field } from 'redux-form';
import { Button } from 'react-bootstrap';

import Input from './input'; // Custom input

const form = reduxForm({
  form: 'bankAccountPaymentForm'
});

class BankAccountPayment extends Component {

  static propTypes = {
    handleFormSubmit: PropTypes.func.isRequired,
  }

  componentDidMount() {
    /* Inject cleave.js into the redux form Field component. 
       The Input component value will be the cleave formatted value
       Disable the next line: eslint will complain cause Cleave is not imported in the file.*/
    new Cleave('#bankAccountNumber', { // eslint-disable-line
      blocks: [4, 4, 2, 10],
    });
  }

  handleSubmit = (values) => {
    this.props.handleFormSubmit({ type: 'bankAccount', ...values });
  }

  render() {
    const { handleSubmit } = this.props;
    return (
      <div className="centerContentColumn">
        <form onSubmit={handleSubmit(this.handleSubmit)}>
          <Field name="bankAccountNumber" id="bankAccountNumber" placeholder="Bank account number" component={Input} /> // Input is a custom input component with our styles and so on
          <div className="centerContentColumn">
            <Button type="submit" bsStyle="success">Submit</Button>
          </div>
        </form>
      </div>
    )
  }
}

export default form(BankAccountPayment);

Results: captura de pantalla de 2017-07-07 13-46-46 ezgif

I hope it is useful to you :)

nosir commented 7 years ago

Hi guys, @firaskrichi, sorry for the delay, I had a look at redux-form, good news, seems it's fairly easy to integrate cleave.js into it.

So you only need to create a stateless function for the redux-form Field, http://redux-form.com/6.8.0/docs/api/Field.md/#2-a-stateless-function

How to:

  1. Create a stateless component function:
    
    import Cleave from 'cleave.js/react';

const rederCleaveField = (field) => ( <Cleave {...field.input} options={{creditCard: true}} /> )

2. Render it into the normal `redux-form` `Field`

![image](https://user-images.githubusercontent.com/352617/27959608-7b42e3c4-636b-11e7-8cce-d6044bb25d60.png)

@hielfx That also works, thanks :)
Tommy10802 commented 7 years ago

Thanks for your solution. But in redux field is formatted value, is it possible to save into redux field rawValue?

nosir commented 7 years ago

@Tommy10802 Redux-form only grabs the exact value from what is it in the input field, so you will need to register onChange event on cleave component and get the raw value from event callback manually.

Or just put something in redux action to sanitize the input value.

nosir commented 7 years ago

I think the original issue was solved, will close it for now. Thanks guys.

nathanvale commented 7 years ago

A better way would be to use the normalize abstraction at redux Field level

`import Cleave from 'cleave.js';

function dobDay(value) {

const input = document.createElement("input");
input.value = value;

var cleave = new Cleave(input, {
    date: true,
    datePattern: ['d', 'm', 'Y']
});

return cleave.getFormattedValue();

}

and then use it this way

<Field name='dob' normalize={dobDay} />

DarrylD commented 7 years ago

@nathanvale you saved my life right now... works perfect!

nosir commented 7 years ago

Thanks, I will add it to how-to doc

DarrylD commented 7 years ago

@nathanvale are you having issues deleting the forward slashes? I can only delete the year and it stops at the forward slash.

dgulabs commented 5 years ago

I'm getting:

Uncaught TypeError: cleave.getFormattedValue is not a function

I see that the function is not available, any idea? I'm using 1.4.4.

devakone commented 5 years ago

Seeing that error as well, seems the api was updated

lzhuor commented 5 years ago

@devakone , did you do import Cleave from 'cleave.js' or import Cleave from 'cleave.js/React'.

The first one should work.

lzhuor commented 5 years ago

@DarrylD For me, I solved it by doing sth. like this:

  const formattedValue = cleave.getFormattedValue()

  return formattedValue[formattedValue.length - 1] === delimiter
    ? formattedValue.slice(0, -1)
    : formattedValue

Basically we have to check if the last char is the delimiter, if yes, remove it.

benjamindulau commented 5 years ago

I'm trying to use the same approach for formatting phone numbers:

import React from 'react';
import Cleave from "cleave.js";
import "cleave.js/dist/addons/cleave-phone.i18n";

const FormattedPhoneNumber = ({ phone }) => {
    const input = document.createElement("input");
    input.value = phone;

    const cleave = new Cleave(input, {
        phone: true,
        phoneRegionCode: 'FR'
    });

    return cleave.getFormattedValue();
};

export default FormattedPhoneNumber;

But I get the following error:

cleave.js:224 Uncaught Error: [cleave.js] Please include phone-type-formatter.{country}.js lib
    at Cleave.initPhoneFormatter (cleave.js:224)
    at Cleave.init (cleave.js:165)
    at new Cleave (cleave.js:139)
    at FormattedPhoneNumber (FormattedPhoneNumber.js:9)
    at renderWithHooks (react-dom.development.js:13452)
    at mountIndeterminateComponent (react-dom.development.js:15608)
    at beginWork (react-dom.development.js:16243)
    at performUnitOfWork (react-dom.development.js:20287)
    at workLoop (react-dom.development.js:20328)
    at HTMLUnknownElement.callCallback (react-dom.development.js:149)

Using version 1.4.10.

Any ideas?

neoswf commented 5 years ago

I got it as well - @DarrylD, implemented what @lzhuor wrote (and ill expend, for the future usage of newbies users)

Setting up the redux-form component

<Field
    id="date"
    name="date"
    placeholder="Data de emissão"
    type="text"
    required
    component="input"
    normalize={this.handleFormat}
/>

Pass options to function


const cleaveFieldOptions = {
    date: true,
    delimiter: '/',
    datePattern: ['d', 'm', 'Y']
};

handleFormat = value => {
    return fieldFormat(value, cleaveFieldOptions);
};

Return Field updated treated format

export function fieldFormat(value, options) {
    // Create virtual cleave object. Manipulate its value.
    const input = window.document.createElement('input');
    input.value = value;
    var cleaveNewObject = new Cleave(input, options);

    // Format it to resolve the bug - @DarrylD Solution
    const formattedValue = cleaveNewObject.getFormattedValue();
    return formattedValue[formattedValue.length - 1] === '/'
        ? formattedValue.slice(0, -1)
        : formattedValue;
}
lzhuor commented 4 years ago

It seems this solution does not work on Android anymore.