{"id":31658217,"url":"https://github.com/metacodi/ionic-angular-dynamic-forms","last_synced_at":"2026-05-09T03:32:04.896Z","repository":{"id":314086961,"uuid":"1052824258","full_name":"metacodi/ionic-angular-dynamic-forms","owner":"metacodi","description":"Dynamic, composable field components for Ionic + Angular that extend existing forms with additional, self‑described JSON fields.","archived":false,"fork":false,"pushed_at":"2025-09-15T09:27:58.000Z","size":118,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-07T15:27:00.870Z","etag":null,"topics":["angular","dynamic-forms","ionic","reactiveforms"],"latest_commit_sha":null,"homepage":"","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/metacodi.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":"2025-09-08T15:45:04.000Z","updated_at":"2025-09-15T09:28:02.000Z","dependencies_parsed_at":"2025-09-10T15:22:58.943Z","dependency_job_id":"255f3fb9-e835-4c31-ac3c-365ebe8e5c8a","html_url":"https://github.com/metacodi/ionic-angular-dynamic-forms","commit_stats":null,"previous_names":["metacodi/ionic-angular-dynamic-forms"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/metacodi/ionic-angular-dynamic-forms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metacodi%2Fionic-angular-dynamic-forms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metacodi%2Fionic-angular-dynamic-forms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metacodi%2Fionic-angular-dynamic-forms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metacodi%2Fionic-angular-dynamic-forms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metacodi","download_url":"https://codeload.github.com/metacodi/ionic-angular-dynamic-forms/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metacodi%2Fionic-angular-dynamic-forms/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32805900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T08:22:46.396Z","status":"online","status_checked_at":"2026-05-09T02:00:06.633Z","response_time":123,"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","dynamic-forms","ionic","reactiveforms"],"created_at":"2025-10-07T15:15:31.281Z","updated_at":"2026-05-09T03:32:04.863Z","avatar_url":"https://github.com/metacodi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Ionic Angular Dynamic Forms\n===========================\n\nDynamic, composable field components for Ionic + Angular that extend existing forms with additional, self‑described JSON fields. This library is designed to augment heterogeneous entity forms with dynamic fields while preserving your existing form structure and layout, including responsive presentation inside `ion-grid`, `ion-row`, and `ion-col` containers.\n\nKey goals\n- Respect existing form hierarchy: extend, don’t replace\n- JSON‑described fields with expression support\n- Responsive layout with `ion-grid` nesting\n- Minimal runtime API with strong TypeScript types\n\nStatus\n- Library code is ready for consumption as an Angular package.\n- The `test/` folder contains usage examples (not test runners).\n\nInstallation\n- Add as a library to your Angular + Ionic workspace.\n- Ensure peer dependencies match your project (Angular 16–17, Ionic 7).\n\n  ```sh\n  npm install @metacodi/ionic-angular-dynamic-forms\n  ```\n\nPeer dependencies\n- `@angular/common`, `@angular/core`, `@angular/forms`\n- `@ionic/angular`\n- `@ngx-translate/core` (optional but supported for i18n)\n- `rxjs`, `zone.js`\n\nBuild\n- Build the package with `npm run build` (ng-packagr). The entry point is `src/public-api.ts`.\n\nHow it works\n- Declare dynamic field metadata (type, label, icon, validators, visibility, etc.).\n- Render fields using the provided directives/components within your existing grid layout.\n- Expressions are evaluated in the context of a provided `host` object plus optional event args.\n\nQuick start\n1) Import module in your app module or a feature module:\n```ts\nimport { IonicAngularDynamicForms } from '@metacodi/ionic-angular-dynamic-forms';\n\n@NgModule({\n  imports: [IonicAngularDynamicForms]\n})\nexport class FeatureModule {}\n```\n\n2) Define a host component implementing the DynamicHost interface with a reactive form and a permission helper:\n```ts\nimport { DynamicHost, DynamicRowType } from '@metacodi/ionic-angular-dynamic-forms';\n\nform = this.fb.group({});\n\nhost: DynamicHost = {\n  isNew: false,\n  initialized: true,\n  hasPermission: (p?: string | boolean) =\u003e p === undefined || p === true\n};\n\nrows: DynamicRowType[] = [\n  {\n    title: 'profile.section',\n    row: { ngClass: `{'compact': true}` },\n    cols: [\n      {\n        name: 'firstName',\n        value: { type: 'string', default: '' },\n        component: {\n          type: 'input',\n          size: { xs: 12, md: 6 },\n          label: 'profile.first_name',\n          input: { placeholder: 'profile.first_name', required: true },\n          validators: `Validators.required`\n        }\n      }\n    ]\n  }\n];\n```\n\n3) Use the directives in your template:\n```html\n\u003cion-content\u003e\n  \u003cform [formGroup]=\"form\"\u003e\n\n    \u003c!-- Grid of fields with rows and columns descriptions --\u003e  \n    \u003cng-container dynamic-fields-grid [frm]=\"form\" [rows]=\"rows\" [host]=\"host\"\u003e\u003c/ng-container\u003e    \n\n    \u003cion-grid\u003e\n\n      \u003c!-- Or per row --\u003e\n      \u003cng-container dynamic-fields-row [frm]=\"form\" [cols]=\"rows[0].cols\" [host]=\"host\"\u003e\u003c/ng-container\u003e\n\n      \u003cion-row\u003e\n        \u003cion-col\u003e\n          \u003c!-- Or per field directly --\u003e\n          \u003cng-container dynamicField [frm]=\"form\" [field]=\"rows[0].cols[0]\" [host]=\"host\"\u003e\u003c/ng-container\u003e\n        \u003c/ion-col\u003e\n      \u003c/ion-row\u003e\n    \n    \u003c/ion-grid\u003e\n\n  \u003c/form\u003e\n\u003c/ion-content\u003e\n```\n\nExpressions\n- Provide expressions as strings; they run with the `host` properties and optional event argument `$event`.\n- Examples:\n  - Visibility: `visible: '!isNew'`\n  - Disabled: `disabled: 'isNew \u0026\u0026 !form.valid'`\n  - Validators: `validators: 'Validators.required'`\n\nExamples\n- See `test/basic-usage.md` and `test/schema-examples.ts` for end-to-end samples of field definitions and layouts.\n\nDirective usage\n- `dynamic-fields-grid`:\n  ```html\n  \u003c!-- Renders sections/rows and each field inside ion-grid/ion-row/ion-col --\u003e\n  \u003cng-container dynamic-fields-grid [frm]=\"form\" [rows]=\"rows\" [host]=\"host\"\u003e\u003c/ng-container\u003e\n  ```\n- `dynamic-fields-row`:\n  ```html\n  \u003c!-- Renders a single row made of columns (fields) --\u003e\n  \u003cng-container dynamic-fields-row [frm]=\"form\" [cols]=\"row.fields\" [host]=\"host\"\u003e\u003c/ng-container\u003e\n  ```\n- `dynamicField`:\n  ```html\n  \u003c!-- Renders one field based on component.type --\u003e\n  \u003cng-container dynamicField [frm]=\"form\" [field]=\"field\" [host]=\"host\"\u003e\u003c/ng-container\u003e\n  ```\n\nField recipes\n- Input with label, placeholder, validators and responsive size:\n  ```ts\n  const firstName: FieldType = {\n    name: 'firstName',\n    value: { type: 'string', default: '' },\n    component: {\n      type: 'input',\n      size: { xs: 12, md: 6 },\n      label: 'profile.first_name',\n      input: { placeholder: 'profile.first_name', required: true },\n      validators: `Validators.required`\n    }\n  };\n  ```\n- Checkbox with label on the right and simple permission:\n  ```ts\n  const tos: FieldType = {\n    name: 'tos',\n    value: { type: 'boolean', default: false },\n    component: {\n      type: 'checkbox',\n      size: 12,\n      permission: 'profile.accept_tos',\n      label: 'profile.accept_tos',\n      checkbox: { slot: 'start' },\n      validators: `Validators.requiredTrue`,\n      errors: [{ validator: 'requiredTrue', text: 'errors.accept_tos', behavior: ['touched'] }]\n    }\n  };\n  ```\n- Toggle with clickable label to flip value:\n  ```ts\n  const newsletter: FieldType = {\n    name: 'newsletter',\n    value: { type: 'boolean', default: true },\n    component: {\n      type: 'toggle',\n      size: { xs: 12, md: 6 },\n      label: 'profile.newsletter',\n      toggle: { slot: 'end' }\n    }\n  };\n  ```\n- Button with icon and click expression:\n  ```ts\n  const submit: FieldType = {\n    name: 'submit',\n    value: { type: 'string' },\n    component: {\n      type: 'button',\n      size: 12,\n      label: 'buttons.submit',\n      icon: { name: 'send', slot: 'start' },\n      button: { expand: 'block', strong: true },\n      click: `saveRow()`\n    }\n  };\n  ```\n- Datetime placeholder and basic config:\n  ```ts\n  const birthDate: FieldType = {\n    name: 'birthDate',\n    value: { type: 'date' },\n    component: {\n      type: 'datetime',\n      size: { xs: 12, md: 6 },\n      label: 'profile.birth_date',\n      datetime: { placeholder: 'profile.birth_date' }\n    }\n  };\n  ```\n\nLabels, icons and pipes\n- Label text defaults to translate; specify pipes to alter behavior:\n  ```ts\n  label: { text: 'buttons.accept', pipes: ['translate', 'uppercase'] }\n  // To avoid default translation pipe\n  label: { text: 'Accept', pipes: [] }\n  ```\n- Icons can use an Ionic icon name or a custom src, with an optional slot:\n  ```ts\n  icon: 'people'                    // shorthand name\n  icon: { name: 'people', slot: 'start' }\n  icon: { src: 'assets/icon/people.svg', slot: 'end' }\n  ```\n\nVisibility, disabled and validators (expressions)\n- Expressions run against the provided `host` and optional `$event`:\n  ```ts\n  component: { visible: '!isNew' }\n  component: { disabled: '!initialized || frm.invalid || frm.pristine' }\n  component: { validators: `[Validators.required, Validators.email]` }\n  ```\n\nError messages behavior\n- Control when to show errors via `behavior`: any of `touched`, `dirty`, `isNew`.\n  ```ts\n  errors: [\n    { validator: 'required', text: 'login.email_required', behavior: ['touched', 'dirty'] }\n  ]\n  ```\n\nEvents\n- Provide expressions that receive `$event` for focus, blur, change or input:\n  ```ts\n  input: { ionBlur: `onBlur($event)`, ionFocus: `onFocus($event)`, ionChange: `onChange($event)`, ionInput: `onInput($event)` }\n  button: { ionBlur: `onButtonBlur($event)`, ionFocus: `onButtonFocus($event)` }\n  ```\n\nPermissions\n- Section/field rendering is gated by `host.hasPermission(permission)`; provide a boolean or a string key.\n  ```ts\n  permission: true // or a string key your host understands\n  // host.hasPermission is called internally by the library\n  ```\n\nDynamicHost interface\n- Your host component (the owner of the reactive form) should implement the following contract:\n  ```ts\n  export interface DynamicHost {\n    // Indicates whether the row/entity being edited is new or existing\n    isNew: boolean;\n    // True after Angular's ngOnInit has run in your host\n    initialized: boolean;\n    // Returns whether the user has the specified permission\n    hasPermission: (permission?: string | boolean) =\u003e boolean;\n  }\n  ```\n- The library evaluates expressions and permissions against this `host`. For example:\n  ```ts\n  component: { visible: '!isNew' }\n  component: { disabled: '!initialized || frm.invalid || frm.pristine' }\n  permission: 'profile.accept_tos' // host.hasPermission('profile.accept_tos')\n  ```\n\nContributing\n- Issues and PRs welcome. Keep changes focused and backward compatible.\n\nLicense\n- MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetacodi%2Fionic-angular-dynamic-forms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetacodi%2Fionic-angular-dynamic-forms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetacodi%2Fionic-angular-dynamic-forms/lists"}