README
Angular2 Schema Form
WARNING:
- MAINTENANCE BRANCH for 1.x (=> Angular < 5)
- THE PACKAGE HAS BEEN RENAMED ngx-schema-form
Angular2 Schema Form is an Angular2 module allowing you to instanciate an HTML form from a JSON schema.
DISCLAIMER
Angular2 Schema Form is not related to angular-schema-form and 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 using Angular2 Schema Form. You can also test the module on the website.
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:
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.
// 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: '<sf-form [schema]="mySchema"></sf-form>'
})
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.
//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.forRoot(),
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.
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:
@Component({
template: '<sf-form [schema]="mySchema" [model]="myModel"></sf-form>'
})
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:
template: '<sf-form [schema]="mySchema" (onChange)="value=$event.value"></sf-form>{{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:
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 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:
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 ):
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
) 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:
"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.
// app.component.ts
@Component({
selector: "minimal-app",
// Bind the actions map to the the "actions" input
template: '<sf-form [schema]="mySchema" [actions]="myActions"></sf-form>'
})
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.
this.register('button', MyButtonWidgetComponent);
Custom
Define a custom button widget by
setting the property button.widget
in the schema
"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
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.
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 {
}
@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:
@Component({
selector: "minimal-app",
// Bind the validator map to the the "validators" input
template: '<sf-form [schema]="mySchema" [validators]="myValidators"></sf-form>'
})
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.
@Component({
selector: "minimal-app",
template: '<sf-form [schema]="mySchema"></sf-form>'
})
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