cr-dynamic-forms

A angular library that provides a formular generator configured by a json document. cr-dynamic-forms is an easy way to create complex forms for complex data, without any knowladge about angular material and angular reactive forms. With cr-dynamic-forms you have the focus on the aspects of the data structure and not on "how to" create a complex form with angular.

Usage no npm install needed!

<script type="module">
  import crDynamicForms from 'https://cdn.skypack.dev/cr-dynamic-forms';
</script>

README

CrDynamicForms

A angular library that provides a formular generator configured by a json document.

cr-dynamic-forms is an easy way to create complex forms for complex data, without any knowladge about angular material and angular reactive forms.
With cr-dynamic-forms you have the focus on the aspects of the data structure and not on "how to" create a complex form with angular.

Installation

Installation is done using npm install command:

# install library
$ npm install --save cr-dynamic-forms

# install depency
$ ng add @angular/material

Example

http://cr-dynamic-components.creaera.com/forms

Features

  • input elements
    • text
    • textarea
    • select
    • chiplist
    • toggle
    • datetime
    • slider
  • structure elements
    • tabgroups and tabs
    • fieldsets
    • groups
    • cols

Quick Start

See an example on https://stackblitz.com/edit/angular-ivy-y9hh8d

app.module.ts

import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { FlexLayoutModule } from "@angular/flex-layout";
import { MatButtonModule } from "@angular/material/button";
import { AppComponent } from "./app.component";
import { CrDynamicFormsModule } from "cr-dynamic-forms";

@NgModule({
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    ReactiveFormsModule,
    FlexLayoutModule,
    CrDynamicFormsModule,
    MatButtonModule
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

CSS/SCSS

@import url(//fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap);
@import url(//fonts.googleapis.com/icon?family=Material+Icons);

HTML

<cr-dynamic-forms 
  #from
  [schema]="schema" 
  [data]="data" 
  [settings]="settings" 
  (onSubmit)="onSubmit($event)"
  (onNew)="onNew($event)"
  (onDelete)="onDelete($event)"
  (onChange)="onChange($event)"
  (onFocus)="onFocus($event)" 
  (onBlur)="onBlur($event)"
></cr-dynamic-forms>

TS

import { Component, VERSION } from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  /**
   * Get a reference of the html element
   */
  @ViewChild("form", { static: true }) form: CrDynamicFormsComponent;

  /**
   * Contains the data / form values;
   */
  public data: any = {};

  /**
   * Describes the data schema and the structure of the form
   */
  public schema = [
    {
      type: "text",
      label: "Textfield",
      name: "text_field"
    }
  ];

  /**
   * Contains optional global settings of the form.
   */
  public settings = {
    toolBarTop: {
      enable: false,
      enableNewButton: true,
      enableSaveButton: true,
      enableDeleteButton: true
    },
    toolBarBottom: {
      enable: true,
      enableNewButton: true,
      enableSaveButton: true,
      enableDeleteButton: true
    },
    text: {
      buttons: {
        new: "New",
        save: "Save",
        delete: "Delete"
      },
      errors: {
        required: "Please enter a value. This field is required.",
        mail: "Please enter a valid email adress.",
        length: "This value is to short."
      }
    },
    items: {
      child1: {
        children:[{          
        }, {
          a: {
            classes: ['red-test']
          }
        }]
      },      
      child2: {
        classes: ['blue-test']
      },
      child3: {
        classes: ['yellow-test']
      }
    }

  };

  /**
   * This method will be triggered, if the submit button would be clicked.
   *
   * @param data    contains the values
   */
  public onSubmit(data: any): void {
    console.log("onSubmit()");
    console.log(data);
  }

  /**
   * This method will be triggered, if the new button would be clicked.
   *
   * @param data    contains the current values before reset
   */
  public onNew(data: any): void {
    console.log("onSubmit()");
    console.log(data);

    this.data = {
      text_field: "Hello world!"
    };
  }

  /**
   * This method will be triggered, if the delete button would be clicked.
   *
   * @param data    contains the current values
   */
  public onDelete(data: any): void {
    console.log("onDelete()");
    console.log(data);
  }

  /**
   * This method will be triggered, if the data would be changed.
   *
   * @param data    contains the current values after a field was changed
   */
  public onChange(data: any): void {
    console.log("onChange()");
    console.log(data);
  }

  /**
   * This method will be triggered, if the cursor enters a field.
   *
   * @param event    { ctlName: string, path: string }
   */
  public onFocus(event: string) {
    console.log("onFocus()");
    console.log(event);
  }

  /**
   * This method will be triggered, if the cursor leaves a field.
   *
   * @param event    { ctlName: string, path: string }
   */
  public onBlur(event: string) {
    console.log("onBlur()");
    console.log(event);
  }
}

Input elements

See an example on https://stackblitz.com/edit/angular-ivy-swxysj

Text fields

  public schema = [
    {
      type: "text",
      label: "Textfield",
      name: "text_field",
      tooltip: "Tooltip"
    },
    {
      type: "text",
      label: "Textfield with autocomplete",
      name: "text_field_autocomplete",
      autocomplete: [
        { value: "tokio", label: "Tokio" },
        { value: "osaka", label: "Osaka" },
        { value: "himeji", label: "Himeji" }
      ]
    },
    {
      type: "text",
      label: "textfield read only",
      name: "text_field_read_only",
      tooltip: "Tooltip",
      autocomplete: [
        {
          group: "A",
          options: [
            {
              label: "Alabama",
              value: 0
            },
            {
              label: "Alaska",
              value: 1
            },
            {
              label: "Arizona",
              value: 2
            },
            {
              label: "Arkansas",
              value: 3
            }
          ]
        },
        {
          group: "C",
          options: [
            {
              label: "California",
              value: 4
            },
            {
              label: "Colorado",
              value: 5
            },
            {
              label: "Connecticut",
              value: 6
            }
          ]
        },
        {
          group: "D",
          options: [
            {
              label: "Delaware",
              value: 7
            }
          ]
        }
      ]
    },
    {
      type: "text",
      label: "Textfield read only",
      name: "text_field_read_only",
      rules: ["readonly"]
    },
    {
      type: "text",
      label: "Mandatory textfield",
      name: "mandatory_textfield",
      rules: ["required"]
    },
    {
      type: "text",
      label: "E-Mail",
      name: "email",
      rules: ["email"]
    },
    {
      type: "text",
      label: "Minimal string length value",
      name: "minimal_value",
      rules: [{ min: 4 }]
    },
    {
      type: "password",
      label: "Password",
      name: "password",
      rules: ["required"]
    },
    {
      type: "textarea",
      label: "textarea",
      name: "textarea",
      height: "100px"
    }
  ];

Select

See an example on https://stackblitz.com/edit/angular-ivy-8wxrdv

  public schema = [
    {
      type: "select",
      label: "select box",
      name: "select_box",
      filter: true
      options: [{ label: "no", value: "no" }, { label: "yes", value: "yes" }]
    },
    {
      type: "select",
      label: "Select",
      name: "select",
      options: [{
        value: "Value",
        label: "Label",
        icon: "format_color_fill",
      }],
      classes: ["color-a"]
    },
    {
      type: "select",
      label: "multi select box",
      name: "multiselect_box",
      multiple: true,
      options: [
        { label: "Alabama", value: "Alabama" },
        { label: "Alaska", value: "Alaska" },
        { label: "Arizona", value: "Arizona" }
      ]
    },
    {
      type: "select",
      label: "select box with groups",
      name: "select_box_with_groups",
      options: [
        {
          group: "A",
          options: [
            {
              label: "Alabama",
              value: 0
            },
            {
              label: "Alaska",
              value: 1
            },
            {
              label: "Arizona",
              value: 2
            },
            {
              label: "Arkansas",
              value: 3
            }
          ]
        },
        {
          group: "C",
          options: [
            {
              label: "California",
              value: 4
            },
            {
              label: "Colorado",
              value: 5
            },
            {
              label: "Connecticut",
              value: 6
            }
          ]
        },
        {
          group: "D",
          options: [
            {
              label: "Delaware",
              value: 7
            }
          ]
        }
      ]
    }
  ];

Other fields

See an example on https://stackblitz.com/edit/angular-ivy-evduv8

public schema = [
  {
    type: 'button',
    label: 'Click me!!!',
    name: 'clickme',
    style: 'flat',
    color: 'primary'
  },
  {
    type: "toggle",
    label: "Toggle",
    name: "toggle"
  },
  {
    type: "datetime",
    label: "Datetime",
    name: "datetime"
  },
  {
    type: "chiplist",
    label: "chiplist",
    name: "chiplist",
    autocomplete: [
      { value: "tokio", label: "Tokio" },
      { value: "osaka", label: "Osaka" },
      { value: "himeji", label: "Himeji" }
    ]
  },
  {
    type: "slider",
    label: "slider read only",
    name: "slider",
    options: {
      min: 0,
      max: 255,
      step: 1,
      thumbLabel: true
    }
  }
  ];

Structure Elements

See an example on https://stackblitz.com/edit/angular-ivy-damzce

  public schema = [
    {
      type: "fieldset",
      label: "fieldset headline with background color",
      classes: ["myClass"],
      options: { background: "#eeeeee", margin: "1em", padding: "1em" },
      children: [
        {
          type: "text",
          label: "Text A",
          name: "text_a"
        }
      ]
    },
    {
      type: "cols",
      children: [
        {
          type: "text",
          label: "Col 1",
          name: "col1"
        },
        {
          type: "text",
          label: "Col 2",
          name: "col2"
        }
      ]
    },
    {
      type: "group",
      label: "App",
      name: "apps",
      children: [
        {
          type: "text",
          label: "Text B",
          name: "text_b"
        }
      ]
    },
    {
      type: "tabgroup",
      children: [
        {
          type: "tab",
          label: "tab 1",
          children: [
            {
              type: "text",
              label: "Text C",
              name: "text_c"
            }
          ]
        },
        {
          type: "tab",
          label: "tab 2",
          children: [
            {
              type: "text",
              label: "Text D",
              name: "text_d"
            }
          ]
        }
      ]
    }
  ];

Public API

See an example on https://stackblitz.com/edit/angular-ivy-zr1mm8

  /**
   * Get a reference of the html element
   */
  @ViewChild("form", { static: true }) form: CrDynamicFormsComponent;

  /**
   * public api save
   */
  public save() {
    this.form.submit();
  }

  /**
   * public api reset
   */
  public new() {
    this.form.reset();
  }

  /**
   * public api reset
   */
  public action() {
    this.form.delete();
  }

  /**
   * public api setAutocomplete
   */
  public setAutocomplete() {
    /**
     * @param fieldName: string
     * @param options: any[ {value: string, label: string} ]
     */
    this.form.setAutocomplete("text_field", [
        { value: "München", label: "München" },
        { value: "Hamburg", label: "Hamburg" },
        { value: "Nürnberg", label: "Nürnberg" }
      ]);
    };    
  }

  /**
   * public api getData 
   */
  public getData() : void {
    console.log(this.form.getData());
  }

Changelog

  • Version 1.30.0 / 1.30.1, 1.30.2
  • Version 1.29.0
    • Update table height
  • Version 1.28.0 / 1.28.1 / 1.28.2
    • Fixed bug in selectdialog
    • Add filter to select
    • Add control elements type table
  • Version 1.27.1 / 1.27.2
    • Fixed bug in selectdialog
  • Version 1.27.0
    • Add public getValue (path: string) : any to public api
  • Version 1.26.0
    • Bug Fixes with layout problems
    • Add prefix to text and textarea
    • Add type button
  • Version 1.25.0
    • Add setValueByPath (path: string, value: any) : void to public api
  • Version 1.24.0
    • Add group support to selectdialog
  • Version 1.23.1
    • Bug Fixes css
  • Version 1.23.0
    • Upgrade to angular 12.2.0
  • Version 1.22.0
    • Add selectdialog
  • Version 1.21.1
    • Bug Fixes in cols - vertical align top
  • Version 1.21.0
    • Add classes to type select
    • Add sortable to type chiplist
  • Version 1.20.0 / 1.20.1
    • Add classes settings to fieldset
    • Add surrounding div around fieldset children
  • Version 1.19.9
    • Bug Fixes in textarea
  • Version 1.19.7 / 1.19.8
    • Bug Fixes in table
  • Version 1.19.6
    • Remove Logs
  • Version 1.19.4 / 1.19.5
    • Support of custom css classes for columns
  • Version 1.19.1 / 1.19.2 / 1.19.3
    • Bug Fixes
  • Version 1.19.0
    • custom css classes for specific fields
  • Version 1.18.0
    • add type table
  • Version 1.17.0
    • add public api getData()
  • Version 1.16.1
    • Select does now support the readonly rule
  • Version 1.16.0
    • Add options.margin and options.padding to fieldset
  • Version 1.15.8 / 1.15.9 / 1.15.10 / 1.15.11 / 1.15.12
    • Refactoring
  • Version 1.15.7
    • Changed property of select box options name to label
  • Version 1.15.6
    • Refactoring
  • Version 1.15.5
    • Fixed bug that prevents to set the values for a multiple selectbox in groups
  • Version 1.15.4
    • Fixed bug that prevents to set the values for a multiple selectbox
  • Version 1.15.3
    • Add disabled-form-field class to text field
  • Version 1.15.2
    • Rewrite documentation
    • Add examples on stackblitz.com
    • Fixed bug that prevents icons from being displayed correctly in select boxes
  • Version 1.15.1
    • Fixed bug with the onFocus event in fieldset
  • Version 1.15.0
    • onBlur and onFocus does now return an object e.g. {ctlName: "text", path: "/apps/0/text"}instead of only the ctlName
  • Version 1.14.1
    • Add Logo
    • Fixed bug with the onFocus event in nested elements
  • Version 1.14.0
    • It's now possible to use icons in a select box
    • It's now possible to use a multiple select box
    • Update documentation
  • Version 1.13.1
    • Add Changelog
    • Fixed bug in type group it's now possible to clone a group with chiplists
    • Update documentation

License

MIT