npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

form-container

v0.2.8

Published

Lightweight React form container with validation (written in TypeScript)

Downloads

85

Readme

Form Container

Build Status npm version Coverage Status Greenkeeper badge

Form container is a lightweight React form container with validation (written in TypeScript). It allows you to use both HTML5 form validation and custom validation functions.

built with ❤️ at Appfocused

TL;DR

It provides your child form with 2 objects of props:

  • form - all data on your form values, states, errors and warnings
  • formMethods - methods to bind your input controllers and manipulate the form model

Demos:

Installation

Form Container is available as the form-container package on npm.

Install it in your project with npm or yarn

npm install form-container

or

yarn add form-container

Getting started

connectForm([validators], [formConfig])(WrappedComponent)

form-container exposes connectForm function to connect an arbitrary form component (WrappedComponent). It does not modify the component class passed to it; instead, it returns a new, connected component class for you to use.

Arguments

  • [validators: ValidationRule[]] (Array): An array of rules can be provided to validate the form model against them. Rules are executed in a sequence that is defined in the array.

  • [formConfig: IFormConfig] (Object): An object contains initial configuration for the form

    • initialModel: Partial<T> — object provides initial values to the form fields
    • middleware: (props: T) => T & M — function transforms props passed to the wrapped component
    • onInputBlur: (e: React.ForcusEvent<any>) => void — function is called on every blur on an input field within the form. Adding a custom onBlur to the input field itself is not recommended, use this method instead

Validation

HTML5 validation example

import * as React from 'react';

// bare minimum import
import { connectForm, IFormProps } from 'form-container';

// IFormProps interface contains the props that are passed down from form-container
interface IProps extends IFormProps {}

export class Form extends React.Component<IProps, {}> {
    handleSubmit = (e: any) => {
        e.preventDefault();
        const { model } = this.props.form;
        console.log(model);
    }

    render() {
        const { validationErrors, touched } = this.props.form;
        const { bindInput, bindNativeInput } = this.props.formMethods;
        return (
            <form onSubmit={this.handleSubmit}>
                <div>
                    <label>
                        Required field
                        <input
                            {/* HTML attribute to validate required field */}
                            required={true}
                            {/* this is how you bind input to a form-container */}
                            {...bindNativeInput('test')}
                        />
                        <small>{touched.test && validationErrors.test}</small>
                    </label>
                </div>
                <div>
                    <button type="submit">Submit</button>
                </div>
            </form>
        );
    }
}

// no custom validators
const validators: any[] = [];

const formConfig = {
    initialModel: {
        test: 'foo'
    }
}

// attaching our Form to form-container with validation
export default connectForm(validators, formConfig)(Form);

Custom validation example

// components/Form.tsx
import * as React from 'react';
import { connectForm, ValidationRuleFactory, Condition, IFormProps } from 'form-container';

interface IProps extends IFormProps {}

// arbitrary form component
export class Form extends React.PureComponent<IProps, {}> {
    render() {
        const { validationErrors, touched } = this.props.form;
        const { bindInput } = this.props.formMethods;
        return (
            <form>
                <div>
                    <label>
                        Test:
                        <input
                            {/* this is how you bind input to a form-container */}
                            {...bindInput('test')}
                        />
                        <small>{touched.test && validationErrors.test}</small>
                    </label>
                </div>
            </form>
        );
    }
}

// custom validator
const hasMoreThan6Chars: Condition = value => (value ? value.length > 6 : true);
export const strongPassword = ValidationRuleFactory(
    hasMoreThan6Chars,
    'We recommend password to contain more than 6 characters for security purposes',
    'warning'
);

// all validators for the form
const validators = [
    isRequired('test'),
    strongPassword('test')
];

// attaching our Form to form-container with validation
export default connectForm(validators)(Form);

Submit validation example

Form Container exposes SubmissionError, a throwable error which can be used to set submit validation for fields in response to promise rejection because of validation errors.

A wrapped form component has access to handleSubmit via formMethods:

handleSubmit([submit])

Arguments

  • [submit: (model) => Promise]: A submit handler function which has access to the form model and returns a Promise.

Inside the provided submit function you can throw a SubmissionError which will be caught by handleSubmit, and the submission errors will be set for each key in the form state under a key submitErrors, e.g.:

throw new SubmissionError({
    test: "Don't like this..."
});
// components/Form.tsx
import * as React from 'react';
import { connectForm, ValidationRuleFactory, Condition, IFormProps } from 'form-container';

interface IProps extends IFormProps {}

interface IFormModel {
    test: string;
}

// arbitrary form component
export class Form extends React.PureComponent<IProps, {}> {
    submit = (model: IFormModel) => {
        return fetch('http://dummyurl.com', {
            method: 'POST',
            body: model
        }) // your Promise based HTTP client of choice
            .catch(error => {
                const { data: { code }, status } = error.response;
                if (status === 422 && code === 'xyz') {
                    throw new SubmissionError({
                        foo: "Back-end doesn't like this..."
                    });
                } else {
                    /// handle other errors accordingly
                }
            });
    };

    render() {
        const { validationErrors, submitErrors, touched } = this.props.form;
        const { bindInput, handleSubmit } = this.props.formMethods;
        return (
            <form>
                <div>
                    <label>
                        Test:
                        <input {...bindInput('test')} />
                        <small>
                            {(touched.test && validationErrors.test) || submitErrors.test}
                        </small>
                    </label>
                </div>
                <button onClick={handleSubmit(this.submit)} />
            </form>
        );
    }
}

export default connectForm<IFormModel>()(Form);