Form
Form component gets as props form model object and resources object that will list all its settings (such as validations, disable term, field dependencies and more) and lifecycle hooks (such as before and after actions, submit, form level validations and dto conversions). In addition, Form component exposes a context object that includes current form model, resources and actions (such as changeValue
) to perform on the form. The context can be used within any child component of the Form component (not necessary direct child component).
Props
Name | Type | Description |
---|---|---|
model | required object | Model object that defines fields, validators and more as described in Form Class |
resources | object | Resources object that defines all the handlers that the form model defined as described in Form Class |
settings | object | Settings overrides object as described in Form Class |
data | object | Pass data as a prop outside the model object - only when data might change outside the Form component during the app life. On init - Form will applies data to the model and pass the final model to the underline Form class instance, and when data prop reference changes only changeData action called on the underline form instance. |
context | object | Pass context as a prop outside the model object - only when context might change outside the Form component during the app life. On init - Form will applies context to the model and pass the final model to the underline Form class instance, and when context prop reference changes only changeContext action called on the underline form instance. |
Example
import { Form, Field } from '@cofi/react-form';
import SomeInputText from './my-components/Text.jsx';
const model = {
id: 'user-form',
fields: {
firstName: {
path: 'firstName',
label: 'First Name',
description: 'Enter user first name',
component: {
name: 'InputText',
},
},
lastName: {
path: 'lastName',
label: 'Last Name',
component: {
name: 'InputText',
},
},
data: {
firstName: 'Roos',
lastName: 'Geller',
}
},
};
const resources = {
components: {
InputText: { renderer: SomeInputText },
}
};
<Form model={model} resources={resources}>
<h2>User Details</h2>
<Field id="firstName" />
<Field id="lastName" />
</Form>
Context
Form provides a context object used by direct and non-direct children components.
Note: In
@cofi/react-form
-context
refer to the data provided by theForm
component object, which can be consumed by each underline component (i.e react's context feature). Whereas in@cofi/form
context refers to the app data which is stored in the model to serve the form's lifecycle handlers.
Context object
Context object contains:
Name | Type | Description |
---|---|---|
model | object | The current model object. Can be used to pull data for an underline component. For instance, a save button component, should use form data to disable the button in some cases |
resources | object | The current resources object |
actions | object | actions object contains a set of function to be called in order to manipulate the from. Full actions docs can be found here |
parent | object | parent form context object |
For Example:
const reset = () => {
context.actions.reset();
};
<Button disabled={context.model.invalid || context.model.processing ||
!context.model.dirty} onClick={reset}>Reset</Button>
Context Consume
In order to use the Form context in child components, components first need to consume Form context.
Example using class component:
import React from 'react';
import { FormContext } from '@cofi/react-form';
class ErrorsSummary extends React.Component {
static contextType = FormContext;
render() {
return (<div>
<h4>Errors summary:</h4>
{
Object.values(this.context.model.fields).map((field, i) => (<ul key={i}>
{ field.errors.map((error, index) => <li key={index}>Error in {field.label}: {error.message}</li>) }
</ul>))
}
</div>);
}
}
Example using functional component:
import React, { useContext } from 'react';
import { FormContext } from '@cofi/react-form';
const ErrorsSummary = () => {
const context = useContext(FormContext);
return (<div>
<h4>Errors summary:</h4>
{
Object.values(context.model.fields).map((field, i) => (<ul key={i}>
{ field.errors.map((error, index) => <li key={index}>Error in {field.label}: {error.message}</li>) }
</ul>))
}
</div>);
};
Context Usage
Components declared inside a Form tag component can define contextType of form, and then
use this.context
to gain access to the form context.
Examples
- Cofi Field component - consumes the form context and interacts with it. It uses the
this.context.model
to get field's data,this.context.actions.changeValue
andthis.context.actions.changeState
actions to notify the form about a change in the component. - Your custom component. You can define another custom components to interact with the form like
SaveButton
Example
import React from 'react';
import Button from '@mui/material/Button';
import { FormContext } from '@cofi/react-form';
export default class SaveButton extends React.Component {
static contextType = FormContext;
render() {
return (<Button disabled={this.context.model.invalid} onClick={this.onSave}></Button>);
}
onSave = () => {
// save data to the server
serverCallToSaveData(this.context.model.data).then(() => {
// reset the form to its initial state
this.context.actions.reset();
});
}
}
- Pass to custom components any additional data (for example app data) through resources or model objects that are passed via context.
Example
const model = {
// ...
appData: {
user: { /* ... */ },
currency: 'USD',
},
}
export default class Currency extends React.Component {
static contextType = FormContext;
render() {
return (<Input
type="number"
value={this.props.value}
onChange={this.onValueChange}
endAdornment={<Label value={this.context.model.appData.currency}/>}/>);
}
onValueChange = (e) => {
this.props.onValueChange(e.target.value);
}
}
Utils
createForm
Use createForm
function, instead of defining multiple custom inner components using FormContext (like the above SaveButton).
The function gets a form definition (model & resources) and returns an HOC which gets a single component.
The component should define its context as the FormContext, and then the form's context will be available in the component.
Example
import React from 'react';
import { Field, FormContext, createForm } from '@cofi/react-form';
import Button from '@mui/material/Button';
import form from '../form'; // object of { model, resources }
class MyComponent extends React.Component {
static contextType = FormContext;
render() {
return (<div>
<h2>User Details</h2>
<Field id="firstName" />
<Field id="lastName" />
<Button disabled={this.context.model.invalid} onClick={this.onSave}></Button>
</div>);
}
onSave = () => {
// save data to the server
serverCallToSaveData(this.context.model.data).then(() => {
// reset the form to its initial state
this.context.actions.reset();
});
}
}
return createForm(form)(MyComponent);
withForm
Use withForm
HOC, to auto inject form context as a form
prop to a component.
Example
import React, { useCallback } from 'react';
import { Field, withForm, createForm } from '@cofi/react-form';
import Button from '@mui/material/Button';
import service from '../service';
import form from '../form'; // object of { model, resources }
const MyComponent = withForm(({ form }) => {
const onSave = useCallback(async () => {
// save data to the server
await service.serverCallToSaveData(form.model.data);
// reset the form to its initial state
await form.actions.reset();
}, [form]);
return (<div>
<h2>User Details</h2>
<Field id="firstName" />
<Field id="lastName" />
<Button disabled={form.model.invalid} onClick={onSave}></Button>
</div>);
});
return createForm(form)(MyComponent);