Angular JSON Schema Form Library
To use the library in your project, follow these steps:
npm i @dashjoin/json-schema-form
npm i @angular/material
npm i jsonata
In your app module add:
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { JsonSchemaFormModule } from '@dashjoin/json-schema-form';
...
@NgModule({
...
imports: [
BrowserModule,
BrowserAnimationsModule,
JsonSchemaFormModule,
...
],
...
}
Note: You need import CommonModule for nested lazy loading modules
import { CommonModule } from '@angular/common';
import { JsonSchemaFormModule } from '@dashjoin/json-schema-form';
@NgModule({
...
imports: [
CommonModule,
JsonSchemaFormModule,
...
],
...
}
A small sample component:
import { Component } from '@angular/core';
import { State } from '@dashjoin/json-schema-form';
import { FormArray } from '@angular/forms';
@Component({
selector: 'app-root',
template: `
<lib-json-schema-form [state]="state"></lib-json-schema-form>
`
})
export class AppComponent {
state: State = {
schema: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
bday: { type: 'string', widget: 'date' }
}
}
},
value: any = [{
name: 'Joe',
bday: '2018-09-09T22:00:00.000Z'
}];
name: 'myform',
// pick FormArray, FormGroup or FormControl for arrays, objects, or single values respectively
control: new FormArray([])
};
foo() {
// subscribe to form value change / validation or state events
this.state.control.valueChanges.subscribe(res => {
console.log(res);
})
}
}
Finally, add the material style and icons to styles.css:
@import "~@angular/material/prebuilt-themes/indigo-pink.css";
@import "https://fonts.googleapis.com/icon?family=Material+Icons";
We define a couple of extensions to JSON Schema in order to define the user interface and layout of the form. Please also see the demo playground where examples of all configuration options are available.
This option specifies a specific input widget to be used. The default is a simple text field. The following options are available:
{
"type": "string",
"widget": "date"
}
It is possible to create custom widgets using the following steps:
The following fields control how select and autocomplete options are obtained from a REST backend:
{
"type": "string",
"choicesUrl": "/assets/autocomplete-simple.json",
"choicesVerb": "GET"
}
If you want the option's control value (what is saved in the form) to be different than the option's display value (what is displayed in the text field), the "displayWith" option allows you to do so. The value of "displayWith" is the name under which the implementation class to perform this job was registered. The class must implement the ChoiceHandler interface. An example can be found at the end of the playground component. The registration can be done in ngOnInit() using this service: this.service.registerDisplayWith('states', new MyDisplayer()); Consider the following example:
{
"type": "string",
"displayWith": "localName",
"choices": [
"https://en.wikipedia.org/wiki/Indonesia",
"https://en.wikipedia.org/wiki/Peru",
"As is - no tooltip"
]
}
The autocomplete is configured with "localName" which is a built-in displayer. It treats options like URLs and displays the local name which is the text after the last slash, hash, colon or dot. This causes the dropdown to display "Peru" with the tooltip indicating the real value "https://en.wikipedia.org/wiki/Peru" which is written to the JSON value.
The custom implementation also enables you to exercise tight control over filtering, typeahead loading of options, and determining the display value. For an example of a typeahead implementation, see the class MyTypeAhead at the bottom of the playground component.
Layout options determine how the input elements of arrays and objects are arranged. These options can be applied for each nesting layer (e.g. if you're entering an array of objects):
{
"type": "array",
"layout": "horizontal",
"items": {
"type": "object",
"layout": "vertical",
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "number"
}
}
}
}
The order field allows to control the inputs of objects:
The style and class fields allow passing CSS styles and classes to the input fields. For instance, you could emphasize the input with a higher z elevation and accommodate for longer input values by increasing the default input element width:
{
"type": "string",
"class": [
"mat-elevation-z2"
],
"style": {
"width": "400px"
}
}
Please also see the definition of the Schema object.
In some situations, you would like to compute a field based on the contents of other fields. This can be achieved via the "compute" option. It can be placed within an object as follows:
{
"type": "object",
"properties": { "first": {"type": "string"}, "last": { "type": "string" }, "salutation": { "type": "string", "readOnly": true } },
"computed": {
"salutation": '"Dear " & first & " " & last & "," & $context("var")'
}
}
In this example, any change to the first or last fields trigger a change in salutation which is displayed as a read only form field. The expression defining the salutation value is expressed in JSONata (https://jsonata.org/). The custom function $context allows the host application to reference data which was set via this.service.setContext(key, value).
Some JSON Schema constructs like "pattern" or "required" allow validating an object against the schema. The result of this validation is displayed on the UI but it is also propagated to the parent component via the "error" output variable. Error contains the first validation error message or null if the form is valid. The following example shows how this information can be used to deactivate form submission:
<lib-json-schema-form [(value)]="value" [schema]="schema" [label]="schema.title" (errorChange)="error=$event">
</lib-json-schema-form>
<button [disabled]="error !== null" (click)="submit()">Submit</button>
Note that not all JSON schema validation constructs are supported. Also, arrays and additional property objects do not propagate the information and the invalid value is undefined.
We support JSON Schema Draft 6 with these exceptions:
In order to foster reuse, schemas are often made available on the web. In this case, you can use JSON schema's $ref mechanism to have the browser load the schema as follows:
<lib-json-schema-form [schema]="{$ref:'https://raw.githubusercontent.com/riskine/ontology/master/schemas/core/profession.json'}">
The URL can also be relative to the form's URL:
<lib-json-schema-form [schema]="{$ref:'schema.json'}">
If you do not want the schema to be downloaded, you can also manually provide referenced schemas via the root schema:
{
...
referenced: {
'http://example.org/': { $id: 'http://example.org/', ... },
'urn:myschema': { $id: 'urn:myschema', ... },
}
}
The repository contains:
We welcome contributions. If you are interested in contributing to Dashjoin, let us know! You'll get to know an open-minded and motivated team working together to build the next generation platform.