clean up forms package and update readme

This commit is contained in:
Sacha Greif 2016-04-04 16:58:53 +09:00
parent a367ed426d
commit 641bc46e13
3 changed files with 42 additions and 246 deletions

View file

@ -8,38 +8,70 @@ This package provides a `NovaForm` component that works with the schema extensio
### Props
#### collection `object.isRequired`
#### `collection`
The collection in which to edit or insert a document.
#### document `object`
#### `document`
If present, the document to edit. If not present, the form will be a “new document” form.
#### currentUser `object`
#### `currentUser`
The current user.
#### submitCallback() `func`
#### `submitCallback()`
A callback called on form submission.
#### successCallback(document) `func`
#### `successCallback(document)`
A callback called on method success.
#### errorCallback(document, error) `func`
#### `errorCallback(document, error)`
A callback called on method failure.
#### methodName `string`
#### `methodName`
The name of the Meteor method to call.
#### labelFunction `func`
#### `labelFunction`
A function to call on field names to get the label.
#### prefilledProps `object`
#### `prefilledProps`
A set of props to prefill for new documents.
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:
#### `insertableIf(user)`
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.
#### `editableIf(user, document)`
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.
#### `control`
Either a text string (one of `text`, `textarea`, `checkbox`, `checkboxgroup`, `radiogroup`, or `select`) or a React component.
#### `position`
A number corresponding to the position of the property's field inside the form.
### Context
The main `NovaForm` components makes the following objects available as context to all its children:
#### `currentValues`
An object containing all the current values of the form.
#### `throwError({content, type})`
A callback function that can be used to throw an error.

View file

@ -1,122 +0,0 @@
import React, { PropTypes, Component } from 'react';
import Formsy from 'formsy-react';
import { Button } from 'react-bootstrap';
import FormComponent from "./FormComponent.jsx";
import Utils from './utils.js';
class EditDocument extends Component{
constructor() {
super();
this.submitForm = this.submitForm.bind(this);
this.state = {
disabled: false
};
}
getFields() {
const collection = this.props.collection;
const fields = collection.getEditableFields(this.props.currentUser);
return fields;
}
submitForm(data) {
this.setState({disabled: true});
// if there's a submit callback, run it
if (this.props.submitCallback) {
this.props.submitCallback();
}
const fields = this.getFields();
const document = this.props.document;
// put all keys with data on $set
const set = _.compactObject(Utils.flatten(data));
// put all keys without data on $unset
const unsetKeys = _.difference(fields, _.keys(set));
const unset = _.object(unsetKeys, unsetKeys.map(()=>true));
const modifier = {$set: set, $unset: unset};
const collection = this.props.collection;
Meteor.call(this.props.methodName, document._id, modifier, (error, document) => {
this.setState({disabled: false});
if (error) {
console.log(error)
this.setState({
errors: [{
content: error.message,
type: "error"
}]
});
if (this.props.errorCallback) {
this.props.errorCallback(document, error);
}
} else {
this.setState({errors: []});
if (this.props.successCallback) {
this.props.successCallback(document);
}
if (this.context.closeCallback) {
this.context.closeCallback();
}
}
});
}
renderErrors() {
Flash = Telescope.components.Flash;
return <div className="form-errors">{this.state.errors.map(message => <Flash message={message}/>)}</div>
}
render() {
const document = this.props.document;
const collection = this.props.collection;
const fields = this.getFields();
const style = {
maxWidth: "800px",
width: "100%"
}
return (
<div className="edit-document" style={style}>
<Formsy.Form onSubmit={this.submitForm} disabled={this.state.disabled}>
{this.renderErrors()}
{fields.map(fieldName => <FormComponent
key={fieldName}
className={"input-"+fieldName}
fieldName={fieldName}
field={collection.simpleSchema()._schema[fieldName]}
labelFunction={this.props.labelFunction}
document={document}
/>)}
<Button type="submit" bsStyle="primary">Submit</Button>
</Formsy.Form>
</div>
)
}
}
EditDocument.propTypes = {
document: React.PropTypes.object.isRequired,
collection: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object,
successCallback: React.PropTypes.func,
errorCallback: React.PropTypes.func,
methodName: React.PropTypes.string,
labelFunction: React.PropTypes.func
}
EditDocument.contextTypes = {
closeCallback: React.PropTypes.func
}
module.exports = EditDocument;
export default EditDocument;

View file

@ -1,114 +0,0 @@
import React, { PropTypes, Component } from 'react';
import Formsy from 'formsy-react';
import { Button } from 'react-bootstrap';
import FormComponent from "./FormComponent.jsx";
import Utils from './utils.js';
class NewDocument extends Component {
constructor() {
super();
this.submitForm = this.submitForm.bind(this);
this.state = {
disabled: false,
errors: []
};
}
submitForm(data) {
this.setState({disabled: true});
// if there's a submit callback, run it
if (this.props.submitCallback) {
this.props.submitCallback();
}
// remove any empty properties
let document = _.compactObject(Utils.flatten(data));
const collection = this.props.collection;
// add prefilled properties
if (this.props.prefilledProps) {
document = Object.assign(document, this.props.prefilledProps);
}
Meteor.call(this.props.methodName, document, (error, document) => {
this.setState({disabled: false});
if (error) {
console.log(error)
this.setState({
errors: [{
content: error.message,
type: "error"
}]
});
if (this.props.errorCallback) {
this.props.errorCallback(document, error);
}
} else {
this.setState({errors: []});
this.refs.newDocumentForm.reset();
if (this.props.successCallback) {
this.props.successCallback(document);
}
if (this.context.closeCallback) {
this.context.closeCallback();
}
}
});
}
renderErrors() {
Flash = Telescope.components.Flash;
return <div className="form-errors">{this.state.errors.map(message => <Flash message={message}/>)}</div>
}
render() {
const collection = this.props.collection;
const fields = collection.getInsertableFields(this.props.currentUser);
const style = {
maxWidth: "800px",
width: "100%"
}
return (
<div className="new-document" style={style}>
<Formsy.Form onSubmit={this.submitForm} disabled={this.state.disabled} ref="newDocumentForm">
{this.renderErrors()}
{fields.map(fieldName => <FormComponent
key={fieldName}
className={"input-"+fieldName}
fieldName={fieldName}
field={collection.simpleSchema()._schema[fieldName]}
labelFunction={this.props.labelFunction}
/>)}
<Button type="submit" bsStyle="primary">Submit</Button>
</Formsy.Form>
</div>
)
}
}
NewDocument.propTypes = {
collection: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object,
errorCallback: React.PropTypes.func,
successCallback: React.PropTypes.func,
methodName: React.PropTypes.string,
labelFunction: React.PropTypes.func,
prefilledProps: React.PropTypes.object
}
NewDocument.contextTypes = {
closeCallback: React.PropTypes.func
}
module.exports = NewDocument;
export default NewDocument;