npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@3kles/kles-material-dynamicforms

v22.6.1

Published

<!--[![pipeline status](http://gitlab.3kles.local/angular/klesmaterialdynamicforms/badges/master/pipeline.svg)](http://gitlab.3kles.local/angular/klesmaterialdynamicforms/-/commits/master)-->

Readme

@3kles/kles-material-dynamicforms

kles-material-dynamicforms is a component library to build Material Angular Form.

Changelog

Check out the changelog to check all the latest changes.

Models

Advanced CSS Grid layout

Add layout to an individual root field to enable CSS Grid automatically. direction="grid" remains available to force it when no field declares a layout. The defaults are a 12-column grid, no gap, colSpan: 12, and rowSpan: 1.

const fields: KlesFormElement[] = [
  { name: 'firstname', component: KlesFormInputComponent, layout: { colSpan: 6, responsive: { xs: { colSpan: 12 } } } },
  { name: 'lastname', component: KlesFormInputComponent, layout: { colSpan: 6, responsive: { xs: { colSpan: 12 } } } },
  { name: 'email', component: KlesFormInputComponent, layout: { colSpan: 12 } },
];

Configure the root with [layout]="{ columns: 12, gap: '16px' }". Structural section and layoutGroup elements can contain nested fields and establish independent grids through layoutConfig; they do not create Angular form controls. layoutGroup is deliberately distinct from the existing data-bearing EnumType.group. The legacy row, column, wrap, and EnumType.group APIs are unchanged.

Collapsible sections

Set collapsible: true on a section to render a Material expansion panel. It starts open by default; use expanded: false to start closed. Sections without collapsible keep their existing appearance. Panels open and close independently, including nested sections. Collapsing keeps fields mounted, so their values, validation, and subscriptions remain active.

const fields: KlesFormElement[] = [
  {
    type: 'section',
    title: 'Additional information',
    description: 'Optional details',
    icon: 'info',
    collapsible: true,
    expanded: false,
    fields: [{ name: 'notes', component: KlesFormInputComponent }],
  },
];

Use a ViewChild reference to open or close every collapsible section, including nested sections, after the view is initialized:

@ViewChild(KlesDynamicFormComponent) dynamicForm!: KlesDynamicFormComponent;

openAll(): void {
  this.dynamicForm.expandAllSections();
}

closeAll(): void {
  this.dynamicForm.collapseAllSections();
}

Updating field configurations

Assign a new fields array to synchronize the root form, including fields inside nested dynamicFields, section and layoutGroup containers. Keep the same configuration object for an unchanged field; replace the object when its component configuration changes. Visual containers retain their identity when the same container object is reused.

Compatible fields retain their control, current value, dirty/touched flags, and UI state. Compatibility requires the same resolved component and control factory, updateOn, and nonNullable. An incompatible field gets a new control with its declared initial value. Changing value on a compatible field does not overwrite user input; use the control's setValue or patchValue to do so.

Validator changes and changes to the configured disabled flag are applied to existing controls. An unrelated update preserves programmatic enabling or disabling. UI defaults that change are patched into the existing leaf state; unrelated runtime UI adjustments are preserved. Unchanged mounted fields keep their components, focus, option subscriptions, and valueChanges callbacks. Their injected sibling list reflects additions and removals without a rebuild.

Removed fields lose their controls and UI states. Names must be unique across all visual containers sharing the root form; duplicates are rejected before changing controls. Structural updates emit the final form value after synchronization, without intermediate structural values. Removed or replaced controls are detached from their old parent. _onLoaded still emits after a new fields input has been synchronized.

Recursive removal and schema updates inside data-bearing EnumType.group and arrays are outside this stage. Their existing behavior is retained.

Dependent child fields: API contract

Dependency execution and automatic child synchronization are active. dependsOn and resolveFields are optional properties on dynamicFields, section and layoutGroup. The component observes dependencies, invokes the resolver and exposes its results through the fieldResolutions Signal. Successful results replace the container's effective children, synchronize controls and UI state, and render at its position. The supplied fields configuration is never mutated.

Use dynamicFields for a transparent insertion slot. It creates no visual wrapper, grid or Angular FormGroup. Its children become siblings of the surrounding fields, preserving the parent's row, column or grid layout. Nested slots are transparent too. Apply layout to individual children when grid placement is needed; these placements enable the root grid just like placements on direct fields. Static fields: [] supplies the initial children and may contain fallback fields. Use section or layoutGroup when the returned fields need a titled section or an independent grid. All three variants share the same dependency engine and belong to the same form group as their static children.

The following configuration inserts the returned fields below the select:

import {
  EnumType,
  KlesFormElement,
} from '@3kles/kles-material-dynamicforms';

// dossierService.loadFields returns KlesFormElement[], a Promise, or an Observable.
const fields: KlesFormElement[] = [
  {
    name: 'typeDossier',
    type: EnumType.select,
    options: ['individual', 'company'],
  },
  {
    type: 'dynamicFields',
    fields: [],
    dependsOn: ['typeDossier'],
    resolveFields: ({ values }) => {
      const dossierType = values['typeDossier'];
      return typeof dossierType === 'string' && dossierType
        ? dossierService.loadFields(dossierType)
        : [];
    },
  },
];

The resolver receives an IKlesFormFieldsResolveEvent containing element (the container), group (the root form), values (its getRawValue() snapshot, including disabled controls), and context (the current external context or null). Use the captured values for asynchronous work; group remains live. Treat the snapshot and its nested values as read-only.

The dependency engine follows this execution contract:

  • Dependencies: paths are relative to the root form, including inside nested visual containers. Dot notation such as address.country follows Angular control paths; wildcards and relative-path syntax are not supported. Empty or missing paths and dependency cycles must produce diagnostics instead of silently starting a resolver. Resolution waits until every declared control exists. Duplicate paths are observed once. Cycles are detected from static children and known resolved definitions; names that have never been returned cannot be inferred in advance. Unrelated valid branches continue to resolve when another branch has a diagnostic.
  • Triggers: resolve once after the form and declared dependencies are ready, then whenever any dependency changes according to Object.is. With an omitted or empty dependsOn, resolve only initially. Object values must be replaced to trigger resolution. Changing context alone is not a trigger in this first contract; existing Signal-based resolveUi behavior is separate.
  • Results: accept readonly KlesFormElement[], a Promise of that array, or an Observable of arrays. Each result replaces all container children; results are never appended. An empty array removes the children. Static fields remains required and supplies the initial children. Without resolveFields, dependsOn has no effect.
  • Concurrency and lifetime: the latest dependency values win. Unsubscribe from the previous Observable and ignore obsolete Promise results. Removing a container or replacing its resolver/dependencies disposes its resolution; a replacement configuration starts a new initial resolution.
  • Loading and errors: keep the last successful children, or the initial fields, while waiting. An Observable may emit multiple replacements; completion without a result keeps the current children. A synchronous throw, rejected Promise, or Observable error keeps the current children and reports a diagnostic. A subsequent dependency change can retry. This contract does not introduce a loading widget or a submission-blocking policy.
  • Controls: a result must be synchronized with controls and UI state before rendering. Preserve existing values only for compatible controls with the same path; remove controls for children that disappear. Child control names must be unique within their form group, including across visual containers. Invalid results, including duplicate names, retain the last synchronized children and produce an invalid-result diagnostic. Generated nested visual containers can declare their own resolvers; they are registered after their controls are ready and disposed when their parent result removes them.

The form builds a separate effective tree, available as renderedFields. Input containers keep their original static fields; use fieldResolutions to inspect resolved definitions and form.getRawValue() to read the actual submitted data. Compatible controls retain user input across resolutions even when a new default value is returned. Removed controls and their values leave the form.

This contract leaves options, optionGroups, resolveUi, validations, asyncValidations, valueChanges, and the one-time asyncValue behavior unchanged. Reusing the dependency engine for those APIs is a later step.

After initialization, inspect an existing container's resolution with a form reference:

const state = dynamicForm.fieldResolutions().get(container);
// state.status: 'waiting' | 'loading' | 'ready' | 'error'
// state.fields: last successful result, or initial fields before the first result
// state.diagnostic: code, message, paths, element and optional underlying error

Diagnostics are also reported to the console and repeated identical dependency diagnostics are suppressed. The engine is refreshed after form configuration updates and disposed when the form is destroyed. Context is read at each resolution but does not trigger resolutions by itself.

KlesFormDependencyEngine is also exported for standalone use. Construct it with a root form and optionally a context getter, call update(elements) to register containers or refresh silent control edits, and call destroy() when finished. Its states Signal retains outcomes; resolved$ emits successful arrays and diagnostics$ emits diagnostics. It never changes fields or controls itself.

Directives

  • KlesComponentDirective -> Directive to inject component with value
  • KlesDynamicFieldDirective -> Directive to inject component with IFieldConfig in FormGroup

Interfaces

IKlesFieldConfig

Interface field model

  • type?: string -> Mapper type if(type && !component)=>type
  • name: string -> Name Field (key for FormControlName)
  • component?: Type<any> -> Component field
  • id?: string -> Attribut html id
  • label?: string -> Label field
  • placeholder?: string -> Placeholder field
  • tooltip?: string -> Tooltip field
  • inputType?: string -> Input type
  • min?: number | Date -> Min value for number input or date field
  • max?: number | Date -> Max value for number input or date field
  • maxLength?: number -> Max length for input value
  • step?: number -> Define a step for number input
  • options?: any[] | Subject<any[]> | Observable<any[]> | ((value?: string) => Observable<any[]>) -> List options for list component
  • ngClass?: any -> ngclass for field
  • ngStyle?: any -> ngStyle for field
  • property?: string -> Property for field
  • collections?: any -> Collections for subfield
  • value?: any -> Value field
  • asyncValue?: Observable<any> -> Async value field
  • multiple?: boolean -> Multiple selection field
  • disabled?: boolean -> Disabled field
  • autocomplete?: boolean -> Autocomplete input field
  • autocompleteComponent?: Type<\any> -> Autocomplete component to display in list option
  • displayWith?: ((value: any) => string) | null -> Autocomplete display format
  • panelWidth?: string | number -> Width for panel list option
  • indeterminate?: boolean -> Indeterminate checkable component
  • color?: string -> Material color
  • icon?: string -> Material icon
  • iconSvg?: string -> Svg icon
  • textareaAutoSize?: { minRows?: number; maxRows?: number } -> Define a min and max number of rows for text area
  • validations?: IKlesValidator<ValidatorFn>[] -> Array of validators for a field
  • asyncValidations?: IKlesValidator<AsyncValidatorFn>[] -> Array of async validators for a field
  • pipeTransform?: { pipe: PipeTransform, options?: any[] }[] -> Array of pipe to apply to value field
  • direction?: 'row' | 'column' -> Direction for the display of the fields
  • valueChanges?: ((field: IKlesFieldConfig, group: UntypedFormGroup, siblingField?: IKlesFieldConfig[], valueChanged?: any) => void) -> Emit each time the value is changing
  • triggerComponent?: Type<any> -> Trigger component to customize trigger label in select
  • virtualScroll?: boolean -> To activate virtual scroll
  • itemSize?: number -> Item size for virtual scroll
  • pending?: boolean -> If the value is pending
  • searchKeys?: string[] -> List of keys for multiple searches
  • updateOn?: 'change' | 'blur' | 'submit' -> Set the update on diferent moment
  • debounceTime?: number -> Time in milliseconds before emit value after changed
  • directive?: (new (ref: ViewContainerRef, field: IKlesField) => IKlesDirective) -> Set a new directive to the field
  • visible?: boolean -> Set if the field is visible
  • lazy?: boolean -> Set if the field is lazy
  • buttonType?: 'submit' | 'button' | 'reset' -> Define the type of a button
  • accept?: string ->
  • dateOptions?: { adapter?: { class: Type<DateAdapter<any>>, deps?: any[] }, language: string, dateFormat: MatDateFormats } -> Define options for date field
  • hint?: string -> Display as a mat-hint for the field
  • clearable?: boolean -> Display a cross to clear the field
  • clearableComponent?: Type<any> -> Define a new clearable component
  • subComponents?: Type<any>[] -> Define sub components of a field
  • autofocus?: boolean -> Set the autofocus on the field
  • attribute?: EnumButtonAttribute -> To display material button design
  • subscriptSizing?: SubscriptSizing ->
  • nonNullable?: boolean -> Set if the field can be null

Fields

  • KlesFormArrayComponent component that creates a form array of other components
  • KlesFormBadgeComponent component to display a badge
  • KlesFormFabComponent component to display a fab button
  • KlesFormButtonComponent component to display a button
  • KlesFormIconButtonComponent component to display an icon button
  • KlesFormMiniFabComponent component to display a mini fab button
  • KlesFormButtonToogleGroupComponent component to display a button group toggle
  • KlesFormButtonCheckerComponent component to check error in form
  • KlesFormButtonFileComponent button that take a file
  • KlesFormCheckboxComponent component to display a checkbox
  • KlesFormChipComponent component to display a chip
  • KlesFormClearComponent component to clear a form
  • KlesFormColorComponent component to select a color
  • KlesFormDateComponent component to select a date
  • KlesFieldAbstract abstract class to build field component
  • KlesFormGroupComponent component that creates a form group of other components
  • KlesFormIconComponent component to display a material icon
  • KlesFormInputClearableComponent component with an input that is clearable
  • KlesFormInputComponent component with an input
  • KlesFormLineBreakComponent component to go to the next line
  • KlesFormLinkComponent component that display a link
  • KlesFormListFieldComponent component to display a list
  • KlesFormRadioComponent component that display a material radio
  • KlesFormRangeComponent component to select a date range
  • KlesFormSelectComponent component to select a value in options
  • KlesFormSelectLazySearchComponent component to select in lazy options that can be filtered
  • KlesFormSelectSearchComponent component to select a value in options that can be filtered
  • KlesFormSelectionListComponent component to create a list with elements that can be selected
  • KlesFormSelectionListSearchComponent component to create a list with elements that can be selected and filtered
  • KlesFormSlideToggleComponent component to display a group toggle
  • KlesFormTextComponent component to display text
  • KlesFormTextareaComponent component to display a text area

Install

npm

npm install @3kles/kles-material-dynamicforms --save

How to use

In the module

import { KlesMaterialDynamicFormsModule } from '@3kles/kles-material-dynamicforms';
...
@NgModule({
 imports: [
    KlesMaterialDynamicFormsModule,
    ...
 ]
 ...
})

Check the documentation to use component and directive.

Tests

npm install
npm test

License

MIT