FormStore is part of a collection of loosely-coupled components for managing, rendering and validating forms in MobX-based apps.
https://alexhisen.gitbooks.io/mobx-forms/
Instances of FormStore (models) store the data entered by the user into form fields and provide observables for managing validation error messages. They also track changes and provide facilities for (auto-)saving the (changed) data.
- Tracks changes in each data property and by default saves only the changes.
- Will not deem a property as changed for string/number, etc datatype changes or different Date objects with the same date or different Arrays (in v1.4+) or Objects (in v3.0+) with the same content.
- Optionally auto-saves incremental changes (if there are any) every X milliseconds (see autoSaveInterval).
- By default, will not (auto-)save data properties that failed validation or that are still being edited by the user (note that FormStore only provides the facilities to track this - validation, etc is done in different components).
- In a long-running app, can prevent unnecessary server requests to refresh data in the model by limiting them to not occur more often than Y milliseconds (see minRefreshInterval).
- Will auto-save any unsaved data when attempting to refresh to prevent loss of user-entered data.
- Provides observable properties to drive things like loading indicators, enabling/disabling form or save button, error and confirmation messages, etc.
- Server responses to save requests can drive error / validation messaging and discard invalid values.
- Can differentiate between 'create' and 'update' save operations. A model that has not yet been created on the server will not try to refresh from server (this works right in v2.0+).
- Saves are queued automatically, never concurrent.
- (NEW in v1.3) Auto-save can be dynamically configured and enabled or disabled
Previously, server.create() was called (instead of server.set()) only when the property defined as the idProperty in store.data was falsy. This worked well if the idProperty was only returned by the server and was not user-enterable. Now whether server.create() is called is driven by a new store.status.mustCreate property which is true only when the idProperty has not yet been returned by the server / saved even if it already has a value in store.data. Note that MobxSchemaForm v.1.14+ supports a readOnlyCondition property that can be set to "!model.status.mustCreate" to allow an id property to be entered but not modified.
- FormStore now deep-clones (and merges) objects and arrays (plain or observable) when storing data coming from server and in the updates object sent to server.
- It's no longer published as a webpack-compiled and minified module.
FormStore only requires MobX 2.2+, 3.x, 4.x or 5.x. MobX strict mode is currently not supported. FormStore does not implement the actual server requests, it only calls methods that you provide with the data to be sent to the server.
npm install --save mobx-form-store
myStore.js (a Singleton):
import FormStore from 'mobx-form-store';
const model = new FormStore({
server: {
// Example uses ES5 with https://github.com/github/fetch API and Promises
get: function() {
return fetch('myServerRefreshUrl').then(function(result) { return result.json() });
},
// Example uses ES6, fetch and async await
set: async (info) => {
const result = await fetch('myServerSaveUrl', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(info),
});
return await result.json() || {}; // MUST return an object
}
},
});
export default model;
IMPORTANT: Your server.get method MUST return an object with ALL properties that need to be rendered in the form. If the model does not yet exist on the server, each property should have a null value but it must exist in the object or it cannot be observed with MobX.
myForm.jsx (this is without MobxSchemaForm (with it there is even less code)).
import React from 'react';
import { observer } from 'mobx-react';
import model from './myStore.js'
@observer class MyForm extends React.Component {
componentDidMount() {
model.refresh();
}
onChange = (e) => {
model.data[e.target.name] = e.target.value;
model.dataErrors[e.target.name] = myCustomValidationPassed ? null : "error message";
}
onSaveClick = () => {
if (!model.status.canSave || !model.status.hasChanges) return;
if (myCustomValidationPassed) model.save();
}
render() {
return (
{/* ... more fields / labels ... */}
<label className={model.dataErrors.myProperty ? 'error' : ''}>My Property</label>
<input
type="text"
name="myProperty"
className={model.dataErrors.myProperty ? 'error' : ''}
value={model.data.myProperty || ''}
readonly={model.status.isReadOnly}
onChange={this.onChange}
/>
<p>{model.dataErrors.myProperty}</p>
<button
className={(!model.status.canSave || !model.status.hasChanges) ? 'gray' : ''}
onClick={this.onSaveClick}
>
Save
</button>
);
}
}