Overview
Nodes are the individual form fields and components that make up your dynamic form. Each node represents a specific input type (text input, dropdown, checkbox, etc.) and contains configuration for its appearance, behavior, and validation.
Node Base Class
All nodes inherit from the NodeBase class which provides common properties:
Unique identifier for the node. Used to access form values and controls.
Placeholder text displayed in the input field
The type of node: 'input', 'checkbox', 'dropdown', 'button', 'date', 'radiogroup', 'textarea', 'file', 'password', 'number', 'switch', 'custom', 'autocomplete', 'datetime', 'daterange', 'timepicker'
Event handlers for the node - see Actions
Whether the node takes up a full row in the form grid
Material icon name to display alongside the field
Custom error message to display when validation fails
Whether the field is disabled
validator
ValidatorFn | ValidatorFn[]
Asynchronous validators for server-side validation
Helper text displayed below the field
Whether the field receives focus on form load
Available Node Types
Standard text input field.
import { Input } from 'mat-dynamic-form';
import { Validators } from '@angular/forms';
const nameInput = new Input(
'name', // id
'Full Name', // placeholder
'', // initial value
100 // maxCharCount (optional)
).apply({
icon: 'person',
hint: 'Enter your full name',
validator: Validators.required,
singleLine: true
});
Additional Properties:
value: Initial/current value
maxCharCount: Maximum character limit
minCharCount: Minimum character count
readOnly: Whether the input is read-only
Methods:
editable(): Makes the input editable
readonly(): Makes the input read-only
Password input field with masked characters.
import { InputPassword } from 'mat-dynamic-form';
import { Validators } from '@angular/forms';
const passwordInput = new InputPassword(
'password',
'Password'
).apply({
validator: [Validators.required, Validators.minLength(8)],
icon: 'lock',
hint: 'Minimum 8 characters'
});
Numeric input with optional min/max constraints.
import { InputNumber } from 'mat-dynamic-form';
const ageInput = new InputNumber(
'age',
'Age',
null
).apply({
min: 18,
max: 120,
icon: 'calendar_today'
});
Additional Properties:
min: Minimum allowed value
max: Maximum allowed value
decimalCount: Number of decimal places
TextArea
Multi-line text input.
import { TextArea } from 'mat-dynamic-form';
const commentsArea = new TextArea(
'comments',
'Comments',
'',
500 // maxCharCount
).apply({
singleLine: true,
hint: 'Share your thoughts'
});
Checkbox
Boolean checkbox input.
import { Checkbox } from 'mat-dynamic-form';
const agreeCheckbox = new Checkbox(
'agreement',
'I agree to the terms and conditions',
false // initial value
).apply({
validator: Validators.requiredTrue
});
Switch
Toggle switch (alternative to checkbox).
import { Switch } from 'mat-dynamic-form';
const notificationSwitch = new Switch(
'notifications',
'Enable notifications',
true
).apply({
hint: 'Receive email updates'
});
Dropdown
Single or multi-select dropdown.
import { Dropdown, OptionChild } from 'mat-dynamic-form';
const countryDropdown = new Dropdown(
'country',
'Country',
[
new OptionChild('United States', 'us'),
new OptionChild('United Kingdom', 'uk'),
new OptionChild('Canada', 'ca')
],
'us', // selectedValue
false // multiple selection
).apply({
icon: 'public',
hint: 'Select your country'
});
Additional Properties:
selectedValue: Initially selected value
multiple: Enable multi-select mode
Loading Options Dynamically:
// From Promise
const dropdown1 = new Dropdown(
'country',
'Country',
fetch('/api/countries').then(r => r.json())
);
// From Observable
import { Observable } from 'rxjs';
const dropdown2 = new Dropdown(
'country',
'Country',
this.http.get<OptionChild[]>('/api/countries')
);
RadioGroup
Mutually exclusive radio button group.
import { RadioGroup, OptionChild } from 'mat-dynamic-form';
const genderRadio = new RadioGroup(
'gender',
'Gender',
[
new OptionChild('Male', 'm'),
new OptionChild('Female', 'f'),
new OptionChild('Other', 'o')
],
'm' // selected value
).apply({
hint: 'Select your gender'
});
AutoComplete
Searchable dropdown with filtering.
import { AutoComplete, OptionChild } from 'mat-dynamic-form';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
const countryAutocomplete = new AutoComplete(
'country',
'Country',
this.http.get<any[]>('https://restcountries.com/v3.1/all').pipe(
map(countries => countries.map(c =>
new OptionChild(c.name.common, c.cca2)
))
),
'us', // selectedValue
false // multiple
).apply({
hint: 'Type to search countries',
icon: 'search'
});
AutoComplete automatically filters options as the user types, matching against both title and value fields.
DatePicker
Date selection input.
import { DatePicker } from 'mat-dynamic-form';
const birthdateInput = new DatePicker(
'birthdate',
'Date of Birth',
null // initial value
).apply({
minDate: new Date(1900, 0, 1),
maxDate: new Date(),
icon: 'calendar_today'
});
Additional Properties:
value: Selected date
minDate: Minimum selectable date
maxDate: Maximum selectable date
DateTimePicker
Date and time selection input.
import { DateTimePicker } from 'mat-dynamic-form';
const appointmentInput = new DateTimePicker(
'appointment',
'Appointment',
new Date(),
'dd/MM/yyyy hh:mm a' // dateFormat
).apply({
minDate: new Date(),
maxDate: new Date(new Date().setMonth(new Date().getMonth() + 3)),
orientation: 'portrait',
showClockPicker: true,
acceptLabel: 'Confirm',
cancelLabel: 'Cancel'
});
Additional Properties:
dateFormat: Display format (e.g., 'dd-MM-yyyy hh:mm a')
acceptLabel: Label for confirm button
cancelLabel: Label for cancel button
selectTimeLabel: Label for time selection
enterTimeLabel: Label for manual time entry
showClockPicker: Show visual clock picker
orientation: 'portrait' or 'landscape'
TimePicker
Time-only selection input.
import { TimePicker } from 'mat-dynamic-form';
const timeInput = new TimePicker(
'meetingTime',
'Meeting Time',
new Date(),
'hh:mm a'
).apply({
orientation: 'portrait',
showClockPicker: true
});
DateRangePicker
Select a start and end date range.
import { DateRangePicker } from 'mat-dynamic-form';
const dateRangeInput = new DateRangePicker(
'vacationDates',
'Vacation Dates'
).apply({
startDate: '2024-01-01',
endDate: '2024-01-07',
minDate: new Date(),
maxDate: new Date(new Date().setFullYear(new Date().getFullYear() + 1))
});
DateRangePicker creates a FormGroup with start and end controls internally.
File upload input with drag-and-drop support.
import { InputFile } from 'mat-dynamic-form';
const profilePicInput = new InputFile(
'profilePic',
'Profile Picture'
).apply({
accept: ['png', 'jpg', 'jpeg'],
maxSize: 5000, // KB
filename: 'profile.png',
dragLabel: 'Drag & Drop your file here',
downloadHint: 'Download file',
removeHint: 'Remove file',
retryHint: 'Retry upload',
onStatusChange: (change: FileChange) => {
console.log('File status:', change);
}
});
Additional Properties:
accept: Array of allowed file extensions
filename: Default filename
maxSize: Maximum file size in KB
dragLabel: Text shown in drag-drop area
downloadHint: Tooltip for download button
removeHint: Tooltip for remove button
retryHint: Tooltip for retry button
onStatusChange: Callback when file status changes
Action button (typically used for validateActions).
import { Button } from 'mat-dynamic-form';
const submitButton = new Button(
'submit',
'Submit',
{
style: 'primary',
onEvent: (param) => {
if (param.structure.isValid()) {
const data = param.structure.getValue();
console.log('Form data:', data);
}
}
}
).apply({
icon: 'check',
validateForm: true,
validation: (param) => {
// Custom validation logic
return param.structure.getControlById('email')?.value?.length > 0;
}
});
Additional Properties:
validateForm: Whether to validate the form before executing the action
validation: Custom validation function that returns boolean
CustomNode
Embed custom Angular components in your form.
import { CustomNode } from 'mat-dynamic-form';
import { MyCustomComponent } from './my-custom.component';
const customField = new CustomNode<MyCustomComponent>(
'customField',
MyCustomComponent,
{
// Properties passed to your component
label: 'Custom Field',
placeholder: 'Enter value',
config: { theme: 'dark' }
}
);
Additional Properties:
component: The Angular component class
instance: Reference to the component instance after creation
properties: Object of properties passed to the component
Your custom component will receive the properties object as @Input() bindings.
Working with Nodes
Using apply() Method
All nodes support the apply() method for fluent configuration:
const input = new Input('email', 'Email Address')
.apply({
icon: 'email',
validator: [Validators.required, Validators.email],
hint: 'We will never share your email',
singleLine: true,
autoFocus: true
});
Accessing Native Elements
const element = node.getNativeElement();
if (element) {
element.focus();
element.scrollIntoView();
}
Getting Node Options
For selectable nodes (Dropdown, RadioGroup, AutoComplete):
const dropdown = formStructure.getNodeById<Dropdown>('country');
const options = dropdown.getOptions();
console.log(options); // Array of OptionChild
Complete Example
import { Component } from '@angular/core';
import { Validators } from '@angular/forms';
import {
FormStructure,
Input,
InputPassword,
InputNumber,
Checkbox,
Dropdown,
RadioGroup,
DatePicker,
TextArea,
Button,
OptionChild
} from 'mat-dynamic-form';
@Component({
selector: 'app-registration',
template: '<mat-dynamic-form [structure]="formStructure"></mat-dynamic-form>'
})
export class RegistrationComponent {
formStructure: FormStructure;
constructor() {
this.formStructure = new FormStructure('User Registration');
this.formStructure.nodeGrid = 2;
this.formStructure.appearance = 'outline';
this.formStructure.nodes = [
new Input('firstName', 'First Name').apply({
icon: 'person',
validator: Validators.required,
autoFocus: true
}),
new Input('lastName', 'Last Name').apply({
validator: Validators.required
}),
new Input('email', 'Email').apply({
icon: 'email',
validator: [Validators.required, Validators.email],
singleLine: true
}),
new InputPassword('password', 'Password').apply({
icon: 'lock',
validator: [Validators.required, Validators.minLength(8)],
hint: 'At least 8 characters'
}),
new InputNumber('age', 'Age').apply({
min: 18,
max: 120,
validator: Validators.required
}),
new Dropdown('country', 'Country', [
new OptionChild('United States', 'us'),
new OptionChild('United Kingdom', 'uk'),
new OptionChild('Canada', 'ca')
]).apply({
icon: 'public',
validator: Validators.required
}),
new RadioGroup('gender', 'Gender', [
new OptionChild('Male', 'm'),
new OptionChild('Female', 'f'),
new OptionChild('Other', 'o')
]),
new DatePicker('birthdate', 'Date of Birth').apply({
maxDate: new Date(),
validator: Validators.required
}),
new TextArea('bio', 'Bio', '', 500).apply({
singleLine: true,
hint: 'Tell us about yourself'
}),
new Checkbox(
'terms',
'I accept the terms and conditions',
false
).apply({
validator: Validators.requiredTrue,
singleLine: true
})
];
this.formStructure.validateActions = [
new Button('cancel', 'Cancel', {
style: 'warn',
onEvent: (param) => param.structure.reset()
}).apply({ icon: 'close' }),
new Button('submit', 'Register', {
style: 'primary',
onEvent: (param) => this.handleSubmit(param.structure)
}).apply({
icon: 'check',
validateForm: true
})
];
}
handleSubmit(structure: FormStructure) {
const data = structure.getValue();
console.log('Registration data:', data);
// Submit to API
}
}