Receber por email
← Voltar para o catálogo

delon-form-dynamic-schema-forms

Create dynamic schema-based forms using @delon/form (SF component). Use this skill when building complex forms with validation, conditional rendering, async data loading, custom widgets, and multi-step workflows. Ensures forms follow JSON Schema standards, integrate with Angular reactive forms, support internationalization, and maintain consistent validation patterns across the application.

367estrelas
Atualizado há 4 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add aiskillstore/marketplace

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/delon-form-dynamic-schema-forms/svg)](https://www.skillteca.com.br/skills/delon-form-dynamic-schema-forms)

Alerta por categoria

Receba novas skills de Design e Frontend toda segunda

1 email curto, só com as skills novas de Design e Frontend. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

@delon/form Dynamic Schema Forms Skill

This skill helps create dynamic forms using @delon/form's SF (Schema Form) component.

Core Principles

Schema-Driven Forms

  • JSON Schema: Define forms declaratively with JSON Schema
  • Type Safety: Full TypeScript support for schema definitions
  • Validation: Built-in validation with custom validators
  • Dynamic Rendering: Conditional fields based on form state

Key Features

  • Automatic form generation from schema
  • Custom widgets for specialized inputs
  • Async data loading (dropdowns, autocomplete)
  • Multi-step forms (wizards)
  • Responsive grid layouts
  • Internationalization support

Basic Schema Form

import { Component, signal, output } from '@angular/core';
import { SFSchema, SFComponent, SFUISchema } from '@delon/form';
import { SHARED_IMPORTS } from '@shared';

@Component({
  selector: 'app-user-form',
  standalone: true,
  imports: [SHARED_IMPORTS, SFComponent],
  template: `
    <sf 
      [schema]="schema"
      [ui]="ui"
      [formData]="initialData()"
      [loading]="loading()"
      (formSubmit)="handleSubmit($event)"
      (formChange)="handleChange($event)"
      (formError)="handleError($event)"
    />
  `
})
export class UserFormComponent {
  loading = signal(false);
  initialData = signal<any>({});
  formSubmit = output<any>();
  
  schema: SFSchema = {
    properties: {
      name: {
        type: 'string',
        title: 'Full Name',
        maxLength: 100
      },
      email: {
        type: 'string',
        title: 'Email',
        format: 'email'
      },
      age: {
        type: 'number',
        title: 'Age',
        minimum: 18,
        maximum: 120
      },
      role: {
        type: 'string',
        title: 'Role',
        enum: ['admin', 'member', 'viewer'],
        default: 'member'
      }
    },
    required: ['name', 'email', 'role']
  };
  
  ui: SFUISchema = {
    '*': {
      spanLabel: 6,
      spanControl: 18,
      grid: { span: 24 }
    },
    $name: {
      placeholder: 'Enter full name',
      widget: 'string'
    },
    $email: {
      placeholder: 'user@example.com',
      widget: 'string'
    },
    $age: {
      widget: 'number'
    },
    $role: {
      widget: 'select',
      placeholder: 'Select role'
    }
  };
  
  handleSubmit(value: any): void {
    console.log('Form submitted:', value);
    this.formSubmit.emit(value);
  }
  
  handleChange(value: any): void {
    console.log('Form changed:', value);
  }
  
  handleError(errors: any): void {
    console.error('Form errors:', errors);
  }
}

Common Widgets

String Input

{
  name: {
    type: 'string',
    title: 'Name',
    ui: {
      widget: 'string',
      placeholder: 'Enter name',
      prefix: 'User',
      suffix: '@',
      maxLength: 100
    }
  }
}

Textarea

{
  description: {
    type: 'string',
    title: 'Description',
    ui: {
      widget: 'textarea',
      autosize: { minRows: 3, maxRows: 6 },
      placeholder: 'Enter description'
    }
  }
}

Number Input

{
  amount: {
    type: 'number',
    title: 'Amount',
    minimum: 0,
    maximum: 1000000,
    ui: {
      widget: 'number',
      precision: 2,
      prefix: '$',
      formatter: (value: number) => `$ ${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
    }
  }
}

Date Picker

{
  birthDate: {
    type: 'string',
    title: 'Birth Date',
    format: 'date',
    ui: {
      widget: 'date',
      mode: 'date',
      displayFormat: 'yyyy-MM-dd',
      end: 'today' // Can't select future dates
    }
  }
}

Date Range

{
  dateRange: {
    type: 'string',
    title: 'Date Range',
    ui: {
      widget: 'date',
      mode: 'range',
      displayFormat: 'yyyy-MM-dd'
    }
  }
}

Select Dropdown

{
  category: {
    type: 'string',
    title: 'Category',
    enum: [
      { label: 'Technology', value: 'tech' },
      { label: 'Business', value: 'business' },
      { label: 'Science', value: 'science' }
    ],
    ui: {
      widget: 'select',
      placeholder: 'Select category',
      allowClear: true,
      showSearch: true
    }
  }
}

Multi-Select

{
  tags: {
    type: 'array',
    title: 'Tags',
    items: {
      type: 'string',
      enum: ['angular', 'react', 'vue', 'typescript']
    },
    ui: {
      widget: 'select',
      mode: 'multiple',
      placeholder: 'Select tags'
    }
  }
}

Autocomplete

{
  city: {
    type: 'string',
    title: 'City',
    ui: {
      widget: 'autocomplete',
      asyncData: () => this.loadCities(),
      debounceTime: 300,
      placeholder: 'Search city'
    }
  }
}

private async loadCities(): Promise<any[]> {
  return [
    { label: 'New York', value: 'ny' },
    { label: 'Los Angeles', value: 'la' },
    { label: 'Chicago', value: 'chi' }
  ];
}

Radio Buttons

{
  priority: {
    type: 'string',
    title: 'Priority',
    enum: [
      { label: 'Low', value: 'low' },
      { label: 'Medium', value: 'medium' },
      { label: 'High', value: 'high' }
    ],
    default: 'medium',
    ui: {
      widget: 'radio',
      styleType: 'button' // or 'default'
    }
  }
}

Checkbox

{
  agree: {
    type: 'boolean',
    title: 'Agree to terms',
    ui: {
      widget: 'checkbox'
    }
  }
}

Switch

{
  isActive: {
    type: 'boolean',
    title: 'Active Status',
    ui: {
      widget: 'switch',
      checkedChildren: 'On',
      unCheckedChildren: 'Off'
    }
  }
}

Slider

{
  rating: {
    type: 'number',
    title: 'Rating',
    minimum: 0,
    maximum: 100,
    ui: {
      widget: 'slider',
      marks: {
        0: '0',
        50: '50',
        100: '100'
      }
    }
  }
}

File Upload

{
  avatar: {
    type: 'string',
    title: 'Avatar',
    ui: {
      widget: 'upload',
      action: '/api/upload',
      accept: 'image/*',
      limit: 1,
      listType: 'picture-card'
    }
  }
}

Async Data Loading

Dynamic Dropdown Options

{
  assignee: {
    type: 'string',
    title: 'Assignee',
    ui: {
      widget: 'select',
      asyncData: () => this.loadUsers(),
      placeholder: 'Select user'
    }
  }
}

private async loadUsers(): Promise<any[]> {
  const users = await this.userService.getUsers();
  return users.map(u => ({
    label: u.name,
    value: u.id
  }));
}

Cascading Selects

{
  country: {
    type: 'string',
    title: 'Country',
    ui: {
      widget: 'select',
      asyncData: () => this.loadCountries(),
      change: (value: string) => this.onCountryChange(value)
    }
  },
  city: {
    type: 'string',
    title: 'City',
    ui: {
      widget: 'select',
      asyncData: () => this.loadCities(this.selectedCountry())
    }
  }
}

private selectedCountry = signal<string>('');

onCountryChange(value: string): void {
  this.selectedCountry.set(value);
}

Conditional Fields

Show/Hide Based on Value

schema: SFSchema = {
  properties: {
    userType: {
      type: 'string',
      title: 'User Type',
      enum: ['individual', 'company']
    },
    // Show only for companies
    companyName: {
      type: 'string',
      title: 'Company Name',
      ui: {
        visibleIf: {
          userType: ['company']
        }
      }
    },
    // Show only for individuals
    firstName: {
      type: 'string',
      title: 'First Name',
      ui: {
        visibleIf: {
          userType: ['individual']
        }
      }
    }
  }
};

Custom Validators

import { SFSchema } from '@delon/form';

schema: SFSchema = {
  properties: {
    password: {
      type: 'string',
      title: 'Password',
      ui: {
        type: 'password',
        validator: (value: string, formProperty: any, form: any) => {
          if (value.length < 8) {
            return [{ keyword: 'minLength', mes
Compartilhar X LinkedIn