• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

guillotinaweb/ngx-schema-form: HTML form generation based on JSON Schema

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

guillotinaweb/ngx-schema-form

开源软件地址:

https://github.com/guillotinaweb/ngx-schema-form

开源编程语言:

TypeScript 91.4%

开源软件介绍:

Ngx Schema Form Build Status

Ngx Schema Form is an Angular 2+ module allowing you to instanciate an HTML form from a JSON schema.

Note: Version 1.x is compliant with Angular <=4, version 2.x is compliant with Angular >=6.

DISCLAIMER

Ngx 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

Demo

Features

  • Generate a form from a single json schema object
  • Generate a form from a default set of html constructs
  • Allow initialization from previous values
  • Validation handled by z-schema
  • Allow injection of custom validators
  • Allow declaration of custom widgets
  • Allow injection of custom bindings (new!)

Installation

To use Ngx Schema Form in your project simply execute the following command:

npm install ngx-schema-form --save

You just have to check that all the peer-dependencies of this module are satisfied in your package.json.

JSON Schema

With the installation there comes a JSON-Schema file that declares all specific or additional properties supported by ngx-schema-form.

When using *.json files you may declare it with the $schema property to let your IDE's autocompletion help you create a schema-form.

{
  "$schema": "./node_modules/ngx-schema-form/ngx-schema-form-schema.json",
  "title": "My awesome schema-form"
  ...
}

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 Ngx schema form.

//app.module.ts

import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import {
  SchemaFormModule,
  WidgetRegistry,
  DefaultWidgetRegistry,
} from "ngx-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:" [email protected]"};
}

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}}';

The model property allow two-way data binding:

<sf-form [schema]="mySchema" [(model)]="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 "ngx-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) => {
      const passwordProperty = formProperty.findRoot().getProperty("password");
      if (
        passwordProperty.value !== undefined &&
        property.valid &&
        value !== passwordProperty.value
      ) {
        return { passwordCheck: { expectedValue: "same as 'password'" } };
      }
      return null;
    },
  };
}

Custom Bindings

Some form field may require a reaction to other forms fields when getting some input. The Form component accepts a bindings input bound to a map of field paths mapped to event and binding functions.
The binding function takes two arguments: the native event, and the property corresponding to it.

The following example creates a form where you will fill in some data for a family. When you type in the name of the parent (first person) the name of the children will be kept updated.

@Component({
  selector: "minimal-app",
  // Bind the bindings map to the the "bindings" input
  template:
    '<sf-form [schema]="mySchema" [bindings]="myFieldBindings"></sf-form>',
})
export class AppComponent {
  mySchema = {
    type: "object",
    title: "Example with custom bindings.",
    description:
      "Type a family name to see how the name gets synchronized with the children.",
    properties: {
      name: {
        type: "string",
        title: "Surname",
      },
      forename: {
        type: "string",
        title: "Forename",
      },
      children: {
        type: "array",
        title: "Family",
        items: {
          type: "object",
          title: "Children",
          properties: {
            name: {
              type: "string",
              title: "Surname",
            },
            forename: {
              type: "string",
              title: "forename",
            },
            age: {
              type: "number",
              title: "age",
            },
          },
        },
      },
    },
  };

  // Declare a mapping between field and event-id
  myFieldBindings = {
    "/name": [
      {
        input: (event, formProperty: FormProperty) => {
          const parent: PropertyGroup = formProperty.findRoot();

          /**
           * Set the input value for the children
           */
          const child1: FormProperty = parent.getProperty("children/0/name");

          child1.setValue(formProperty.value, false);

          const child2: FormProperty = parent.getProperty("children/1/name");
          child2.
                      

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap