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

ngx-dynamic-entity

v2.1.0

Published

Angular standalone UI library for rendering dynamic entity forms.

Readme

ngx-dynamic-entity

npm version License: MIT

Angular dynamic form renderer and tabbed record editor, powered by @dynamic-entity/core. Supports Angular 17 through 22.

This package renders forms. It ships no data table — rendering a table over your records is the consumer's choice.


📦 Installation

npm install ngx-dynamic-entity @dynamic-entity/core

No Angular Material required. This package has no dependency on Material or the CDK.


✨ Features

  • DynamicFormComponent — tabbed dynamic forms generated from an EntityFormConfig, built on Angular Reactive Forms.
  • DynamicRecordFormComponent — record editor with summary drawer (showOnMinimize), profile header, and per-section saving.
  • Reactive rules — real-time condition evaluation driving field/tab visibility, validation errors and warnings, and info banners.
  • Entity references & cascades — consumer-registered loaders, parent→child dropdown filtering, and autoPatch record copying.
  • 21 field types — each a standalone component, registered explicitly so unused types are never bundled.
  • Fully translatable — config text is LocalizedText; the library's own buttons and empty states resolve through uiText.
  • Configurable masking and dates — MASKED_PLACEHOLDER replaces the XXXXXXXXX literal; setDateFormatters in @dynamic-entity/core replaces the browser-locale date punctuation.

🚀 Setup & Registration

Field types are not registered automatically. Without this step the form renders no fields.

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideNgxDynamicEntity, provideBuiltInFieldTypes } from 'ngx-dynamic-entity';

export const appConfig: ApplicationConfig = {
  providers: [
    provideNgxDynamicEntity({
      maskedRoles: ['IT_SUPPORT'],
      entityRefs: {
        countries: () => fetch('/api/countries').then(res => res.json()),
      },
    }),
    provideBuiltInFieldTypes(),
  ],
};

To bundle only the types you use, register them individually instead:

import { provideFieldTypes, TextFieldComponent, DropdownFieldComponent } from 'ngx-dynamic-entity';

provideFieldTypes({ text: TextFieldComponent, dropdown: DropdownFieldComponent });

Multiple provideFieldTypes calls merge; on a key collision the later registration wins. Pass your own component for a key to override a built-in, or add a key of your own for a custom type.

Some field types take an optional collaborator. A markdown field renders its source as text until you register a renderer — see Markdown for MARKDOWN_RENDERER, which is any function from source to HTML.


🌍 Translating the library's own text

Labels, placeholders and options come from your config as LocalizedText and already follow the language input. The chrome around them — Save, Reset, "No rows yet." — is the library's, and resolves through uiText:

import { provideNgxDynamicEntity } from 'ngx-dynamic-entity';

export const providers = provideNgxDynamicEntity({
  uiText: {
    save: { en: 'Save', de: 'Speichern' },
    reset: { en: 'Reset', de: 'Zurücksetzen' },
  },
});

Values may be LocalizedText, a flat string, or — for an app that already has ngx-translate, Transloco or $localize — a resolver (key, defaultText, language) => string provided through UI_TEXT. Resolution is per key: anything you leave out keeps its English default. DEFAULT_UI_TEXT exports every key with its English source string, so a translation file can be generated from it. See i18n.


🎭 Masked placeholder and dates

A masked field shows XXXXXXXXX unless you replace it. The real value stays in the control either way — see Security.

import { ApplicationConfig } from '@angular/core';
import { MASKED_PLACEHOLDER, provideNgxDynamicEntity } from 'ngx-dynamic-entity';

export const maskedPlaceholderConfig: ApplicationConfig = {
  providers: [
    provideNgxDynamicEntity({}),
    { provide: MASKED_PLACEHOLDER, useValue: '••••••••' },
  ],
};

date / datetime / time still format in the browser's locale. To use the form's language, or a fixed format, call setDateFormatters from @dynamic-entity/core:

import { setDateFormatters } from '@dynamic-entity/core';

setDateFormatters({
  date: (value, lang) => value.toLocaleDateString(lang ?? []),
});

A partial object overrides one kind; setDateFormatters() restores the defaults. Every read-only date, datetime and time field goes through it, as does the record view's summary panel, so one value cannot render two ways depending on where you look.


💡 Usage

<ngx-dynamic-form
  [config]="formConfig"
  [initialData]="record"
  [userRoles]="['editor']"
  [language]="'en'"
  (formSubmit)="onSave($event)"
/>

Inputs

| Input | Type | Notes | |---|---|---| | config | EntityFormConfig | Required. | | initialData | Record<string, any> | See Record shape below. | | userRoles | string[] | Drives permission and masking checks. Defaults to []. | | language | string | Defaults to 'en'. | | rules | FormRule[] | Optional reactive rules. | | originalData | Record<string, any> | Baseline for the VALUE_CHANGED operator. | | readonly | boolean | Renders the whole form read-only. | | readOnlyFields | string[] | Read-only by field id. | | loading / error | boolean / string \| null | Render loading and error states. |

Outputs

| Output | Payload | |---|---| | formSubmit | Record<string, any> | | formChange | Record<string, any> | | formReset | void | | activeTabChange | string (tab id) |


📄 Three ways to present a record

DynamicFormComponent is the editable form. DynamicRecordFormComponent renders the same config as a record, and two of its inputs decide how much a reader may do:

| Presentation | Component | Inputs | |---|---|---| | Form | ngx-dynamic-form | editable controls plus the actions block | | Record view | ngx-dynamic-record-form | viewMode (default true) — values, with a per-tab "Edit section" flow | | Data only | ngx-dynamic-record-form | isReadOnly="true" — values, and no way to edit them |

<!-- Read-only for everyone, whatever their roles allow. -->
<ngx-dynamic-record-form
  [config]="config"
  [initialData]="record"
  [userRoles]="roles"
  [isReadOnly]="true"
></ngx-dynamic-record-form>

viewMode="false" gives a directly editable record with no view/edit distinction, for hosts that do not want the section flow.

Record-form inputs

| Input | Type | Notes | |---|---|---| | config / initialData / userRoles / language | — | As the form component. | | viewMode | boolean | Default true. Read-only with a per-tab edit flow. | | isReadOnly | boolean | Whole record read-only, with no edit affordance at all. | | readOnlyFields | string[] | Specific ids read-only while the rest stays editable. | | rules | FormRule[] | Optional reactive rules. |

| Output | Payload | |---|---| | formSubmit / formChange / formReset | As the form component. | | sectionSave | { tabId, record } — one tab was saved. Carries the whole record, not the tab's slice. | | saveRejected | { reason, error? } — a beforeSave hook aborted the save. |

A beforeSave hook governs both ways out of this component — the whole-record Save and "Save section" — and saveRejected fires for either. Bind it: an aborted save with nothing listening is indistinguishable from a button that does nothing.

RBAC applies on top of all three. Roles outside permissions.edit get a read-only record whichever presentation you choose — see Security, because that is a rendering decision, not an access-control boundary.


🗂 Record shape

A record is nested by tab id by default:

const record = { general: { firstName: 'Alice' }, billing: { vatNumber: 'GB123' } };

Set flatData: true on a tab to store that tab's fields at the record root:

const record = { firstName: 'Alice', lastName: 'Smith' };

This applies in both directions — initialData is read with it and formSubmit emits with it. Passing a flat record to a tab that is not marked flatData leaves those fields empty, with no error.


🔐 Security

userRoles, EntityPermissions, and maskData affect rendering only. A masked field displays XXXXXXXXX by default (MASKED_PLACEHOLDER overrides the text) while the real value stays in the form control and is included in the formSubmit payload. Enforce authorization on the server and never send a user data they may not see.


Server-side rendering

This package is intended to work under Angular SSR. There is no raw document or window access; jumpToField schedules with afterNextRender, which does not run on the server. CI calls renderApplication on the published tarball.

This package does not use NgZone. It is intended to work under provideZonelessChangeDetection() on Angular 20+. CI renderApplications a form that way, with no zone.js installed.

The visual builder is a separate package and is not an SSR target.


🎨 Styling

This package ships no CSS. Field components emit ngx-field, ngx-field__label, ngx-field__input, and ngx-field__error for you to style. See packages/demo-angular/src/styles.css in the repository for a working reference.