2016-04-07 22:21:01 +09:00
# Nova Forms
2016-03-30 19:08:06 +09:00
2016-12-20 09:27:16 +09:00
This package provides a `SmartForm` component that works with the schema extension defined in the [smart-methods ](https://github.com/meteor-utilities/smart-methods ) package to let you easily generate new document and edit document forms.
2016-03-30 19:08:06 +09:00
### Install
2016-04-04 11:30:14 +09:00
`meteor add nova:forms`
2016-03-30 19:08:06 +09:00
2016-04-07 15:53:18 +09:00
(note: package is not published yet)
### Features
This package can generate new document and edit document forms from a [SimpleSchema ](https://github.com/aldeed/meteor-simple-schema ) schema. Features include:
- Error handling.
- Bootstrap-compatible.
- Cross-component communciation (prefill a field based on another).
- Callbacks on form submission, success, and failure.
- Support for basic form controls (input, textarea, radio, etc.).
- Support for custom form controls.
- Submission to Meteor methods.
2016-06-13 16:05:46 +09:00
### NPM Dependencies
```
react react-intl formsy-react react-bootstrap formsy-react-components
```
2016-06-14 10:45:03 +09:00
You also need to load Bootstrap's CSS separately.
2016-04-07 15:53:18 +09:00
### Usage
2016-04-07 16:05:40 +09:00
Example schema:
```js
2017-01-10 10:09:24 +01:00
import SimpleSchema from 'simpl-schema';
2016-04-07 16:05:40 +09:00
import BodyFormControl from './components/BodyFormControl.jsx';
const isLoggedIn = (user) => !!user;
const isOwner = (user, document) => user._id === document.userId;
const isAdmin = (user) => user.isAdmin;
const PostsSchema = new SimpleSchema({
postedAt: {
type: Date,
optional: true
2016-12-06 10:55:47 +01:00
// no insertableBy or editableBy means this field won't appear in forms
2016-04-07 16:05:40 +09:00
},
title: {
type: String,
optional: false,
max: 500,
2016-12-06 10:55:47 +01:00
insertableBy: isLoggedIn,
editableBy: isOwner,
2016-04-07 16:05:40 +09:00
control: "text",
order: 1
},
body: {
type: String,
optional: true,
max: 3000,
2016-12-06 10:55:47 +01:00
insertableBy: isLoggedIn,
editableBy: isOwner,
2016-04-07 16:05:40 +09:00
control: BodyFormControl,
order: 2
},
sticky: {
type: Boolean,
optional: true,
defaultValue: false,
2016-12-06 10:55:47 +01:00
insertableBy: isAdmin,
editableBy: isAdmin,
2016-04-07 16:05:40 +09:00
control: "checkbox",
order: 3
},
}
```
2016-04-07 15:53:18 +09:00
New document form:
```jsx
2016-12-20 09:27:16 +09:00
< SmartForm
2016-04-07 15:53:18 +09:00
collection={Posts}
methodName="posts.new"
/>
```
Edit document form:
```jsx
2016-12-20 09:27:16 +09:00
< SmartForm
2016-04-07 15:53:18 +09:00
collection={Posts}
2016-10-14 08:47:18 +02:00
methodName="posts.edit"
document={post}
2016-04-07 15:53:18 +09:00
labelFunction={capitalize}
successCallback={closeModal}
/>
```
2016-04-04 11:30:14 +09:00
### Props
2016-03-30 19:08:06 +09:00
2016-04-07 15:53:18 +09:00
###### `collection`
2016-03-30 19:08:06 +09:00
2016-04-04 11:30:14 +09:00
The collection in which to edit or insert a document.
2016-03-30 19:08:06 +09:00
2016-06-13 16:05:46 +09:00
###### `schema`
If you prefer, you can also specify a schema instead of a collection.
2016-04-07 15:53:18 +09:00
###### `document`
2016-03-30 19:08:06 +09:00
2016-04-04 11:30:14 +09:00
If present, the document to edit. If not present, the form will be a “new document” form.
2016-03-30 19:08:06 +09:00
2016-04-07 15:53:18 +09:00
###### `currentUser`
2016-04-04 11:30:14 +09:00
The current user.
2016-06-17 13:05:17 +09:00
###### `submitCallback(data)`
2016-04-04 11:30:14 +09:00
2016-06-17 13:05:17 +09:00
A callback called on form submission on the form data. Should return the `data` object as well.
2016-04-04 11:30:14 +09:00
2016-04-07 15:53:18 +09:00
###### `successCallback(document)`
2016-04-04 11:30:14 +09:00
A callback called on method success.
2016-04-07 15:53:18 +09:00
###### `errorCallback(document, error)`
2016-04-04 11:30:14 +09:00
A callback called on method failure.
2016-04-08 10:29:32 +09:00
###### `cancelCallback()`
If provided, will show a "cancel" link next to the form's submit button.
2016-04-07 15:53:18 +09:00
###### `methodName`
2016-04-04 11:30:14 +09:00
The name of the Meteor method to call.
2016-04-07 15:53:18 +09:00
###### `labelFunction`
2016-04-04 11:30:14 +09:00
A function to call on field names to get the label.
2016-04-07 15:53:18 +09:00
###### `prefilledProps`
2016-04-04 11:30:14 +09:00
2016-04-04 16:58:53 +09:00
A set of props to prefill for new documents.
### Collection Schema
This package generates forms based on the following special schema properties (see also the [Smart Methods ](https://github.com/meteor-utilities/smart-methods ) package:
2016-12-06 10:55:47 +01:00
###### `insertableBy(user)`
2016-04-04 16:58:53 +09:00
A function called on the `user` performing the operation, should return `true` or `false` . When generating a form for inserting new documents, the form will contain all the fields that return `true` for the current user.
2016-12-06 10:55:47 +01:00
###### `editableBy(user, document)`
2016-04-04 16:58:53 +09:00
Called on the `user` performing the operation, and the `document` being operated on, and should return `true` or `false` . When generating a form for editing existing documents, the form will contain all the fields that return `true` for the current user.
2016-04-07 15:53:18 +09:00
###### `control`
2016-04-04 16:58:53 +09:00
Either a text string (one of `text` , `textarea` , `checkbox` , `checkboxgroup` , `radiogroup` , or `select` ) or a React component.
2016-04-07 15:53:18 +09:00
###### `order`
2016-04-04 16:58:53 +09:00
A number corresponding to the position of the property's field inside the form.
2016-06-03 11:03:36 +09:00
###### `group`
An optional object containing the group/section/fieldset in which to include the form element. Groups have `name` , `label` , and `order` properties.
For example:
```js
postedAt: {
type: Date,
optional: true,
2016-12-06 10:55:47 +01:00
insertableBy: Users.isAdmin,
editableBy: Users.isAdmin,
2016-06-03 11:03:36 +09:00
publish: true,
control: "datetime",
group: {
name: "admin",
label: "Admin Options",
order: 2
}
},
```
Note that fields with no groups are always rendered first in the form.
2016-07-04 10:32:00 +09:00
###### `placeholder`
A placeholder value for the form field.
###### `beforeComponent`
A React component that will be inserted just before the form component itself.
###### `afterComponent`
A React component that will be inserted just after the form component itself.
2016-04-04 16:58:53 +09:00
### Context
2016-12-20 09:27:16 +09:00
The main `SmartForm` components makes the following objects available as context to all its children:
2016-04-04 16:58:53 +09:00
2016-05-26 15:52:35 +09:00
###### `autofilledValues`
2016-04-07 14:20:44 +09:00
2016-05-26 15:52:35 +09:00
An object containing optional autofilled properties.
2016-04-07 14:20:44 +09:00
2016-05-26 15:52:35 +09:00
###### `addToAutofilledValues({name: value})`
2016-04-07 14:20:44 +09:00
2016-06-13 09:44:09 +09:00
A function that takes a property, and adds it to the `autofilledValues` object.
2016-04-07 14:20:44 +09:00
2016-04-07 15:53:18 +09:00
###### `throwError({content, type})`
2016-04-04 16:58:53 +09:00
2016-04-07 14:20:44 +09:00
A callback function that can be used to throw an error.
2016-07-04 10:32:00 +09:00
###### `getDocument()`
A function that lets you retrieve the current document from a form component.
2016-05-26 15:52:35 +09:00
### Handling Values
The component handles three different layers of input values:
- The value stored in the database (when editing a document).
- The value being currently inputted in the form element.
- An “autofilled” value, typically provided by an *other* form element (i.e. autofilling the post title from its URL).
The highest-priority value is the user input. If there is no user input, we default to the database value provided by the `props` . And if that one is empty too, we then look for autofilled values.
2016-06-13 09:44:09 +09:00
### i18n
This package uses [React Intl ](https://github.com/yahoo/react-intl/ ) to automatically translate all labels. In order to do so it expects an `intl` object ot be passed as part of its context. For example, in a parent component:
```
getChildContext() {
const intlProvider = new IntlProvider({locale: myLocale}, myMessages);
const {intl} = intlProvider.getChildContext();
return {
intl: intl
};
}
```