New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

a-plus-forms

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

a-plus-forms

A+ forms. Would use again

  • 0.5.3
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
26
decreased by-81.29%
Maintainers
1
Weekly downloads
 
Created
Source

A+ Forms

I love making forms. Forms are fantastic usage of my time

-- Nobody ever

Let me introduce you to the A+ forms, a react forms library that makes forms creation a civilized and enjoyable process.

Features

There are two principles A+ forms are build on top:

  1. zero learning curve, all interfaces are 100% familiar and act as expected
  2. just give me data, all routine processes are taken care of, the forms just send the user's input as an object via the onSubmit prop.

If you want to more, here is more:

  1. ~7.5k in size and zero deps
  2. used in production for ages and scales to complex forms nicely
  3. easy custom layouts support
  4. easy custom validators support
  5. easy custom inputs support
  6. easy compound inputs support
  7. allows to re-wrap existing inputs into new specialized ones

Usage & Examples

npm install a-plus-forms
yarn add a-plus-forms

The basic example

First thing to understand about A+ forms is that the inputs in this system are abstractions. Those inputs convey the structure of the form, and, depending on a context, they can render different desirable results.

import { Form, EmailInput, PasswordInput } from 'a-plus-forms';

<Form onSubmit={signInAction}>
  <EmailInput name="username" label="Username" />
  <PasswordInput name="password" label="Password" />
  <button>Sign In</button>
</Form>

In this example, the signInAction will receive an object that looks like this:

{
  username: '...',
  password: '...'
}

Input Field Layouts

Inputs in A+ forms are simply standard interfaces, to style them according to your needs we use the concept of layouts. Which are basically dumb components that consistently render all inputs in a form. For example:

const MyLayout = ({ label, error, input }) =>
  <div className="my-input">
    <label>{label}</label>
    <div className="input-container">{input}</div>
    {error ? <small className="error">{error}</small> : null}
  </div>;

In this case, label is the label one specifies in the input props. error is a validation error string, and input is the actual input field element.

Now that you have a layout, you have options:

// set this layout as a default layout globally
import { config } from 'a-plus-forms';
config.defaultLayout = MyLayout;

// specify the layout as a default via a layout provider
import { LayoutProvider } from 'a-plus-forms';
<App>
  <LayoutProvider layout={MyLayout}>
    // content....
  </LayoutProvider>
</App>

// render a specific form with this layout
<Form onSubmit={handler} layout={MyLayout}>
  // all inputs here will use that layout
</Form>

// render an individual input with this layout
<EmailInput name="username" layout={MyLayout} />

You also can render any input without any layout decoration whatsoever, which is useful when you start making compound.

<TextInput name="something" layout={null} />
// will render simply this
<input type="text" name="something" />

A basic react/redux wiring

import { connect } from 'react-redux';
import { Form, EmailInput, PasswordInput } from 'a-plus-forms';
import { signIn } from './actions';

const dispatchToProps = dispatch => ({
  signIn({ username, password }) {
    return dispatch(signIn({ username, password }));
  }
});

const SignInForm = ({ signIn }) =>
  <Form onSubmit={signIn}>
    <EmailInput name="username" label="Username" />
    <PasswordInput name="password" label="Password" />
    <button>Sign In</button>
  </Form>;

export default connect(null, dispatchToProps)(SignInForm);

NOTE: if the signIn action returns a Promise the form will automatically mark the form as disabled for the duration of the server request.

Validation Story

A+ forms have several validation options. A very simple one is to just pass a function into the schema prop:

const validate = (data) => {
  if (!data.username) return { username: 'is required' };
};

const SignInForm = ({ signIn }) =>
  <Form onSubmit={signIn} schema={validate}>
    <EmailInput name="username" label="Username" />
    <PasswordInput name="password" label="Password" />
  </Form>;

Whenever the validation function returns data, the form will automatically pass the errors into the respective fields. All the pristine/dirty states are automatically taken care of.

One also can create custom re-usable validators and pass them around via a ValidatorProvider component:

import { ValidatorProvider } from 'a-plus-forms';

class CustomValidator {
  errorsFor(data) {
    if (!data.username) {
      return { username: 'is required' };
    }
  }
}

<ValidatorProvider validator={CustomValidator}>
  <SignInForm signIn={signIn} />
</ValidatorProvider>

Async validators are also supported out of the box:

class CustomValidator {
  async errorsFor({ username }) {
    const taken = await api.get('/username-check', { username });

    if (taken) {
      return { username: 'is already taken' };
    }
  }
}

For a reference implementation of custom validators, please see the JSON Schema validator plugin

Custom Inputs

Where A+ forms really shine is the ease of creation of new custom inputs. A very simple use case would look somewhat like this:

import { field } from 'a-plus-forms';

const MyCustomInput = ({ value, onChange }) =>
  <input type="text" value={value} onChange={onChange} />;

export default field(MyCustomInput);

Essentially, the field decorator is where the magic happens, it takes care of all the state, errors and layouts management for you. The only think you need to provide back a controlled input that understands value and onChange.

You can also re-use/re-wrap existing inputs to build extra functionality into the fields:

import { field, TextInput } from 'a-plus-forms';

const PhoneNumberInput = props => {
  const { onChange, ...rest } = props;
  const format = userInput => toPhoneNumber(userInput);

  return (
    <TextInput
      {...rest}
      layout={null} // <- render the input only
      onChange={value => onChange(format(value))}
    />
  );
}

For more examples, please see the collection of the built in inputs

Compound/Nested Inputs

One can easily combine several inputs into one compound input if needed.

import { field, TextInput } from 'a-plus-forms';

const AddressInput = field({ nested: true })(
  () => (
    <div>
      <TextInput name="country" layout={false} />
      <TextInput name="state" layout={false} />
      <TextInput name="city" layout={false} />
    </div>
  )
);

const ProfileForm = ({ onSubmit }) =>
  <Form onSubmit={onSubmit}>
    <AddressInput name="address" />
  </Form>;

When the user submits the form, the onSubmit function will receive a structure that looks like so:

{
  address: {
    country: '...',
    state: '...',
    city: '...'
  }
}

All source code in this repository released under the terms of the ISC license.

Copyright (C) 2017 Nikolay Nemshilov

Keywords

FAQs

Package last updated on 07 Nov 2017

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc