Angular Dynamic Form Generator

Richard Knight 02d14f5030 Better comment 4 years ago
_notes 1d1b90ec92 Adding metadata generation utilities 6 years ago
e2e 4fe4114fe8 chore: initial commit from @angular/cli 6 years ago
src 02d14f5030 Better comment 4 years ago
.editorconfig 4fe4114fe8 chore: initial commit from @angular/cli 6 years ago
.gitignore 4fe4114fe8 chore: initial commit from @angular/cli 6 years ago
README.md ec1959367e Bumping dependencies to latest Angular 8 compatible versions 4 years ago
angular.json d5ae6eec55 Clarity icons now working 5 years ago
browserslist 8a34f783f2 Angular 8 upgrade 5 years ago
karma.conf.js 1e1f528f58 Upgrade to Angular 6 6 years ago
ng-dynaform.code-workspace ff113f0631 Changing to OnPush change detection for greater efficiency 5 years ago
package-lock.json 02d14f5030 Better comment 4 years ago
package.json ec1959367e Bumping dependencies to latest Angular 8 compatible versions 4 years ago
protractor.conf.js 4fe4114fe8 chore: initial commit from @angular/cli 6 years ago
tsconfig.json ff113f0631 Changing to OnPush change detection for greater efficiency 5 years ago
tslint.json 8a34f783f2 Angular 8 upgrade 5 years ago
yarn.lock 1e1f528f58 Upgrade to Angular 6 6 years ago

README.md

NgDynaform

GearWithWrench

A Dynamic Form Builder for Angular, with built in model mapper.

  • Define forms with metadata, or build directly from models

  • Combine metadata with models to populate form values

  • Group related fields in containers, to any level of nesting

  • Use repeating containers to model complex arrays

  • Take advantage of terse metadata, and sensible defaults

Basic Usage

Import the Dynaform module.

import { NgModule } from '@angular/core';

...

import { DynaformModule } from './dynaform/dynaform.module';
...

@NgModule({
	imports: [
		...
		DynaformModule
		...
	]
    ...
})
export class AppModule { }

In any component where you want to generate a form, import andthen inject the DynaformService.

import { Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { DynaformService } from './dynaform'; // Paths may vary

@Component({
	selector: 'app-root',
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

	form: FormGroup;
	meta: StringMap<any>;

    constructor(private dynaform: DynaformService) {
    }


Build the form from your model, your metadata, or a combination of the two.

ngOnInit() {

	const model = { firstName: 'Olly', lastName: 'October', country: 'France' };
    const meta = {
        country: { type: 'select', options: ['France', 'Germany', 'Norway', 'Sweden'] }
    }
    
    // Build FormGroup and Modeled Metadata
	const { form, modeledMeta } = this.dynaform.build(model, meta);

}

Build strategies

Dynaform supports two build strategies: MODELFISRT and METAFIRST (the default)

MODELFIRST generates a form from the supplied model, and by default will ignore any metadata points that don't occur in the model.

METAFIRST generates a form from the supplied metadata, and by default will ignore any data in the model that doesn't have a corresponding metadata entry.

You can set the build strategy by calling setBuildStrategy

this.dynaform.setBuildStrategy('MODELFIRST');

The behaviour of the MODELFIRST build strategy can be further modified by supplying true as the third parameter to dynaform.build, where true means "create extra fields from metadata, even when they don't occur ion the model". It's useful when the model might be incomplete.

// Build FormGroup and Modeled Metadata, creating fields for any points mentioned in 
// the metadata that don't occur in the model
const { form, modeledMeta } = this.dynaform.build(model, meta);

Callbacks

  • Callbacks allow events inside a form to trigger functions in the host component.
  • Callbacks can be attached to buttons, and to the change handler of any field.
  • Callbacks are identified by a string, which maps them to a function in the host component.
// Register callbacks (probably in ngOnInit)
this.dynaform.register({
	'SAYHELLO': this.sayHello,
	'SAYCHEESE': this.sayCheese
}, this);

Then later in the component...

handleCallback(fnId) {
	this.dynaform.call(fnId);
}

sayHello() {
	alert('HELLO');
}

sayCheese() {
	alert('CHEESE');
}

Field metadata

Common properties

All fields support the following metadata. The missing properties will be 'filled oin' by dynaform's field-specific models, allowing forms to specifiued tersely.

Property Description Default / Notes
name The FormControl name Do not set.
Derived automatically from the key of the metadata object.
type The control's type Text
label The controls's label An unCamelCased and spaced version of name
value The control's initial value Empty string. May be set from model, overriding the default
checkedValue Value when checked* Checkboxes / Checkbuttons only
default The control's default value
placeholder Optional placeholder text
class CSS class(es) to apply Single class (string) or array of classes (string[])
id CSS id to apply
disabled Whether field is disbled false
change Name of function in host component to call when value changes
source Location of data in model Defaults to the same name and path. Used by model-mapper.
before Ordering instruction - move before name of another control in group
after Ordering instruction - move after name of another control in group
validators Array of validator functions - following Angular FormControl API []
asyncValidators Array of async validator functions - following Angular FormControl API []
valFailureMsgs Validation failure messages - used to display appropriate message if validation fails

Available types

  • Text
  • Textarea
  • Select
  • Radio
  • Checkbox
  • Checkbutton
  • Password
  • Hidden
  • Checkbox
  • CheckboxGroup
  • Datepicker
  • Timepicker
  • DropdownModifiedInput
  • Multiline

'Option' field metadata

(for selects, radios, checkbutton groups, etc)

Options can be specified as an array of values, an array of [value, label] pairs, or an array of objects with the structure { value: 'VAL', label: 'My label' }. If you supply a a simple array of values, each value is used as both the value and label.

Examples

{
	firstName: {},
	lastName: {},
	telephoneNumber: {},
	country: { type: 'select', options: ['France', 'Germany', 'Italy', 'Norway'] }
}
{
	cheeseLover: { type: 'checkbox', label: 'Do you like cheese?' },
	favouriteCheese: {
		type: 'radio',
		label: 'What`s your favourite?',
		options: [
			['CHED', 'Cheddar'],
			['WENS', 'Wensleydale'],
			['BRIE', 'Brie'],
			['GORG', 'Gorgonzola']
		]
	}
}

Field-type specific metadata

TO ADD.

e.g. radio, horizontal.

Containers

Related fields can be grouped using containers. A container has it's own meta property, under which the fields it contains are defined in the usual way. Containers can even contain other containers, with no hard limit on depth, although in practice you'll probably not need to use deeply nested metadata.

cheeseMakersContract and cheeseMakersAddress are both containers...

{
	optIn: { label: 'Become a cheese maker?', type: 'checkbutton' },
	optInConfirm: { label: 'Really?', type: 'radio', options: ['Yes', 'No', 'Maybe'] },
	cheeseMakersContract: {
		label: 'Contract details',
		meta: {
			type: { label: 'Contract type' },
			startDate: { type: 'datepicker' }
      cheeseMakersAddress: {
        label: 'Address'
        meta: {
          address1: {},
          address2: {},
          city: {},
          postcode: { class: 'short-field' }
        }
      }
		}
	}
}

Container options

Property Description Default / Notes
name The FormGroup name Do not set.
Derived automatically from the key of the metadata object.
label Optional container label Creates a heading above the container
seed An object containing key-value pairs, used to seed metadata for all contained fields Use when all or most fields share certain properties, e.g. to apply the same class or default
template Optional template override This should be an Angular TemplateRef
See https://angular.io/api/core/TemplateRef

Seeding containers with shared options

TO ADD.

Repeating Containers

TO ADD.

Buttons

TO ADD.

Presentational types

TO ADD.

The built-in model mapper

TO ADD.

Programatically updating displayed form values

TO ADD.

Validation

TO ADD.