Skip to content

Schema Formio

This tutorial shows how to use the @tsed/schema-formio package to generate Form.io compatible schemas from TypeScript classes using decorators.

Installation

bash
npm install --save @tsed/schema-formio
bash
yarn add @tsed/schema-formio
bash
pnpm add @tsed/schema-formio
bash
bun add @tsed/schema-formio

Introduction

The @tsed/schema-formio package allows you to transform a Schema declared with @tsed/schema into a valid Form.io schema. This is particularly useful when you want to automatically generate forms based on your TypeScript models.

The package supports most of the JsonSchema decorators provided by @tsed/schema (like Property, Required, CollectionOf, etc.) and adds additional decorators to customize the Form.io components generated for your class properties.

Basic Usage

Here's a simple example of how to use the package:

typescript
import {getFormioSchema, Form} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Property()
  firstName: string;

  @Property()
  lastName: string;

  @Property()
  email: string;
}

// Generate the Form.io schema
const formioSchema = await getFormioSchema(User);
console.log(formioSchema);
json
{
  "components": [
    {
      "type": "textfield",
      "key": "firstName",
      "label": "First Name",
      "input": true
    },
    {
      "type": "textfield",
      "key": "lastName",
      "label": "Last Name",
      "input": true
    },
    {
      "type": "email",
      "key": "email",
      "label": "Email",
      "input": true
    }
  ]
}

This will generate a Form.io schema with text fields for firstName, lastName, and email.

Basic Form.io Example

TIP

You can play with Formio builder to see the generated schema in action: Form.io Builder.

Decorators

Form

The Form decorator is used to mark a class as a Form.io form. It's required for the getFormioSchema function to work properly.

typescript
import {Form} from "@tsed/schema-formio";

@Form()
export class User {
  // properties
}

Component

The Component decorator allows you to define any extra Form.io metadata on a decorated property:

typescript
import {Form, Component} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Component({
    tooltip: "Enter your username",
    placeholder: "Username",
    description: "Your unique username for the system"
  })
  @Property()
  username: string;
}
json
{
  "components": [
    {
      "type": "textfield",
      "key": "username",
      "label": "Username",
      "input": true,
      "tooltip": "Enter your username",
      "placeholder": "Username",
      "description": "Your unique username for the system"
    }
  ],
  "display": "form",
  "name": "user",
  "title": "User",
  "type": "form"
}

Note

Tooltip, Placeholder, and Description decorators can also be used directly on properties to achieve the same effect.

Currency

The Currency decorator changes a property to a Currency component:

typescript
import {Form, Currency} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class Product {
  @Property()
  name: string;

  @Currency()
  @Property()
  price: number;
}
json
{
  "components": [
    {
      "type": "textfield",
      "key": "name",
      "label": "Name",
      "input": true
    },
    {
      "type": "currency",
      "key": "price",
      "label": "Price",
      "input": true,
      "currency": "USD",
      "delimiter": true,
      "allowNegative": false
    }
  ],
  "display": "form",
  "name": "product",
  "title": "Product",
  "type": "form"
}

DataSourceJson

The DataSourceJson decorator adds a custom data source for a Select component:

typescript
import {Form, DataSourceJson, Select} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Select()
  @DataSourceJson([
    {value: "admin", label: "Administrator"},
    {value: "user", label: "Regular User"},
    {value: "guest", label: "Guest"}
  ])
  @Property()
  role: string;
}

DataSourceUrl

The DataSourceUrl decorator fetches data source from an endpoint for a Select component:

typescript
import {Form, DataSourceUrl, Select} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Select()
  @DataSourceUrl("/api/roles")
  @Property()
  role: string;
}

Hidden

The Hidden decorator changes the property to a Hidden component:

typescript
import {Form, Hidden} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Hidden()
  @Property()
  id: string;

  @Property()
  name: string;
}

InputTags

The InputTags decorator changes the property to an InputTags component:

typescript
import {Form, InputTags} from "@tsed/schema-formio";
import {Property, CollectionOf} from "@tsed/schema";

@Form()
export class Article {
  @Property()
  title: string;

  @InputTags()
  @CollectionOf(String)
  @Property()
  tags: string[];
}

Label

The Label decorator customizes the label for a component:

typescript
import {Form, Label} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Label("First Name")
  @Property()
  firstName: string;

  @Label("Last Name")
  @Property()
  lastName: string;
}

Mask

The Mask decorator adds an input mask to a component:

typescript
import {Form, Mask} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Mask("(999) 999-9999")
  @Property()
  phoneNumber: string;

  @Mask("99/99/9999")
  @Property()
  birthDate: string;
}

MaxWords

The MaxWords decorator sets the maximum number of words for a text component:

typescript
import {Form, MaxWords, Textarea} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class Article {
  @Property()
  title: string;

  @Textarea()
  @MaxWords(100)
  @Property()
  summary: string;
}

MinWords

The MinWords decorator sets the minimum number of words for a text component:

typescript
import {Form, MinWords, Textarea} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class Article {
  @Property()
  title: string;

  @Textarea()
  @MinWords(10)
  @Property()
  summary: string;
}

ModalEdit

The ModalEdit decorator enables modal editing for a component:

typescript
import {Form, ModalEdit} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @ModalEdit()
  @Property()
  profile: object;
}

Multiple

The Multiple decorator sets the multiple flag on a property:

typescript
import {Form, Multiple, Select} from "@tsed/schema-formio";
import {Property, CollectionOf} from "@tsed/schema";

@Form()
export class User {
  @Select()
  @Multiple()
  @CollectionOf(String)
  @Property()
  roles: string[];
}

OpenWhenEmpty

The OpenWhenEmpty decorator makes a component open when it's empty:

typescript
import {Form, OpenWhenEmpty} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @OpenWhenEmpty()
  @Property()
  comments: string;
}

Password

The Password decorator changes the property to an input Password component:

typescript
import {Form, Password} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Property()
  email: string;

  @Password()
  @Property()
  password: string;
}

Placeholder

The Placeholder decorator adds a placeholder to a component:

typescript
import {Form, Placeholder} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Placeholder("Enter your email")
  @Property()
  email: string;

  @Placeholder("Enter your password")
  @Property()
  password: string;
}

Prefix

The Prefix decorator adds a prefix to a component:

typescript
import {Form, Prefix} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class Product {
  @Property()
  name: string;

  @Prefix("$")
  @Property()
  price: number;
}

Select()

The Select decorator changes the property to a Select component:

typescript
import {Form, Select} from "@tsed/schema-formio";
import {Property, Enum} from "@tsed/schema";

export enum UserRole {
  ADMIN = "admin",
  USER = "user",
  GUEST = "guest"
}

@Form()
export class User {
  @Property()
  name: string;

  @Select()
  @Enum(UserRole)
  @Property()
  role: UserRole;
}

Suffix

The Suffix decorator adds a suffix to a component:

typescript
import {Form, Suffix} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class Product {
  @Property()
  weight: number;

  @Suffix("kg")
  @Property()
  weight: number;
}

TableView

The TableView decorator controls whether a property is displayed in a table view:

typescript
import {Form, TableView} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @TableView(true)
  @Property()
  name: string;

  @TableView(false)
  @Property()
  password: string;
}

Tabs

The Tabs decorator groups properties using the Form.io tab component:

typescript
import {Form, Tabs} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Tabs("Basic Information")
  @Property()
  firstName: string;

  @Tabs("Basic Information")
  @Property()
  lastName: string;

  @Tabs("Contact Information")
  @Property()
  email: string;

  @Tabs("Contact Information")
  @Property()
  phone: string;

  @Tabs("Security")
  @Property()
  password: string;
}

Textarea

The Textarea decorator changes a component to a Textarea component:

typescript
import {Form, Textarea} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class Article {
  @Property()
  title: string;

  @Textarea()
  @Property()
  content: string;
}

TextCase

The TextCase decorator sets the text case for a component:

typescript
import {Form, TextCase} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @TextCase("uppercase")
  @Property()
  code: string;

  @TextCase("lowercase")
  @Property()
  email: string;
}

Tooltip

The Tooltip decorator adds a tooltip to a component:

typescript
import {Form, Tooltip} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Tooltip("Enter your full name")
  @Property()
  name: string;

  @Tooltip("Enter a valid email address")
  @Property()
  email: string;
}

Validate

The Validate decorator adds custom validation to a component:

typescript
import {Form, Validate} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Validate({
    required: true,
    minLength: 3,
    maxLength: 50
  })
  @Property()
  username: string;

  @Validate({
    required: true,
    pattern: "^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$"
  })
  @Property()
  email: string;
}

Combining Decorators

You can combine multiple decorators to customize your Form.io components:

typescript
import {Form, Select, DataSourceJson, Placeholder, Required, Tooltip} from "@tsed/schema-formio";
import {Property} from "@tsed/schema";

@Form()
export class User {
  @Property()
  @Required()
  @Tooltip("Enter your full name")
  @Placeholder("John Doe")
  name: string;

  @Select()
  @DataSourceJson([
    {value: "admin", label: "Administrator"},
    {value: "user", label: "Regular User"},
    {value: "guest", label: "Guest"}
  ])
  @Required()
  @Property()
  role: string;
}

Complete Example

Here's a more complete example showing how to use various decorators together:

typescript
import {Form, Tabs, InputTags, Password, Textarea, Select, TableView, Placeholder, Tooltip} from "@tsed/schema-formio";
import {Property, Required, Email, MinLength, MaxLength, CollectionOf, Enum} from "@tsed/schema";

export enum UserRole {
  ADMIN = "admin",
  USER = "user",
  GUEST = "guest"
}

@Form()
export class User {
  // Basic Information Tab
  @Tabs("Basic Information")
  @Required()
  @Property()
  firstName: string;

  @Tabs("Basic Information")
  @Required()
  @Property()
  lastName: string;

  @Tabs("Basic Information")
  @Select()
  @Enum(UserRole)
  @Required()
  @Property()
  role: UserRole;

  // Contact Information Tab
  @Tabs("Contact Information")
  @Required()
  @Email()
  @Placeholder("example@email.com")
  @Tooltip("Enter a valid email address")
  @Property()
  email: string;

  @Tabs("Contact Information")
  @Property()
  phone: string;

  @Tabs("Contact Information")
  @Textarea()
  @Property()
  address: string;

  // Security Tab
  @Tabs("Security")
  @Password()
  @Required()
  @MinLength(8)
  @MaxLength(50)
  @Placeholder("Enter a strong password")
  @Property()
  password: string;

  // Preferences Tab
  @Tabs("Preferences")
  @InputTags()
  @CollectionOf(String)
  @TableView(true)
  @Property()
  interests: string[];

  @Tabs("Preferences")
  @Textarea()
  @Property()
  bio: string;
}

// Generate the Form.io schema
const formioSchema = await getFormioSchema(User);

This example creates a form with four tabs (Basic Information, Contact Information, Security, and Preferences) and various field types with different validations and customizations as follows:

formio with tsed

Conclusion

The @tsed/schema-formio package provides a powerful way to generate Form.io schemas from TypeScript classes using decorators. By combining decorators from both @tsed/schema and @tsed/schema-formio, you can create complex forms with various field types, validations, and customizations.

It becomes easy to maintain your forms alongside your TypeScript models, ensuring type safety and consistency across Ts.ED applications.

Released under the MIT License.