# angular2-schema-form **Repository Path**: mirrors_Juniper/angular2-schema-form ## Basic Information - **Project Name**: angular2-schema-form - **Description**: HTML form generation based on JSON Schema - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-08-09 - **Last Updated**: 2025-12-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Angular2 Schema Form [![Build Status](https://travis-ci.org/makinacorpus/angular2-schema-form.svg?branch=master)](https://travis-ci.org/makinacorpus/angular2-schema-form) Angular2 Schema Form is an Angular2 module allowing you to instanciate an HTML form from a [JSON schema](http://json-schema.org/). ## DISCLAIMER Angular2 Schema Form is **not** related to [angular-schema-form](https://github.com/json-schema-form/angular-schema-form) and [schemaform.io](http://schemaform.io/). We think `angular-schema-form` is a great Angular 1 library, and when it will move to Angular 2, we will probably join our efforts to produce and maintain a unique Angular 2 solution. ## Demo There is an [example of application](https://github.com/fbessou/angular2-schema-form-demo) using Angular2 Schema Form. You can also test the module on [the website](https://makinacorpus.github.io/angular2-schema-form). ## Features * Generate a form from a single json schema object * Allow initialization from previous values * Validation handled by z-schema * Allow injection of custom validators * Allow declaration of custom widgets ## Installation To use Angular2 Schema Form in your project simply execute the following command: ```bash npm install angular2-schema-form --save ``` You just have to check that all the peer-dependencies of this module are satisfied in your package.json. ## Getting started Here our goal will be to create a simple login form. Let's start by creating a simple AppComponent taking a simple JSON schema as input. ```js // app.component.ts import { Component } from "@angular/core"; @Component({ selector:"minimal-app", // Bind the "mySchema" member to the schema input of the Form component. template: '' }) export class AppComponent { // The schema that will be used to generate a form mySchema = { "properties": { "email": { "type": "string", "description": "email", "format": "email" }, "password": { "type": "string", "description": "Password" }, "rememberMe": { "type": "boolean", "default": false, "description": "Remember me" } }, "required": ["email","password","rememberMe"] } } ``` Create a module which import the AppComponent and configure Angular2 schema form. ```js //app.module.ts import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; import { SchemaFormModule, WidgetRegistry, DefaultWidgetRegistry } from "angular2-schema-form"; import { AppComponent } from "./app.component"; @NgModule({ imports: [ SchemaFormModule, BrowserModule ], declarations: [AppComponent], providers: [{provide: WidgetRegistry, useClass: DefaultWidgetRegistry}] }) export class AppModule {} ``` The code above creates a form with three required fields. The validation state of each field is reflected by the class of each of them which can be either "has-error" or "has-success". Validation is done everytime a field's value changes. Basic validation is made by testing the value of the field against its corresponding schema. The input schema support almost all the features listed on the [JSON schema specification](http://json-schema.org/). ### Accessing the form's value #### Input binding It is possible to provide initial values to the form. You can set the initial form's value through the `model` input: ```js @Component({ template: '' }) export class AppComponent { mySchema = {...}; myModel = {email:" john.doe@example.com"}; } ``` #### Output binding The Form component provides the `onChange` output binding of which value represents the value of the form. For instance, you can display the current forms's value with the following template: ```js template: '{{value | json}}' ``` ### Widgets Each field can be displayed using a specific widget. To declare the widget you want to use, add its `id` to the field's definition: ```js mySchema = { "properties": { "email": { "type": "string", "description": "email", "format": "email" }, "password": { "type": "string", "description": "Password", "widget": "password"// == "widget": {"id": "password"} }, "rememberMe": { "type": "boolean", "default": false, "description": "Remember me" } } } ``` If there is no widget declared in a given property's schema, its type is used as widget id and the [default registry](#default-widgets-registry) gives a default widget (see details below). For instance, a string property will use the "string" widget. The following JSON schema is equivalent with the above example: ```js mySchema = { "properties": { "email": { "type": "string", "description": "email", "format": "email", "widget": "string" }, "password": { "type": "string", "description": "Password", "widget": "password"// == "widget": {"id": "password"} }, "rememberMe": { "type": "boolean", "default": false, "description": "Remember me", "widget": "boolean" } } } ``` Some widgets accept parameters as input, in such cases, it is possible to provide them in the schema directly within the `widget` property (here the [TinyMCE widget](https://github.com/fbessou/ng2sf-tinymce) ): ```js mySchema = { "properties": { "pageContent": { "type": "string", "description": "Page content", "widget": { "id": "richtext", "plugins": "textcolor colorpicker", "toolbar": "forecolor backcolor" } } } } ``` ### Default widget's registry Available widgets are managed through a `WidgetRegistry`. The default registry ([`DefaultWidgetRegistry`](./src/schema-form/defaultwidgets/defaultwidgetregistry.ts)) contains many widgets listed below, ordered by type: - **string**: string, search, tel, url, email, password, color, date, date-time, time, textarea, select, file, radio, richtext - **number**: number, integer, range - **integer**: integer, range - **boolean**: boolean, checkbox Note that the select and radio widgets rely on the `oneOf` property: ```js "operatingSystem": { "type": "string", "oneOf":[{ "enum": ["linux"], "description": "GNU/Linux" }, { "enum": ["osx"], "description": "OSX" }, { "enum": ["windows"], "description": "Windows" },{ "enum": ["other"], "description": "Other" }], "default": "other" } ``` ### Actions and buttons Each schema can be extended by adding buttons after its widget. ```js // app.component.ts @Component({ selector:"minimal-app", // Bind the actions map to the the "actions" input template: '' }) export class AppComponent { // The schema that will be used to generate a form mySchema = { "properties": { "email": { "type": "string", "description": "email", "format": "email" }, "password": { "type": "string", "description": "Password", "buttons": [{ "id": "reset", "label": "Reset" }] }, "rememberMe": { "type": "boolean", "default": false, "description": "Remember me" } }, "required": ["email","password","rememberMe"], "buttons": [{ "id": "alert", // the id of the action callback "label": "Alert !" // the text inside the button }] } // Declare a mapping between action ids and their event listener myActions = { "alert": (property) => {alert(JSON.stringify(property.value))}, "reset": (property) => {property.reset()} } } ``` #### Render buttons You may define you own widget to create buttons by overriding the default widget for action buttons or create completely customized button widgets. ##### Override Override the default action button widget in your `WidgetRegistry` implementation and register your own button widget. ```js this.register('button', MyButtonWidgetComponent); ``` ##### Custom Define a custom button widget by setting the property `button.widget` in the schema ```json "password": { "type": "string", "description": "Password", "buttons": [{ "id": "reset", "label": "Reset" },{ "id": "custom_b", "label": "My custom button", "widget": "my_custom_button" // custom widget name for this button }] }, ``` and then register it in your `WidgetRegistry` implementation ```js this.register('my_custom_button', MyCustomButtonWidgetComponent); ``` ##### Binding The button widget will get provided the `button` object form the schema including the `button.action` from the action registry and the `formProperty` object. To be fully AOT compatible the custom button widget may then extend `ButtonWidget` or provide the properties `button` and `formProperty` by it self. ```js import {Component} from "@angular/core"; import {ButtonWidget} from 'angular2-schema-form/dist/defaultwidgets' @Component({ selector: 'sf-button-widget', templateUrl: 'custom-button.widget.html' }) export class CustomWidgetComponent extends ButtonWidget { } ``` ```js @Component({ selector: 'sf-button-widget', templateUrl: 'custom-button.widget.html' }) export class CustomWidgetComponent { public button public formProperty } ``` ### Advanced validation JSON schema provides validation against a static schema but its often necessary to provide other validation rules. The Form component accepts a `validators` input bound to a map between a field id and a validation function. The validation function takes three arguments: the value of the field, the property corresponding to it and the form object. In the following example we create a simple registration form. The user have to enter his password twice. To perform this check we create a custom validator: ```js @Component({ selector:"minimal-app", // Bind the validator map to the the "validators" input template: '' }) export class AppComponent { mySchema = { "properties": { "email": { "type": "string", "description": "email", "format": "email" }, "password": { "type": "string", "description": "Password" }, "passwordCheck": { "type": "string", "description": "Password (verification)" } }, "required": ["email", "password", "passwordCheck"] } // Declare a mapping between action ids and their implementations myValidators = { "/passwordCheck": (value, property, form) => { if (controls.password !== undefined && controls.password.valid && value !== values.password ) { return {"passwordCheck":{"expectedValue":"same as 'password'"}} } return null; } } } ``` ### Conditional fields It is possible to make the presence of a field depends on another field's value. To achieve this you just have to add a `visibleIf` property to a field's definition. Adding the value $ANY$ to the array of conditional values,will make the field visible for any value inserted. ```js @Component({ selector:"minimal-app", template: '' }) export class AppComponent { mySchema = { "properties": { "name": { "type": "string", "description": "Username" }, "comment": { "type": "string", "description": "Comment" }, "registerNewsletter": { "type": "boolean", "description": "I want to receive the newsletter", "default": false, "visibleIf": { "comment": ['$ANY$'] } }, "registerEmail": { "type": "string", "description": "Email", "format": "email", // Declare that this field must be displayed only if registerNewsletter is true "visibleIf": { "registerNewsletter": [true] } } }, "required": ["name","comment","registerToNewsletter"] } } ``` Assigning an empty Object to 'visibleIf' is interpreted as _visibleIf_ nothing, thereby the widget is hidden. ```js mySchema = { "properties": { "hidden": { "type": "boolean", "visibleIf": { } } } } ``` #### Hidden fields When a field has been made invisible by the condition `visibleIf` then the property of the invisible field will be missing in the result model. If there is need to submit default values that are not visible for the form the `widget.id` `hidden` might be the better choice ```js mySchema = { "properties": { "hiddenInput": { "type": "boolean", "widget": "hidden", "default": true }, "lastName": { "type": "string", ... } } } ``` so the value of the hidden field will be bound to the output model ```js { "hiddenInput": true, "lastName": "Doe", ... } ``` ### Fields presentation and ordering As a JSON object is an unordered collection you can't be sure your fields will be correctly ordered when the form is built. The `order` and `fieldsets` entries of the schema are here to organize your fields. #### Ordering The `order` entry is an array listing all the fields ids in the order they must appear in the form: ```js { "properties": { "firstName": {"type": "string","description": "First name"}, "lastName": {"type": "string","description": "Last name"}, "email": {"type": "string","description": "Email"} }, "order": ["firstName", "lastName", "email"] } ``` #### Fieldsets With the `fieldsets` property, you can describe the different parts of the form and the fields they contain: ```js { "properties": { "firstName": {"type": "string","description": "First name"}, "lastName": {"type": "string","description": "Last name"}, "email": {"type": "string","description": "Email"}, "notificationsFrequency": { "type":"string", "description": "Notifications frequency", "widget": "select", "oneOf": [{ "description": "Daily", "enum": ["daily"] }, { "description": "Weekly", "enum": ["weekly"] }, { "description": "Monthly", "enum": ["monthly"] }], "default": "daily" } }, "fieldsets": [{ "title": "Personal information", "fields": ["firstName", "lastName", "email"] }, { "title": "Account settings", "fields": ["notificationsFrequency"] }] } ``` The `title` entry of each fieldset is optional. ## Fixing the schema or model before rendering Sometimes your schema (or model) is provided by a backend you cannot control. If it is not formatted the way Angular 2 Schema Form expects or if some elements are missing (for instance the fieldsets, some widgets, etc.), you can fix it very easily in your component: ```javascript @Component({ selector: 'plone-view-edit', template: '' }) export class MyComponent { private schema:any = 'properties': {} }; private actions:any = {}; private model:any = {}; constructor(private http: Http) { } ngOnInit() { this.http.get('http://mybackend/schema').subscribe(res => { let schema = res.json(); // FIXES // the "description" field must be rendered with tinymce schema.properties.description.widget = 'tinymce' // the "publication" field is required schema.required = ['publication']; this.schema = schema; }); } } ``` ## Creating a custom widget Angular2 schema form allows you to create your own widget. Note: Currently this feature is not completely defined and the API might change. You need to derivate the widget you want to customize: ```javascript @Component({ selector: 'mdl-sf-string-widget', templateUrl: './string.widget.html' }) export class MyStringWidget extends StringWidget {} ``` You need to provide its html template (let's imagine we want to use the Material Design text field): ```html ``` And you need to declare it in a custom registry: ```javascript import { MyStringWidget } from './mystring'; export class MyWidgetRegistry extends DefaultWidgetRegistry { constructor() { super(); this.register('string', MyStringWidget); } } ``` And then you need to provide your registry in your module: ```javascript providers: [{provide: WidgetRegistry, useClass: MyWidgetRegistry}], ``` Note: you will also need to import `ReactiveFormsModule` if you want to be able to use form control: ```javascript import { ReactiveFormsModule } from '@angular/forms'; ... @NgModule({ ... imports: [ ... ReactiveFormsModule, ] ``` ## Development and build To work on this package: ```bash npm install ``` You also need the peer dependencies: ```bash npm run install:peers ``` Then you can build: ```bash npm run build ``` If you want to work with the demo: ```bash npm install -g @angular/cli cd ./tests npm install cd ./src/app ln -s ../../../src/ lib cd - ng serve ``` ## Building the API documentation You can build an HTML version of the API documentation by running the following command: ```bash npm run typedoc ``` The api is then available in the "doc" directory.