refactor + simplify nova:forms logic

This commit is contained in:
Sacha Greif 2016-04-07 15:24:38 +09:00
parent 0c76519954
commit 7f8a38a2a1
4 changed files with 59 additions and 107 deletions

View file

@ -68,10 +68,6 @@ A number corresponding to the position of the property's field inside the form.
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.
#### `prefilledValues`
An object containing optional prefilled properties.

View file

@ -13,75 +13,48 @@ const Textarea = FRC.Textarea;
class FormComponent extends Component {
constructor() {
super();
}
renderComponent() {
({fieldName, field, labelFunction, document} = this.props);
// if control is a React component, use it
if (typeof this.props.control === "function") {
let options = [];
if (field.autoform && field.autoform.options) {
options = typeof field.autoform.options === "function" ? field.autoform.options() : field.autoform.options;
}
return <this.props.control {...this.props} />
const value = document && Utils.deepValue(document, fieldName) ? Utils.deepValue(document, fieldName) : "";
const label = typeof labelFunction === "function" ? labelFunction(fieldName) : fieldName;
} else { // else pick a predefined component
const inputProps = {
key: fieldName,
name: fieldName,
value: value,
label: label
};
if (typeof field.control === "function") {
return <field.control {...inputProps} />
} else {
switch (field.control) {
switch (this.props.control) {
case "text":
return <Input {...inputProps} type="text" />;
return <Input {...this.props} type="text" />;
case "textarea":
return <Textarea {...inputProps} />;
return <Textarea {...this.props} />;
case "checkbox":
return <Checkbox {...inputProps}/>;
return <Checkbox {...this.props} />;
// note: checkboxgroup cause React refs error
case "checkboxgroup":
return <CheckboxGroup {...inputProps} options={options} />;
return <CheckboxGroup {...this.props} />;
case "radiogroup":
return <RadioGroup {...inputProps} options={options} />;
return <RadioGroup {...this.props} />;
case "select":
return <Select {...inputProps} options={options} />;
return <Select {...this.props} />;
default:
return <Input {...inputProps} type="text" />;
return <Input {...this.props} type="text" />;
}
}
}
render() {
if (this.props.field.control === "none") {
return null;
} else {
return (
<div className={"input-"+fieldName}>
{this.renderComponent()}
</div>
)
}
return <div className={"input-"+this.props.name}>{this.renderComponent()}</div>
}
}
FormComponent.propTypes = {
fieldName: React.PropTypes.string,
field: React.PropTypes.object,
labelFunction: React.PropTypes.func,
document: React.PropTypes.object
name: React.PropTypes.string,
label: React.PropTypes.string,
value: React.PropTypes.any,
options: React.PropTypes.any,
control: React.PropTypes.any
}
export default FormComponent;

View file

@ -11,14 +11,12 @@ class NovaForm extends Component{
super(props);
this.submitForm = this.submitForm.bind(this);
this.methodCallback = this.methodCallback.bind(this);
this.updateState = this.updateState.bind(this);
this.addToPrefilledValues = this.addToPrefilledValues.bind(this);
this.throwError = this.throwError.bind(this);
this.clearErrors = this.clearErrors.bind(this);
this.state = {
disabled: false,
errors: [],
currentValues: this.props.document
errors: []
};
}
@ -28,7 +26,7 @@ class NovaForm extends Component{
}
// get relevant fields
getFields() {
getFieldNames() {
const collection = this.props.collection;
const fields = this.getFormType() === "edit" ? collection.getEditableFields(this.props.currentUser) : collection.getInsertableFields(this.props.currentUser);
return fields;
@ -54,31 +52,17 @@ class NovaForm extends Component{
return <div className="form-errors">{this.state.errors.map(message => <Flash key={message} message={message}/>)}</div>
}
// whenever the form values change, keep track of them in the state
updateState(e) {
// e can sometimes be event, sometims be currentValue
// see https://github.com/christianalfoni/formsy-react/issues/203
if (e.stopPropagation) {
e.stopPropagation();
} else {
this.setState({
currentValues: e
});
}
}
// add something to the state
// add something to prefilled values
addToPrefilledValues(property) {
this.setState({
prefilledValues: {...this.state.prefilledValues, ...property}
});
}
// pass on form values as context to all child components for easy access
// pass on context to all child components
getChildContext() {
return {
throwError: this.throwError,
currentValues: this.state.currentValues,
prefilledValues: this.state.prefilledValues,
addToPrefilledValues: this.addToPrefilledValues
};
@ -122,7 +106,7 @@ class NovaForm extends Component{
submitForm(data) {
this.setState({disabled: true});
const fields = this.getFields();
const fields = this.getFieldNames();
const collection = this.props.collection;
// if there's a submit callback, run it
@ -163,28 +147,41 @@ class NovaForm extends Component{
}
render() {
// build fields array by iterating over the list of field names
let fields = this.getFieldNames().map(fieldName => {
// get schema for the current field
const fieldSchema = this.props.collection.simpleSchema()._schema[fieldName]
const document = this.props.document;
const collection = this.props.collection;
const fields = this.getFields();
// add name, label, and type properties
let field = {
name: fieldName,
label: (typeof fieldSchema.labelFunction === "function") ? fieldSchema.labelFunction(fieldName) : fieldName,
type: fieldSchema.type,
control: fieldSchema.control
}
const style = {
maxWidth: "800px",
width: "100%"
}
// add value
field.value = this.props.document && Utils.deepValue(this.props.document, fieldName) ? Utils.deepValue(this.props.document, fieldName) : "";
// add options if they exist
if (fieldSchema.autoform && fieldSchema.autoform.options) {
field.options = typeof fieldSchema.autoform.options === "function" ? fieldSchema.autoform.options() : fieldSchema.autoform.options;
}
return field;
});
// remove fields where control = "none"
fields = _.reject(fields, field => field.control === "none");
return (
<div className={"document-"+this.getFormType()} style={style}>
<Formsy.Form onSubmit={this.submitForm} onChange={this.updateState} disabled={this.state.disabled} ref="form">
<div className={"document-"+this.getFormType()}>
<Formsy.Form onSubmit={this.submitForm} disabled={this.state.disabled} ref="form">
{this.renderErrors()}
{fields.map(fieldName => <FormComponent
key={fieldName}
className={"input-"+fieldName}
fieldName={fieldName}
field={collection.simpleSchema()._schema[fieldName]}
labelFunction={this.props.labelFunction}
document={document}
/>)}
{fields.map(field => <FormComponent key={field.name} {...field} />)}
<Button type="submit" bsStyle="primary">Submit</Button>
</Formsy.Form>
</div>
@ -210,10 +207,9 @@ NovaForm.contextTypes = {
}
NovaForm.childContextTypes = {
currentValues: React.PropTypes.object,
prefilledValues: React.PropTypes.object,
throwError: React.PropTypes.func,
addToPrefilledValues: React.PropTypes.func
addToPrefilledValues: React.PropTypes.func,
throwError: React.PropTypes.func
}
module.exports = NovaForm;

View file

@ -31,11 +31,7 @@ Posts.schema = new SimpleSchema({
insertableIf: Users.is.admin,
editableIf: Users.is.admin,
publish: true,
control: "datepicker",
autoform: {
group: 'admin',
type: "bootstrap-datetimepicker"
}
control: "datepicker"
},
/**
URL
@ -61,9 +57,7 @@ Posts.schema = new SimpleSchema({
editableIf: Users.is.ownerOrAdmin,
control: PrefilledTitle,
publish: true,
autoform: {
order: 20
}
order: 20
},
/**
Slug
@ -84,10 +78,7 @@ Posts.schema = new SimpleSchema({
editableIf: Users.is.ownerOrAdmin,
control: PrefilledBody,
publish: true,
autoform: {
rows: 5,
order: 30
}
order: 30
},
/**
HTML version of the post body
@ -164,11 +155,7 @@ Posts.schema = new SimpleSchema({
insertableIf: Users.is.admin,
editableIf: Users.is.admin,
control: "checkbox",
publish: true,
autoform: {
group: 'admin',
leftLabel: "Sticky"
}
publish: true
},
/**
Whether the post is inactive. Inactive posts see their score recalculated less often