Skip to content

Repository files navigation

ajsf

AJSF (Angular JSON Schema Form)

AJSF turns a JSON Schema into an Angular 22 form, rendered as plain HTML by @ajsf/core or styled by one of five framework packages: @ajsf/material, @ajsf/primeng, @ajsf/bootstrap3, @ajsf/bootstrap4 and @ajsf/bootstrap5.

CI Status Code coverage npm version npm number of downloads MIT licence GitHub stars Netlify Status

The playground rendering a JSON Schema as a Material form, submitting it, then rendering the same schema with Bootstrap 5

Try the live playground

Note: This project is a continuation of dschnelldavis/Angular2-json-schema-form and is not affiliated with any organization.

A JSON Schema Form builder for Angular, similar to, and mostly API compatible with:

Packages

Version compatibility

From 14.0.0 onward, the @ajsf major matches the Angular major it targets, the same convention Angular Material uses. For the newest Angular major supported by AJSF, install the current release:

npm install @ajsf/material

npm uses the latest tag when no version is specified. If your application uses an older Angular major, install the matching AJSF major instead:

npm install @ajsf/material@17   # for Angular 17

Peer ranges are bounded from 14.0.0 onward, so npm reports a clear resolution error rather than installing a combination that was never built or tested. This also means a plain install fails when the application is not on the newest supported Angular major.

0.8.0 and earlier predate this scheme. They declare open peer ranges with no upper bound: >=14.0.0 in 0.8.0, which was built against Angular 14, >=13.0.0 in 0.7.0, and lower floors before that, down to >=6.0.0 in 0.1.x.

There is no release for Angular 15, which reached end of life. See the versions on npm for what is currently available.

Upgrading from 0.8.0 to 14.0.0

@ajsf/material no longer depends on @angular/flex-layout, which is deprecated and has no Angular 16 release. You can uninstall it unless something else in your project uses it.

No code change is needed. FlexLayoutRootComponent, FlexLayoutSectionComponent, the flex and section layout types and options such as fxFlex, fxFlexAlign and fxLayoutGap all behave as before. The version jump is the Angular-aligned scheme starting, not a rewrite: 14.0.0 targets the same Angular 14 that 0.8.0 did.

Angular 6 to 8

Before the @ajsf packages, this project shipped as one package, angular6-json-schema-form. Its last release is 8.0.0, and its documentation is on the angular6-json-schema-form branch.

JSON Schema versions

Draft 4, 6 and 7 schemas work as they are. Drafts 1 to 3 were dropped in 19.0.0: a property level required, optional or requires is removed, with a console warning naming the properties, so move those into a draft 4 required array.

Draft Status
Draft 1, 2, 3 Not supported since 19.0.0
Draft 4 Supported, converted to draft 7 internally
Draft 6 Supported directly
Draft 7 Supported, including if, then and else
2019-09, 2020-12 Not supported yet

Every schema passes through convertSchemaToDraft6 before the form is built. Despite the name, a draft 4 schema comes out stamped as draft 7. Keywords it does not recognise are carried through untouched rather than dropped, which is why draft 7 schemas validate correctly.

A schema without $schema is read as draft 7.

Two limits are worth knowing about before you rely on them.

Draft 7 conditionals validate, but the layout does not follow them. if, then and else are enforced, so a field that becomes required because of another field's value really is required and the form will not submit without it. The layout is built once, though, so that field shows no required marker and no message, even after it is touched or the user tries to submit: with the default options Submit stays disabled and nothing names the missing field. readOnly and writeOnly are accepted and currently have no effect: they are annotations, and neither the validator nor the widgets act on them.

2020-12 is not supported yet. A schema declaring "$schema": "https://json-schema.org/draft/2020-12/schema" fails to compile and the form does not render, so it fails loudly rather than quietly. It needs a newer validator, and prefixItems replaces the array form of items, which is how AJSF recognises tuples.

Check out the live demo and play with the examples

Check out some examples here.

The playground includes more than 70 JSON Schemas. You can render each one with Material Design, PrimeNG, Bootstrap 3, Bootstrap 4, Bootstrap 5, or plain HTML.

Installation

To install from npm or Yarn and use in your own project

Pick the package for the UI you want. @ajsf/material renders with Angular Material, @ajsf/primeng renders with PrimeNG, and there are Bootstrap 3, Bootstrap 4 and Bootstrap 5 packages alongside them.

@ajsf/material renders Angular Material components, so it declares @angular/material and @angular/cdk as peer dependencies. Your app needs both installed and a Material theme. ng add @angular/material installs both, writes a theme to src/material-theme.scss, adds it to the angular.json styles and links the Roboto and Material Symbols fonts in index.html:

ng add @angular/material

Then install AJSF. npm uses the current latest release when no version is specified:

npm install @ajsf/material

Or with Yarn:

yarn add @ajsf/material

For an older Angular major, install the matching AJSF major as described in Version compatibility.

For PrimeNG setup, including theme configuration, see the @ajsf/primeng getting started guide.

Then import MaterialDesignFrameworkModule in the imports of the standalone component that renders the form, which is how ng new scaffolds an Angular 22 app. The module provides everything the form needs, so app.config.ts stays as ng new wrote it:

import { Component } from '@angular/core';
import { MaterialDesignFrameworkModule } from '@ajsf/material';

@Component({
  selector: 'app-root',
  imports: [MaterialDesignFrameworkModule],
  templateUrl: './app.html',
})
export class App { }

In an app that still bootstraps an NgModule, add the framework module to that module's imports instead. The declared component needs standalone: false, since components are standalone by default:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MaterialDesignFrameworkModule } from '@ajsf/material';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [ AppComponent ],
  imports: [ BrowserModule, MaterialDesignFrameworkModule ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

No animations setup is needed. With Angular Material 22, the select, datepicker, expansion panel, tabs and stepper widgets open, switch and submit without @angular/animations installed and with nothing logged to the console. ng new does not install that package, so importing BrowserAnimationsModule fails the build with Could not resolve "@angular/animations/browser", and Angular has deprecated it since 20.2.

Zoneless change detection, the Angular 22 default, works as well: no zone.js is needed.

@ajsf/material takes the initial bundle of a new app to 1.40 MB, and ng new sets a 1 MB maximumError budget, so ng build fails with bundle initial exceeded maximum budget. Raise the initial budget in the production configuration of angular.json:

{
  "type": "initial",
  "maximumWarning": "2MB",
  "maximumError": "2.5MB"
}

@ajsf/primeng needs the same change (1.55 MB). The Bootstrap packages and @ajsf/core stay under 1 MB.

The build also warns that ajv and ajv-formats are not ES modules. That warning is harmless, and adding both to allowedCommonJsDependencies in the build options silences it:

"allowedCommonJsDependencies": ["ajv", "ajv-formats"]

Six framework modules are available. Choose the one that matches the UI you want:

  • MaterialDesignFrameworkModule from @ajsf/material for Material Design
  • PrimengFrameworkModule from @ajsf/primeng for PrimeNG
  • Bootstrap3FrameworkModule from @ajsf/bootstrap3 for Bootstrap 3
  • Bootstrap4FrameworkModule from @ajsf/bootstrap4 for Bootstrap 4
  • Bootstrap5FrameworkModule from @ajsf/bootstrap5 for Bootstrap 5
  • JsonSchemaFormModule from @ajsf/core for plain HTML (no styling)

All six are used the same way, in the imports of a standalone component or an NgModule, and none needs extra providers. In 22.2.1 and earlier, a standalone component that imports only JsonSchemaFormModule also needs providers: [FrameworkLibraryService], imported from @ajsf/core, or the form fails with NG0201.

It is also possible to load multiple frameworks and switch between them at runtime, like the example playground on GitHub. But most typical sites will just load one framework.

To install from GitHub

To run the library and the example playground from GitHub, assuming you have git and Node installed, enter the following in your terminal:

git clone https://github.com/hamzahamidi/ajsf.git ajsf
cd ajsf
npm ci
npm start

The repository ships a package-lock.json, so use npm rather than Yarn here: npm ci installs exactly the versions CI builds and tests with. See .nvmrc for the Node version.

This starts the example playground at http://localhost:4200.

The main directories are:

  • projects/ajsf-core: Angular JSON Schema Form main library
  • projects/ajsf-bootstrap3: Bootstrap 3 framework
  • projects/ajsf-bootstrap4: Bootstrap 4 framework
  • projects/ajsf-bootstrap5: Bootstrap 5 framework
  • projects/ajsf-material: Angular Material framework
  • projects/ajsf-primeng: PrimeNG framework
  • projects/ajsf-core/src/lib/framework-library: framework library
  • projects/ajsf-core/src/lib/widget-library: widget library
  • projects/ajsf-core/src/lib/shared: utilities and helper functions
  • demo: example playground application
  • demo/assets/example-schemas: JSON Schema examples used in the playground

The API reference is generated from the six packages' public exports and published with the playground. The functions under projects/ajsf-core/src/lib/shared carry doc comments describing what they do; the widget and framework libraries mostly do not, so their pages list signatures without descriptions.

Using Angular JSON Schema Form

Basic use

For basic use, after importing a framework module as described above, to display a form in your Angular component, simply add the following to your component's template:

<json-schema-form
  [loadExternalAssets]="true"
  [schema]="yourJsonSchema"
  framework="material-design"
  (onSubmit)="yourOnSubmitFn($event)">
</json-schema-form>

Here, schema is a valid JSON Schema object and onSubmit calls a function that processes the submitted form data. Sample schemas are available in demo/assets/example-schemas.

framework selects the template set to render with. The default is no-framework. The possible values are:

  • material-design for Material Design
  • primeng for PrimeNG
  • bootstrap-3 for Bootstrap 3
  • bootstrap-4 for Bootstrap 4
  • bootstrap-5 for Bootstrap 5
  • no-framework for plain HTML

Setting [loadExternalAssets]="true" loads assets the display framework needs from a CDN. It is useful while trying the library out, but production sites should load those assets themselves. See Loading external assets required by a framework for details.

Keep the brackets: the input is a boolean, and a project created by ng new rejects the plain attribute loadExternalAssets="true" with TS2322.

Note what this does and does not cover. For bootstrap-4 and bootstrap-5 it loads Bootstrap's CSS and JavaScript, so a form is styled straight away. For material-design it loads only the Material Icons and Roboto fonts: an Angular Material theme is not included, so add one to your app as ng add @angular/material offers to do, or the controls render unthemed.

Data-only mode

Angular JSON Schema Form can also create a form entirely from a JSON object, with no schema, like so:

<json-schema-form
  [loadExternalAssets]="true"
  [(ngModel)]="exampleJsonObject">
</json-schema-form>
exampleJsonObject = {
  "first_name": "Jane", "last_name": "Doe", "age": 25, "is_company": false,
  "address": {
    "street_1": "123 Main St.", "street_2": null,
    "city": "Las Vegas", "state": "NV", "zip_code": "89123"
  },
  "phone_numbers": [
    { "number": "702-123-4567", "type": "cell" },
    { "number": "702-987-6543", "type": "work" }
  ], "notes": ""
};

In this mode, Angular JSON Schema Form automatically generates a schema from your data. The generated schema is relatively simple, compared to what you could create on your own. However, as the above example shows, it does detect and enforce string, number, and boolean values (nulls are also assumed to be strings), and automatically allows array elements to be added, removed, and reordered.

After displaying a form in this mode, use the formSchema and formLayout outputs to inspect the generated schema and layout. See Debugging inputs and outputs.

The ngModel input supports Angular's bidirectional data binding, so an onSubmit function is not always necessary.

Advanced use

Additional inputs and outputs

For more control over your form, you may provide these additional inputs:

  • layout array with a custom form layout (see Angular Schema Form's form definitions for information about how to construct a form layout)
  • data object to populate the form with default or previously submitted values
  • options object to set any global options for the form
  • widgets object to add custom widgets
  • language string to set the error message language (currently supports de, en, es, fr, it, pt, and zh)
  • framework string or object to set which framework to use

For framework, pass a custom framework object or the name of a loaded framework. The included names are material-design, primeng, bootstrap-3, bootstrap-4, bootstrap-5, and no-framework.

If you want more detailed output, you may provide additional functions for onChanges to read the values in real time as the form is being filled out, and you may implement your own custom validation indicators from the boolean isValid or the detailed validationErrors outputs.

Here is an example:

<json-schema-form
  [schema]="yourJsonSchema"
  [layout]="yourJsonFormLayout"
  [(data)]="yourData"
  [options]="yourFormOptions"
  [widgets]="yourCustomWidgets"
  language="fr"
  framework="material-design"
  [loadExternalAssets]="true"
  (onChanges)="yourOnChangesFn($event)"
  (onSubmit)="yourOnSubmitFn($event)"
  (isValid)="yourIsValidFn($event)"
  (validationErrors)="yourValidationErrorsFn($event)">
</json-schema-form>

Note: If you prefer brackets around all your attributes, the following is functionally equivalent:

<json-schema-form
[schema]="yourJsonSchema"
[layout]="yourJsonFormLayout"
[(data)]="yourData"
[options]="yourFormOptions"
[widgets]="yourCustomWidgets"
[language]="'fr'"
[framework]="'material-design'"
[loadExternalAssets]="true"
(onChanges)="yourOnChangesFn($event)"
(onSubmit)="yourOnSubmitFn($event)"
(isValid)="yourIsValidFn($event)"
(validationErrors)="yourValidationErrorsFn($event)">
</json-schema-form>

With this syntax, include the nested quotes ("' and '") around language and framework names. Without the inner quotes, Angular reads the values as variables instead of strings. Attributes without brackets are read as strings and do not need inner quotes.

Single-input mode

You may also combine all your inputs into one compound object and include it as a form input, like so:

const yourCompoundInputObject = {
  schema:    { ... },  // REQUIRED
  layout:    [ ... ],  // optional
  data:      { ... },  // optional
  options:   { ... },  // optional
  widgets:   { ... },  // optional
  language:   '...' ,  // optional
  framework:  '...'    // (or { ... }) optional
}
<json-schema-form
  [form]="yourCompoundInputObject"
  (onSubmit)="yourOnSubmitFn($event)">
</json-schema-form>

You can also mix these two styles depending on your needs. In the example playground, all examples use the combined form input for schema, layout, and data, which enables each example to control those three inputs, but the playground uses separate inputs for language and framework, enabling it to change those settings independent of the example.

Combining inputs is useful when each form stores its data and schema together. Separate inputs are often clearer for one form or several forms with the same structure. A custom layout can still be stored with its schema and passed through the combined input.

Compatibility modes

If you have used Angular Schema Form for AngularJS, React JSON Schema Form, or JSON Form for jQuery, Angular JSON Schema Form recognizes their input names and custom input objects. It also accepts the truncated schema format supported by JSON Form, though a draft 3 required: true on a property is dropped with a console warning. See JSON Schema versions for the drafts AJSF supports.

Angular Schema Form (AngularJS) compatibility:

<json-schema-form
  [schema]="yourJsonSchema"
  [form]="yourAngularSchemaFormLayout"
  [(model)]="yourData">
</json-schema-form>

React JSON Schema Form compatibility:

<json-schema-form
  [schema]="yourJsonSchema"
  [UISchema]="yourReactJsonSchemaFormUISchema"
  [(formData)]="yourData">
</json-schema-form>

JSON Form (jQuery) compatibility:

<json-schema-form
  [form]="{
    schema: yourJsonSchema,
    form: yourJsonFormLayout,
    customFormItems: yourJsonFormCustomFormItems,
    value: yourData
  }">
</json-schema-form>

Bidirectional data binding works with the data, model, ngModel, and formData inputs. It does not work with the combined form input.

Debugging inputs and outputs

Finally, Angular JSON Schema Form includes some additional inputs and outputs for debugging:

  • debug input: activates debugging mode.
  • loadExternalAssets input: loads the external JavaScript and CSS the selected framework needs from a CDN. Useful while trying the library out, but not reliable enough for production, where you should load those assets yourself. If the console reports that an asset failed to load (jQuery, for example, which Bootstrap 3 needs), reloading the page usually fixes it.
  • formSchema and formLayout outputs: return the final schema and layout used to build the form. That shows how your inputs were modified, or, if you gave none, the ones generated from your data.
<json-schema-form
  [schema]="yourJsonSchema"
  [debug]="true"
  [loadExternalAssets]="true"
  (formSchema)="showFormSchemaFn($event)"
  (formLayout)="showFormLayoutFn($event)">
</json-schema-form>

Customizing

In addition to a large number of user-settable options, Angular JSON Schema Form also has the ability to load custom form control widgets and layout frameworks. All forms are constructed from these basic components. The default widget library includes all standard HTML 5 form controls, as well as several common layout patterns, such as multiple checkboxes and tab sets. The default framework library includes templates to style forms using Material Design, PrimeNG, Bootstrap 3, Bootstrap 4, or Bootstrap 5 (or plain HTML with no formatting, which is not useful in production, but can be helpful for development and debugging).

User settings

(TODO: List all available user settings, and configuration options for each.)

Creating custom input validation error messages

You can easily add your own custom input validation error messages, either for individual control widgets, or for your entire form.

Setting error messages for individual controls or the entire form

To set messages for individual form controls, add them to that control's node in the form layout, like this:

const yourFormLayout = [
  { key: 'name',
    title: 'Enter your name',
    validationMessages: {
      // Put your error messages for the 'name' field here
    }
  },
  { type: 'submit', title: 'Submit' }
]

To set messages for the entire form, add them to defautWidgetOptions.validationMessages in the form options. The defautWidgetOptions spelling is part of the public API and is preserved for compatibility.

const yourFormOptions = {
  defautWidgetOptions: {
    validationMessages: {
      // Put your error messages for the entire form here
    }
  }
}

How to format error messages

The validationMessages object uses validator names as keys and messages as values. Messages may use any of these formats:

  • String: A plain text message, which is always the same.
  • String template: Text containing Angular template style {{variables}}, replaced with values from the returned error object.
  • Function: A JavaScript function which accepts the error object as input, and returns a string error message.

Here are examples of all three error message types:

validationMessages: {

  // String error message
  required: 'This field is required.',

  // String template error message
  // - minimumLength variable will be replaced
  minLength: 'Must be at least {{minimumLength}} characters long.',

  // Function error message
  // Example error: { minimumValue: 2, currentValue: 1 }
  minimum: function(error) {
    return `Must be at least ${error.minimumValue}; received ${error.currentValue}.`;
  }
}

The message key must match the validator error it handles. The table below lists the values available to each message.

Available input validation errors and object values

Here is a list of all the built-in JSON Schema errors, which data type each error is available for, and the values in their returned error objects:

Error name Data type Returned error object values
required any (none)
type any requiredType, currentValue
const any requiredValue, currentValue
enum any allowedValues, currentValue
minLength string minimumLength, currentLength
maxLength string maximumLength, currentLength
pattern string requiredPattern, currentValue
format string requiredFormat, currentValue
minimum number minimumValue, currentValue
exclusiveMinimum number exclusiveMinimumValue, currentValue
maximum number maximumValue, currentValue
exclusiveMaximum number exclusiveMaximumValue, currentValue
multipleOf number multipleOfValue, currentValue
minProperties object minimumProperties, currentProperties
maxProperties object maximumProperties, currentProperties
dependencies * object (varies, based on dependencies schema)
minItems array minimumItems, currentItems
maxItems array maximumItems, currentItems
uniqueItems array duplicateItems
contains * array requiredItem
  • Note: contains and dependencies are enforced on the form as a whole. While either fails, the isValid output is false, validationErrors reports it, and with the default options the submit button stays disabled.

contains never shows a message on a field. With Material, dependencies on a nested object shows a generated message on that object's section, such as Credit card Error: Billing address: Required when credit_card requires billing_address. On the root object it shows none.

Changing or adding widgets

To add a new widget or override an existing widget, either add an object containing your new widgets to the widgets input of the <json-schema-form> tag, or load the WidgetLibraryService and call registerWidget(widgetType, widgetComponent), with a string type name and an Angular component to be used whenever a form needs that widget type.

Example:

import { YourInputWidgetComponent } from './your-input-widget.component';
import { YourCustomWidgetComponent } from './your-custom-widget.component';
...
const yourNewWidgets = {
  'text': YourInputWidgetComponent,           // Replace the existing 'text' widget
  'custom-control': YourCustomWidgetComponent // Add a new 'custom-control' widget
}

Use the widget map in a form:

<json-schema-form
  [schema]="yourJsonSchema"
  [widgets]="yourNewWidgets">
</json-schema-form>

You can also register widgets directly:

import { WidgetLibraryService } from '@ajsf/core';
...
constructor(private widgetLibrary: WidgetLibraryService) { }
...
// Replace the existing 'text' widget:
widgetLibrary.registerWidget('text', YourInputWidgetComponent);
// Add new 'custom-control' widget:
widgetLibrary.registerWidget('custom-control', YourCustomWidgetComponent);

Call getAllWidgets() on WidgetLibraryService to inspect the registered widgets. Default widgets are in projects/ajsf-core/src/lib/widget-library, Material widgets are in projects/ajsf-material/src/lib/widgets, and PrimeNG widgets are in projects/ajsf-primeng/src/lib/widgets.

Bootstrap 3 reformats the default widgets. Bootstrap 4 and Bootstrap 5 do the same for most widgets, and since 22.1.0 ship their own checkbox, checkboxes and radios widgets, in projects/ajsf-bootstrap4/src/lib/widgets and projects/ajsf-bootstrap5/src/lib/widgets.

Changing or adding frameworks

To change the active framework, either use the framework input of the <json-schema-form> tag, or load the FrameworkLibraryService and call setFramework(yourCustomFramework), with either the name of an available framework ('bootstrap-3', 'bootstrap-4', 'bootstrap-5', 'material-design', 'primeng', or 'no-framework'), or with your own custom framework object, like so:

import { YourFrameworkComponent } from './your-framework.component';
import { YourWidgetComponent } from './your-widget.component';
...
const yourCustomFramework = {
  framework: YourFrameworkComponent,                                // required
  widgets:     { 'your-widget-name': YourWidgetComponent,   ... },  // optional
  stylesheets: [ '//url-to-framework-external-style-sheet', ... ],  // optional
  scripts:     [ '//url-to-framework-external-script',      ... ]   // optional
}

Use the framework in a form:

<json-schema-form
  [schema]="yourJsonSchema"
  [framework]="yourCustomFramework">
</json-schema-form>

You can also register it directly:

import { FrameworkLibraryService } from '@ajsf/core';
...
constructor(private frameworkLibrary: FrameworkLibraryService) { }
...
frameworkLibrary.setFramework(yourCustomFramework);

The required framework key is the Angular component used to format each widget. The optional widgets object overrides or adds widgets. The optional stylesheets and scripts arrays contain external assets loaded when loadExternalAssets is true.

Loading external assets required by a framework

Most UI frameworks need external JavaScript or CSS assets. Load them in your application before rendering an Angular JSON Schema Form. See the setup guides for Bootstrap and Angular Material.

During development, Angular JSON Schema Form can load these resources for you in three ways:

  • Call setFramework with a second parameter of true (e.g. setFramework('material-design', true)), or
  • Add loadExternalAssets: true to your options object, or
  • Add [loadExternalAssets]="true" to your <json-schema-form> tag, as shown above

Finally, if you want to see what scripts a particular framework will automatically load, after setting that framework you can call getFrameworkStylesheets() or getFrameworkScripts() from the FrameworkLibraryService to return the built-in arrays of URLs.

In production, load these assets in the application and remove loadExternalAssets to avoid loading them twice.

Sponsors

Support AJSF through GitHub Sponsors. Sponsors are recognized here according to their selected tier.

Contributing

See the contributing guide for local setup, tests, and pull request guidance.

License

MIT

About

JSON Schema form builder for Angular, with Material, Bootstrap 3, Bootstrap 4, Bootstrap 5 and primeNG widgets

Topics

Resources

Contributing

Security policy

Stars

359 stars

Watchers

17 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages