bietkul / react-reactive-form

Angular like reactive forms in React.
MIT License
309 stars 32 forks source link

How to use FormGenerator correctly with Typescript? #20

Closed jfbloom22 closed 6 years ago

jfbloom22 commented 6 years ago

I was about to switch an entire project from React to Angular for the model based forms. I thought maybe I could write a React form builder and follow the patterns in Angular... then I found this plugin! Whooohooo!

My question is how to properly use the FormGenerator with Typescript.

If I follow the example in the Readme, Typescript gives me this error: Property 'userForm' does not exist on type 'UserForm'. (it is called loginForm in the Readme) So I define it like this:

export default class UserForm extends React.Component<Iprops, Istate> {
  userForm = FormBuilder.group({
      username: ["", Validators.required]
  });

this.userForm is replaced with the dynamically generated form when this.setForm runs and everything appears to be working great. Is this the best way to write this?

full component:

import * as React from "react";
import { Validators, FormGenerator, FormBuilder } from "react-reactive-form";
// Input component
const TextInput = ({ handler, touched, hasError, meta }: any) => (
  <div>
    <input placeholder={`Enter ${meta.label}`} {...handler()} />
    <span>
      {touched && hasError("required") && `${meta.label} is required`}
    </span>
  </div>
);
// Checkbox component
const CheckBox = ({ handler }: any) => (
  <div>
    <input {...handler("checkbox")} />
  </div>
);
// Field config to configure form
const fieldConfig = {
  controls: {
    username: {
      options: {
        validators: Validators.required
      },
      render: TextInput,
      meta: { label: "Username" }
    },
    password: {
      options: {
        validators: Validators.required
      },
      render: TextInput,
      meta: { label: "Password" }
    },
    rememberMe: {
      render: CheckBox
    },
    $field_0: {
      isStatic: false,
      render: ({ invalid, meta: { handleReset } }: any) => (
        <div>
          <button type="button" onClick={handleReset}>
            Reset
          </button>
          <button type="submit" disabled={invalid}>
            Submit
          </button>
        </div>
      )
    }
  }
};
interface Iprops extends React.Props<{}> {
  handleSubmit: any;
}
interface Istate {
  signupForm: any;
}
export default class UserForm extends React.Component<Iprops, Istate> {
  userForm = FormBuilder.group({
      username: ["", Validators.required]
  });
  constructor(props: Iprops) {
    super(props);
    this.state = {
      signupForm: {}
    };
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleReset = () => {
    this.userForm.reset();
  };
  handleSubmit = (e: React.MouseEvent<HTMLFormElement>) => {
    e.preventDefault();
    console.log("Form values", this.userForm.value);
  };
  setForm = (form: any) => {
    this.userForm = form;
    this.userForm.meta = {
        handleReset: this.handleReset
    }
  };
  render() {
    return (
      <form onSubmit={this.handleSubmit} className="user-form">
        <FormGenerator onMount={this.setForm} fieldConfig={fieldConfig} />
      </form>
    );
  }
}
bietkul commented 6 years ago

Hey buddy, You don't need an another control userForm, FormGenerator will create a control itself if no control is defined in fieldConfig explicitly. So you can just define a control like that:

import { AbstractControl } from 'react-reactive-form';
...
export default class UserForm extends React.Component<Iprops, Istate> {
public userForm: AbstractControl
....
jfbloom22 commented 6 years ago

awesome works perfectly! thx @bietkul!