Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
react-jsonschema-form-manager
Advanced tools
Extension of react-jsonschema-form with conditional field support
This project is opinionated implementation of a manager for mozilla form or any of it's derivative projects, it provides a REST based api layer that handles form submission and update.
REST
or LocalStorage
api for testingPUT
requestsREST
form configuration supportInstall react-jsonschema-form-manager
by running:
npm install --s react-jsonschema-form-manager
The simplest use case would be to load static properties and save them in localStorage
,
this can be done like this:
import React from "react";
import ReactDOM from "react-dom";
import Form from "react-jsonschema-form";
import withManager, { LocalStorageFormManager, StaticConfigResolver, intervalUpdateStrategy } from "react-jsonschema-form-manager";
let config = {
schema: {
type: "object",
required: ["firstName", "lastName"],
properties: {
firstName: {
type: "string",
title: "First name",
},
lastName: {
type: "string",
title: "Last name",
}
}
}
};
let configResolver = new StaticConfigResolver(config);
let manager = new LocalStorageFormManager();
let updateStrategy = intervalUpdateStrategy(10000);
let FormToDisplay = withManager(manager, configResolver, updateStrategy)(Form);
ReactDOM.render(<FormToDisplay />, document.getElementById("app"));
Functional part of API consist of 3 parts
static
or REST
)localStorage
or REST
)imediate
or interval
)You can configure and extend them independently of one another, to get functionality you need.
UI workflow consists of 2 phases
LoadingScreen
- display loading, while configuration is resolvedform
with loaded configuration, or display an ErrorScreen
screen in case configuration can't be resolvedYou can override default presentations, when you call withManager
import React, { Component } from "react";
import Form from "react-jsonschema-form";
import withManager from "react-jsonschema-form-manager";
class CustomLoadingScreen extends Component {
render() {
return (
<div className="container">
<h1>About to launch</h1>
</div>
);
}
}
class CustomErrorScreen extends Component {
render() {
return (
<div className="container">
<h4>Houston, we have a problem</h4>
<h2>
{this.props.error.message}
</h2>
</div>
);
}
}
...
export default withManager(manager, configResolver)(Form, CustomLoadingScreen, CustomErrorScreen);
Each mozilla form needs a configuration, which can be either pre configured or loaded from the server.
LoadingScreen
will be displayed while configuration loads. ConfigResolver
does not make any assumptions regarding the content
of the configuration, so except for schema
and uiSchema
you can return any configuration needed.
The simplest configuration for schema configuration load, would look like this:
import React, { Component } from "react";
import Form from "react-jsonschema-form";
import withManager, { StaticConfigResolver, LocalStorageFormManager } from "react-jsonschema-form-manager";
let config = {
schema: {
//...
},
uiSchema: {
//...
},
formData: {
//...
},
};
let configResolver = new StaticConfigResolver(config);
let localStorageManager = new LocalStorageFormManager();
let FormToDisplay = withManager(configResolver, localStorageManager)(Form);
class ResultForm extends Component {
render() {
return (
<FormToDisplay />
);
}
}
Here the config
will be used as Form configuration and StaticConfigResolver
is used to return it.
This is enough for forms to operate properly
There are 2 supported configuration resolvers:
config
settingconfiguration
You can also specify your own configuration resolver.
StaticConfigResolver
takes 2 parameters config
and timeout
,
config
is the configuration to returntimeout
delay before returning configuration, it was added primarily for testing purposesimport { StaticConfigResolver } from "react-jsonschema-form-manager";
let timeout = 10000;
let config = {
schema: {
//...
},
uiSchema: {
//...
},
formData: {
//...
},
};
let configResolver = new StaticConfigResolver(config, timeout);
Primary configuration option is by means of REST
API, which you can configure with needed authentication
RESTConfigResolver
takes 3 parameters
url
configuration url to usecredentials
authentication to use with urloutputHandler
manipulate data returned by the HTTP call before resolving the promiseThe simplest RESTConfigResolver
will look like this:
import { RESTConfigResolver } from "react-jsonschema-form-manager";
let configResolver = new RESTConfigResolver(`https://example.com/schema`);
In this case no authentication is needed and config resolver returnes json result of REST
request
There are 3 options to specify credentials
for the configuration endpoint
In this case no authentication will be provided for the request
import { RESTConfigResolver } from "react-jsonschema-form-manager";
let configResolver = new RESTConfigResolver(`https://example.com/schema`);
In case authentication can be done with domain Cookies, you can simply specify credentials object in accordance with fetch documentation. Refer to whatwg-fetch sending cookies documentation for more details.
import { RESTConfigResolver } from "react-jsonschema-form-manager";
let configResolver = new RESTConfigResolver(
`https://example.com/schema`,
{
credentials: 'same-origin'
});
or
import { RESTConfigResolver } from "react-jsonschema-form-manager";
let configResolver = new RESTConfigResolver(
`https://example.com/schema`,
{
credentials: 'include'
});
Transformation function, would sign fetch Request
with any custom authentication logic needed.
For example to have a Basic authentication
can be done like this:
import { RESTConfigResolver } from "react-jsonschema-form-manager";
let credentials = (req) => new Request(req, { headers: {
'Authorization': 'Basic '+ btoa(`${username}:${password}`),
}});
let configResolver = new RESTConfigResolver(
`https://example.com/schema`,
credentials);
Use this if your HTTP resource doesn't return the exact JSON data you want to pass to your Form's props. Also useful for adding/merging default values to the HTTP response.
import { RESTConfigResolver } from "react-jsonschema-form-manager";
let outputHandler = obj => {
let output = {
subSetOfData: obj.someProperty,
};
return output;
};
let configResolver = new RESTConfigResolver(
"http://localhost:3000/conf",
{},
outputHandler
);
// if origOutput = JSON response of REST call,
//configResolver resolves to: { subSetOfData: origOutput.someProperty };
If neither REST
or Static
configuration fits your needs, you can create your own configuration, by simply providing
resolve
method with no params, that would return Promise
with config
result
For example GraphQL ConfigResolver can be defined something like this:
import { RESTConfigResolver } from "react-jsonschema-form-manager";
class GraphQLConfigResolver {
constructor(url, credentials) {
this.restResolver = new RESTConfigResolver(url, credentials);
}
resolve = () => {
return this.restResolver.resolve().then(({ data, error }) => {
return new Promise((resolve, reject) => {
if (error) {
reject(new Error(error));
} else {
resolve(data);
}
});
});
};
}
Besides schema configuration, you need to specify the way to save formData. System supports 2 ways of saving data
LocalStorage
- using LocalStorage for storing submitted data, this is primarily for testing purposeREST
- saving data in REST endpointThe simplest configuration can look like this
import React, { Component } from "react";
import Form from "react-jsonschema-form";
import withManager, { StaticConfigResolver, LocalStorageFormManager } from "react-jsonschema-form-manager";
let config = {
//...
};
let configResolver = new StaticConfigResolver(config);
let localStorageManager = new LocalStorageFormManager();
let FormToDisplay = withManager(configResolver, localStorageManager)(Form);
class ResultForm extends Component {
render() {
return (
<FormToDisplay onSubmit={() => onSuccessCallback}/>
);
}
}
In this case data is stored in LocalStorage
and on successful submit, if there are no errors onSuccessCallback
is called for further processing.
Implementation wraps onSubmit
on original form and calls it only after formData was successfully saved with underlying FormManager
.
LocalStorage
store is there only for testing purposes and not supposed to be used in production.
You can specify a key
under which provided form will be stored. By default form
is used as a key and you are limited to single form.
As with schema configuration, this is primary option for storing your data.
RESTFormManager
takes 2 parameters
url
configuration url to usecredentials
authentication to use with urlAuthentication logic is the same as for RESTConfigurationResolver
, the only difference is that instead of the GET
, we send a POST
with JSON
of formData
as a request.
As with ConfigurationResolver
you can create your custom implementation for FormManager
, which needs only 3 methods
submit
called when form is submitted, should return Promise
that will be resolved after successful submissionupdateIfChanged
called when form needs to update it's transient stay to the server. It must return either a Promise
, if manager
considers data changed and will trigger an update, or undefined
if there is nothing to change.
updateIfChanged
accepts force
flag that will always result in update.onChange
called whenever form data changes, it's needed to manage formData state inside a managerFor example FormManager
using SessionStorage
can look something like this:
class SessionStorageFormManager {
constructor(key = DEFAULT_KEY) {
this.key = key;
}
onChange = (state) => {
this.formData = state.formData;
}
submit = () => {
return new Promise(resolve => {
sessionStorage.setItem(this.key, JSON.stringify(this.formData));
resolve(formData);
});
}
updateIfChanged = () => {
return new Promise(resolve => {
sessionStorage.setItem(this.key, JSON.stringify(this.formData));
resolve(formData);
});
}
}
You can skip logic in update
, if you are not planning to use any UpdateStrategy
in your case.
UpdateStrategy
is needed in case you want to save transient results of work.
For example
import React, { Component } from "react";
import Form from "react-jsonschema-form";
import { instantUpdateStrategy } from "react-jsonschema-form-manager";
...
let FormToDisplay = withManager(configResolver, manager, instantUpdateStrategy)(Form);
class ResultForm extends Component {
render() {
return (
<FormToDisplay/>
);
}
}
In this case all changes to the formData
, will be instantly submitted by the FormManager
to the underlying server.
As with FormManager, UpdateStrategy override embedded onChange
mozilla-jsonschema functionality.
There are 2 kinds of UpdateStrategy
currently supported
instantUpdateStrategy
updates request on every change of the form. This is a lot of work for the server and not recommended.
import React, { Component } from "react";
import Form from "react-jsonschema-form";
import { instantUpdateStrategy } from "react-jsonschema-form-manager";
let FormToDisplay = withManager(configResolver, manager, instantUpdateStrategy)(Form);
class ResultForm extends Component {
render() {
return (
<FormToDisplay onChange={() => onSuccessChange}/>
);
}
}
intervalUpdateStrategy
updates request in specified interval of time, it takes timeout
as parameter.
For example, in order to update server every 100 seconds configuration would look something like this:
import React, { Component } from "react";
import Form from "react-jsonschema-form";
import { intervalUpdateStrategy } from "react-jsonschema-form-manager";
let updateStrategy = intervalUpdateStrategy(100000);
let FormToDisplay = withManager(configResolver, manager, updateStrategy)(Form);
class ResultForm extends Component {
render() {
return (
<FormToDisplay/>
);
}
}
As with other components you can easily override UpdateStrategy
with custom implementation.
Construction of the object is done with a currying pattern, you can define any parameters you want on first call,
and you will get manager
when updateStrategy is going to be used. Returned object needs 2 methods onChange
and stop
.
stop
get called when component unMounts, or after successful submitonChange
called when ever formData
changes, in original Form
For example you want to update only on even dates, this would look something like this:
export function evenDaysUpdateStrategy() {
return (manager) => {
return {
onChange: () => {
if (new Date().getDate() % 2 == 0) {
manager.updateIfChanged();
}
},
stop: function() {};
};
};
}
If you want to track server data updates, you can do this by specifying onUpdate
callback on rendered form
...
let FormToDisplay = withManager(configResolver, manager, updateStrategy)(Form);
class ResultForm extends Component {
render() {
return (
<FormToDisplay onUpdate={() => console.log("Data updated")}/>
);
}
}
If you are having issues, please let us know here or on StackOverflow.
The project is licensed under the Apache Licence 2.0.
FAQs
Extension of react-jsonschema-form with conditional field support
The npm package react-jsonschema-form-manager receives a total of 1 weekly downloads. As such, react-jsonschema-form-manager popularity was classified as not popular.
We found that react-jsonschema-form-manager demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.