react-admin - How to set input values based on another
Asked Answered
V

2

9

i'm trying to create a ZIP code input that loads street, state and city values automatically in a Create form using React-Admin. How can I populate the inputs based on the onBlur event of the zip code input? The best result i achieved is the following scenario:

I created a custom component that has 4 Inputs: zip code (in my country is called CEP), street address, state and city. Then I added an onBlur event on the zip input and set the value on the inputs based on state attributes. Here is the code

class CustomAddressInput extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      cep : '',
      address : '',
      uf : '',
      city : '',
    }
    this.setAddress = this.setAddress.bind(this);
  }
  setAddress(e){
    if(e.target.value != undefined){
      endereco(e.target.value).then((result)=>{
        this.setState({
          cep: result.cep,
          address: result.logradouro,
          uf: result.uf,
          city: result.localidade
        });
      });
    }
  }

  render() {
    const { classes } = this.props;
    return (
      <TextInput label="CEP" source="cep" onBlur={(e) => this.setAddress(e)} defaultValue={this.state.cep} />
      <TextInput label="Endereco" source="address" defaultValue={this.state.address}/>
      <SelectInput label="Estado" source="state" choices={stateList} defaultValue={this.state.uf}/>
      <TextInput label="Cidade" source="city" defaultValue={this.state.city}/>
    );
  }
}
export default withStyles(styles)(CustomAddressInput);

And i'm using it on a Create

...
<Create {...props}>
  <SimpleForm>
    <TextInput label="Nome" source="name"/>
    <TextInput label="CPF/CNPJ" source="cpfcnpj"/>
    <TextInput label="Email" source="email"/>
    <TextInput label="Senha" source="password" type="password" />
    <TextInput label="Telefone" source="phone" type="tel"/>
    <CustomAddressInput/>
    <BooleanInput label="Pode criar outros usuários do sistema" source="canCreateUser" defaultValue={false}/>
    <BooleanInput label="Pode gerenciar projetos" source="canCreateProjects" defaultValue={false}/>
    <BooleanInput label="Pode visualizar honorários" source="canSeeFees" defaultValue={false}/>
  </SimpleForm>
</Create>
...

I know i'm setting the values in a wrong way because when the values are set, all the create form is wiped. What should i do? I'm not familiar developing with React. Thanks in advance

Volta answered 18/7, 2018 at 14:26 Comment(0)
V
9

I think i found the proper way of doing this. I moved the auto fill address function to a onChange event on the SimpleForm element and removed it from the CEP input. It works like a charm now. Here is the code:

Custom Address input

export default withStyles(styles)(
  class CustomAddressInput extends React.Component {
    render() {
      return (
        <div>
          <div>
            <TextInput label="CEP" source="cep" parse={parseCep} format={parseCep} validate={validateCEP}/>
          </div>
          <div>
            <TextInput label="Endereco" source="address"/>
            <SelectInput label="Estado" source="state" choices={stateList}/>
            <TextInput label="Cidade" source="city"/>
          </div>
        </div>
      );
    }
  }
);

And the Create Component

const autoFillAddress = (event)=>{
  if(event.cep){
    if(event.cep.length === 9){
      endereco(event.cep).then((result)=>{
        event.address = result.logradouro;
        event.state = result.uf;
        event.city = result.localidade;
      });
    }
  }
}
...
<Create {...props}>
  <SimpleForm onChange={autoFillAddress}>
    <div>
      <TextInput label="Nome" source="name" validate={validateName}/>
      <TextInput label="CPF/CNPJ" source="cpfcnpj" parse={parseCpfCnpj} format={parseCpfCnpj} validate={validateCpfCnpj}/>
    </div>
    <div className={classes.packTres, classes.fullInput}>
      <TextInput label="Email" source="email"validate={validateEmail}/>
      <TextInput label="Senha" source="password" type="password" validate={validatePassword}/>
    </div>
    <TextInput label="Telefone" source="phone" type="tel" parse={parsePhone} format={parsePhone} validate={validatePhone}/>
    <CustomAddressInput />
    <BooleanInput label="Pode criar outros usuários do sistema" source="canCreateUser" defaultValue={false}/>
    <BooleanInput label="Pode gerenciar projetos" source="canCreateProjects" defaultValue={false}/>
    <BooleanInput label="Pode visualizar honorários" source="canSeeFees" defaultValue={false}/>
  </SimpleForm>
</Create>
...
Volta answered 19/7, 2018 at 11:51 Comment(1)
This works for me on a create page in React-Admin as well. Now I'm trying to figure out how to do the same on an edit page, as this approach only seems to work on a Create.Rozek
B
0

After spending more hours than i would like trying to achieve this, decided to share my findings. This is currently working along with react-hook-form.

  1. Make sure you create a new component for the zip field like ZIPComponent bellow.
  2. Inside custom components you can use the useFormContext from react-hook-form to correctly interact with the form state and methods. Along with setValue you can use getValues, reset and others to manipulate any react-admin field.
  3. You can then use this ZIPComponent inside both <Create> and <Edit> react-admin pages where you would place the zip code field

import { useFormContext } from "react-hook-form";
import axios from "axios";
import { TextInput } from "react-admin";
export const ZIPComponent = () => {
  const { setValue } = useFormContext();

  const fetchZip = async (cep: any) => {
    try {
      const response = await axios.get(`http://viacep.com.br/ws/${cep}/json`);

      const { data } = response;

      setValue("addressZipCode", cep);
      setValue("addressState", data.uf);
      setValue("addressCity", data.localidade);
      setValue("addressNeighborhood", data.bairro);
      setValue("address", data.logradouro);
      return { ...data };
    } catch (error) {
      console.error("Erro ao buscar CEP: ", error);
      return null;
    }
  };

  return (
    <TextInput
      source="addressZipCode"
      label="CEP"
      fullWidth
      onChange={(e) => {
        if (e.target.value.length >= 7) fetchZip(e.target.value);
      }}
      format={(v: any) => formatZipCode(v)}
    />
  );
};
Balch answered 27/9, 2023 at 17:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.