{"id":51377215,"url":"https://github.com/ronbodnar/ng-modular-forms","last_synced_at":"2026-07-05T21:00:33.626Z","repository":{"id":351979421,"uuid":"1208504278","full_name":"ronbodnar/ng-modular-forms","owner":"ronbodnar","description":"A lightweight architectural layer with built-in components for modular Angular reactive forms.","archived":false,"fork":false,"pushed_at":"2026-06-28T21:28:06.000Z","size":981,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-28T23:07:36.877Z","etag":null,"topics":["angular","angular-forms","angular-material","dynamic-forms","forms","modular-forms","reactive-forms","typescript"],"latest_commit_sha":null,"homepage":"http://ngmf.ronbodnar.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ronbodnar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-12T11:28:03.000Z","updated_at":"2026-06-28T21:25:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ronbodnar/ng-modular-forms","commit_stats":null,"previous_names":["ronbodnar/ng-modular-forms"],"tags_count":25,"template":false,"template_full_name":null,"purl":"pkg:github/ronbodnar/ng-modular-forms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronbodnar%2Fng-modular-forms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronbodnar%2Fng-modular-forms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronbodnar%2Fng-modular-forms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronbodnar%2Fng-modular-forms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ronbodnar","download_url":"https://codeload.github.com/ronbodnar/ng-modular-forms/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ronbodnar%2Fng-modular-forms/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35168795,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","angular-forms","angular-material","dynamic-forms","forms","modular-forms","reactive-forms","typescript"],"created_at":"2026-07-03T14:00:27.945Z","updated_at":"2026-07-05T21:00:33.619Z","avatar_url":"https://github.com/ronbodnar.png","language":"TypeScript","funding_links":[],"categories":["Third Party Components"],"sub_categories":["Forms"],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/ronbodnar/ng-modular-forms/main/projects/examples/public/icons/apple-touch-icon.png\" height=\"120\" alt=\"ng-modular-forms logo\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eng-modular-forms\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eA structured Angular forms architecture built for complex, scalable applications.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/ronbodnar/ng-modular-forms/actions/workflows/ci-cd.yml\"\u003e\u003cimg src=\"https://github.com/ronbodnar/ng-modular-forms/actions/workflows/ci-cd.yml/badge.svg\" alt=\"CI/CD\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://angular.dev\"\u003e\u003cimg src=\"https://img.shields.io/badge/Angular-19--21-DD0031?logo=angular\" alt=\"Angular Version\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/@ng-modular-forms/core\"\u003e\u003cimg src=\"https://badge.fury.io/js/%40ng-modular-forms%2Fcore.svg\" alt=\"npm version\" /\u003e\u003c/a\u003e\n  \u003ca href=\"/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"License: MIT\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://ngmf.ronbodnar.com/docs/examples\"\u003e\n    \u003cstrong\u003eLive Examples \u0026 Interactive Demo →\u003c/strong\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n## The Problem\n\nAs enterprise Angular applications grow, form logic quickly becomes a maintenance bottleneck. Side-effect subscriptions spread across components, API data mapping logic gets duplicated, and components bloat with validation mechanics. \n\n## The Solution\n\n`ng-modular-forms` introduces a strict **separation of concerns** layer built right on top of Angular Reactive Forms. It isn't a replacement for standard reactive APIs — it's an architectural framework designed to decouple:\n\n* **Form Orchestration:** Isolate conditional validation and multi-step workflow logic.\n* **Reactive Behaviors:** Pull complex cross-field dependencies out of presentation components.\n* **Data Mapping:** Translate backend DTOs to form states declaratively.\n* **UI Shells:** Interchange native elements and Angular Material layers instantly.\n\n## Key Features\n- Typed Reactive Forms support\n- Form orchestration layer\n- DTO ↔ Form mapping\n- State hydration and serialization\n- Cross-field behavior management\n- Dynamic enable/disable workflows\n- Native and Angular Material UI packages\n- Consistent component APIs\n- Enterprise-scale architecture patterns\n- Angular 19–21 support\n\n## Installation \u0026 Setup\n\n### 1. Install Packages\nStart with core:\n\n```bash\nnpm install @ng-modular-forms/core\n```\n\nIf you are using Angular Material components, install the UI adapter and its peer dependencies:\n\n```bash\nnpm install @ng-modular-forms/material @angular/material @angular/cdk\n```\n\n### 2. Configure Global Styles\nAdd the required control structural themes to your angular.json styles pipeline depending on your configuration:\n\n```json\n\"styles\": [\n  \"src/styles.css\",\n  \n  // Required ONLY if utilizing @ng-modular-forms/core native UI elements\n  \"node_modules/@ng-modular-forms/core/styles/form-controls.css\",\n  \n  // Required ONLY if utilizing @ng-modular-forms/material UI components\n  \"node_modules/@ng-modular-forms/material/styles/form-controls.css\"\n]\n```\n\n###  3. Configure Global Configuration\n`@ng-modular-forms/core` provides a single global configuration system via `provideNmfConfig`.\n\nThis configuration is shared across all packages (core + material) **and is optional**.\n\nIf not provided, sensible defaults are used.\n\n#### Available options\n  \n```typescript\ntranslate?: (\n  key: string,\n  params?: Record\u003cstring, unknown\u003e\n) =\u003e string;\n\nvalidationMessages?: ValidationMessages;\n```\n\n#### Code Example\n```typescript\nimport { ApplicationConfig, inject } from '@angular/core';\nimport { TranslateService } from '@ngx-translate/core';\nimport { provideNmfConfig } from '@ng-modular-forms/core';\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    // Simple config\n    provideNmfConfig({\n      validationMessages: {\n        email: 'Invalid email',\n      },\n    })\n\n    //DI-based config\n    provideNmfConfigFactory(() =\u003e {\n      const translate = inject(TranslateService);\n\n      return {\n        translate: (k, p) =\u003e translate.instant(k, p),\n        validationMessages: {},\n      };\n    });\n  ],\n};\n```\n\n## Code Examples\n\n### Start Simple\n\n```typescript\n@Component({\n  template: `\n    \u003cform [formGroup]=\"form\"\u003e\n      \u003cnmf-text\n        label=\"First Name\"\n        formControlName=\"firstName\"\n      /\u003e\n\n      \u003cnmf-number\n        label=\"Salary\"\n        formControlName=\"salary\"\n        [formatValue]=\"true\"\n        prefix=\"$\"\n        suffix=\"USD\"\n      /\u003e\n    \u003c/form\u003e\n  `,\n})\nexport class EmployeeComponent {\n  form = new FormGroup({\n    firstName: new FormControl('', Validators.required),\n    salary: new FormControl\u003cnumber | null\u003e(null),\n  });\n}\n```\n\nStart with standard Angular Reactive Forms and adopt orchestration features only when your application needs them.\n\n### Add Reactive Logic\n\nAs forms grow, reactive business rules often end up scattered throughout Angular components.\n\n`ng-modular-forms` allows reactive behavior to be moved into dedicated handlers.\n\n```typescript\n@Injectable()\nexport class RegistrationFormHandler extends FormHandlerBase\u003cControls\u003e {\n\n  override getReactiveLogic(form?: FormGroup) {\n    if (!form) {\n      throw new Error(\n        'RegistrationFormHandler requires a form instance'\n      );\n    }\n\n    this.initializeForm(form);\n\n    const emailControl =\n      this.getControl('personalInfo.email');\n\n    return this.valueChangesOf(\n      'preferences.agreeToTerms'\n    ).subscribe((acceptedTerms) =\u003e {\n\n      const validators = acceptedTerms\n        ? [Validators.required, Validators.email]\n        : [Validators.email];\n\n      emailControl.setValidators(validators);\n      emailControl.updateValueAndValidity({\n        emitEvent: false,\n      });\n    });\n  }\n}\n```\n\nInstead of managing subscriptions inside components, business rules are isolated into reusable handlers that can be tested independently.\n\n### DTO ↔ Form Mapping\n\nAPI contracts and form models often evolve independently.\n\n`ng-modular-forms` provides dedicated mappers for translating between them.\n\n```typescript\nexport class PreferencesMapper extends FormMapperBase\u003c\n  PreferencesDto,\n  PreferencesForm,\n  PreferencesForm,\n  PreferencesOptions\n\u003e {\n\n  override fromModel(dto: PreferencesDto) {\n    return {\n      receiveEmails: dto.receive_emails,\n      theme: dto.theme_name,\n    };\n  }\n\n  override toRequest(formValue: PreferencesForm, options?: PreferencesOptions) {\n    return {\n      receive_emails: formValue.receiveEmails,\n      theme: formValue.theme,\n    };\n  }\n}\n```\n\nMapping logic remains centralized and reusable instead of being duplicated throughout components and services.\n\n### Large Forms and Multi-Step Workflows\n\nAs complexity grows, handlers, mappers, hydration, and serialization can be orchestrated through a single coordinator.\n\n```typescript\n@Component({\n  providers: [\n    MultiStepFormHandler,\n    PersonalInfoFormHandler,\n    AccountDetailsFormHandler,\n    PreferencesFormHandler,\n  ],\n})\nexport class MultiStepFormComponent\n  extends FormOrchestrator {\n\n  initialize() {\n    this.orchestrate({\n      form: registrationForm,\n\n      handlerRegistry: [\n        inject(MultiStepFormHandler),\n        inject(PersonalInfoFormHandler),\n        inject(AccountDetailsFormHandler),\n        inject(PreferencesFormHandler),\n      ],\n\n      mapperRegistry: {\n        preferences: new PreferencesMapper(),\n      },\n    });\n\n    this.hydrateFromModel(existingUser);\n  }\n}\n```\n\nThe same architecture scales from simple forms to enterprise workflows without moving business logic into Angular components.\n\n## Developer Quick Start\n\nClone and run the examples app:\n\n```bash\ngit clone https://github.com/ronbodnar/ng-modular-forms.git\ncd ng-modular-forms\nnpm install\nnpm start\n```\n\nOpen your browser to `http://localhost:4200/docs/examples` to see the live interactive examples.\n\n## Ecosystem Packages\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth style=\"white-space: nowrap;\"\u003ePackage\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n    \u003cth style=\"white-space: nowrap;\"\u003eLinks\u003c/th\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd style=\"white-space: nowrap;\"\u003e\u003cb\u003e@ng-modular-forms/core\u003c/b\u003e\u003c/td\u003e\n    \u003ctd\u003e\n      Form orchestration, reactive behavior handling, API mapping, and state hydration\n    \u003c/td\u003e\n    \u003ctd style=\"white-space: nowrap;\"\u003e\n      \u003ca href=\"https://www.npmjs.com/package/@ng-modular-forms/core\"\u003enpm\u003c/a\u003e,\n      \u003ca href=\"https://github.com/ronbodnar/ng-modular-forms/tree/main/projects/core#readme\"\u003edocs\u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd style=\"white-space: nowrap;\"\u003e\u003cb\u003e@ng-modular-forms/material\u003c/b\u003e\u003c/td\u003e\n    \u003ctd\u003e\n      Angular Material-based input components\n    \u003c/td\u003e\n    \u003ctd style=\"white-space: nowrap;\"\u003e\n      \u003ca href=\"https://www.npmjs.com/package/@ng-modular-forms/material\"\u003enpm\u003c/a\u003e,\n      \u003ca href=\"https://github.com/ronbodnar/ng-modular-forms/tree/main/projects/material#readme\"\u003edocs\u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Unified Input Control API\n\nAll integrated control primitives are built on ControlValueAccessor. This allows you to interchange Native selectors for Material selectors instantly without altering a single line of orchestration logic.\n\n| Input Type        | Native Selector                | Material Selector                  | Description                                              |\n|-------------------|--------------------------------|------------------------------------|----------------------------------------------------------|\n| Text / Password   | `nmf-text`                     | `nmf-mat-text`                     | Masking, text parsing, and standard string inputs.       |\n| Lookup            | `nmf-lookup`                   | `nmf-mat-lookup`                   | Automated synchronous/asynchronous auto-complete engine. |\n| Number / Currency | `nmf-number`                   | `nmf-mat-number`                   | Strictly typed runtime numeric normalization.            |\n| Range Slider      | `nmf-range`                    | Not available                      | Clamped min/max sliding numeric selection                |\n| Date              | `nmf-datepicker`               | `nmf-mat-datepicker`               | Calendar utility wrappers.                               |\n| Time              | `nmf-timepicker`               | `nmf-mat-timepicker`               | Standard temporal data picking inputs.                   |\n| Select            | `nmf-select`                   | `nmf-mat-select`                   | Option-driven stream selectors.                          |\n| Textarea          | `nmf-textarea`                 | `nmf-mat-textarea`                 | Multi-line textual controls.                             |\n\n### Features\n\n- `ControlValueAccessor` compatible\n- Fully compatible with Angular Reactive Forms\n- Consistent API across all inputs\n- Built-in validation state + error messaging\n- Label, required indicator, and loading state support\n- Behavior-driven input handling (formatting, parsing, restrictions)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronbodnar%2Fng-modular-forms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fronbodnar%2Fng-modular-forms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fronbodnar%2Fng-modular-forms/lists"}