{"id":25986617,"url":"https://github.com/Jaspero/schema-forms","last_synced_at":"2025-03-05T12:06:24.889Z","repository":{"id":37261537,"uuid":"242409981","full_name":"Jaspero/schema-forms","owner":"Jaspero","description":"A library for building forms through standard JSON schemas.","archived":false,"fork":false,"pushed_at":"2024-09-10T12:23:23.000Z","size":6507,"stargazers_count":2,"open_issues_count":19,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-02-24T05:44:16.580Z","etag":null,"topics":["angular","schemas"],"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/Jaspero.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":["Jaspero"]}},"created_at":"2020-02-22T20:51:32.000Z","updated_at":"2024-09-10T12:23:25.000Z","dependencies_parsed_at":"2024-08-02T11:21:45.025Z","dependency_job_id":null,"html_url":"https://github.com/Jaspero/schema-forms","commit_stats":null,"previous_names":[],"tags_count":1031,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jaspero%2Fschema-forms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jaspero%2Fschema-forms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jaspero%2Fschema-forms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Jaspero%2Fschema-forms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Jaspero","download_url":"https://codeload.github.com/Jaspero/schema-forms/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242023186,"owners_count":20059299,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","schemas"],"created_at":"2025-03-05T12:02:32.626Z","updated_at":"2025-03-05T12:06:24.866Z","avatar_url":"https://github.com/Jaspero.png","language":"TypeScript","readme":"[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n![Release](https://github.com/Jaspero/schema-forms/workflows/Release/badge.svg)\n\n| Library | Version |\n| ---- | ---- |\n| Core | [![NPM Version](https://img.shields.io/npm/v/@jaspero/form-builder.svg)](https://www.npmjs.com/package/@jaspero/form-builder) |\n| Fields Material | [![NPM Version](https://img.shields.io/npm/v/@jaspero/fb-fields-mat.svg)](https://www.npmjs.com/package/@jaspero/fb-fields-mat) |\n| Segments Materila | [![NPM Version](https://img.shields.io/npm/v/@jaspero/fb-segments-mat.svg)](https://www.npmjs.com/package/@jaspero/fb-segments-mat) |\n| Page Builder | [![NPM Version](https://img.shields.io/npm/v/@jaspero/fb-page-builder.svg)](https://www.npmjs.com/package/@jaspero/fb-page-builder) |\n| Form UI | [![NPM Version](https://img.shields.io/npm/v/@jaspero/fb-form-ui.svg)](https://www.npmjs.com/package/@jaspero/fb-form-ui) |\n| Tinymce | [![NPM Version](https://img.shields.io/npm/v/@jaspero/fb-tinymce.svg)](https://www.npmjs.com/package/@jaspero/fb-tinymce) |\n| Monaco Editor | [![NPM Version](https://img.shields.io/npm/v/@jaspero/fb-monaco-editor.svg)](https://www.npmjs.com/package/@jaspero/fb-monaco-editor) |\n\n# @jaspero/form-builder\n\n## Installation\n\nTo install this library, run:\n\n```bash\n$ npm install --save @jaspero/form-builder\n```\n\n### Add Fields and Segments\n\nIf you need to render the forms in the UI you'll need to add fields and segments.\nFields and segments are installed separably from the core module.\nWe provide one set of fields and segments built with material.\n\n```bash\n$ npm install --save @jaspero/fb-fields-mat\n$ npm install --save @jaspero/fb-segments-mat\n```\n\nAdd them in to your module like this:\n\n```tsv\n@NgModule({\n  imports: [\n    FbFieldsMatModule.forRoot({prefix: ''}),\n    FbSegmentsMatModule.forRoot({prefix: ''}),\n  ]\n})\n```\n\nGiving them an empty `prefix` defines them as the defaults.\n\n### Provide services and values\n\n1. Provide necessary services\n  - StorageService - Used for storing files when `FileComponent`, `ImageComponent` or `GalleryComponent` are used.\n  - DbService - Used for fetching referenced relations from your server in runtime\n2. Provide necessary values\n  - STORAGE_URL - Root URL for fetching files from your server\n  - ROLE - Segments and fields can be conditionally shown/hidden if the value of the role\n  matches what is expected in the schema\n  \n### Styles\n\nIn order to make the generated forms customizable, this library doesn't provide\nany default styles. This means that the styles need to be loaded in the root of your application.\nA good starting point are the example styles provided here.\n\n## Composing Forms\n\n### Definitions\n\nThis configuration is used for defining addition field based options. Changing the label or\nwhat component is used to represent the field in the form. The `Definitions` interface looks like this:\n\n| Property     | Type   | Description                                                                                                                                              | Default                                        |\n| ------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |\n| label        | string | Label of the field                                                                                                                                       | uses the name of the property                  |\n| hint         | string | Shows a hint below the field if defined                                                                                                                  | -                                              |\n| defaultValue | any    | What the value of the field should be if empty                                                                                                           | -                                              |\n| component    | object | `{type: string, configuration: any}` - The `type` defines the field to use and the `configuration` is used for any additional component specific options | What ever is the default for the property type |\n\n#### Component Types\n\n| Name  | Selector | Description    | Configuration Options               |\n| ----- | -------- | -------------- | ----------------------------------- |\n| Input | input    | A simple input | `{type: 'text', 'number', 'email'}` |\n\n#### Example\n\n```JSON\n{\n  \"name\": {\n    \"label\": \"Name\",\n    \"defaultValue\": \"John\"\n  },\n  \"age\": {\n    \"label\": \"Age\",\n    \"component\": {\n      \"type\": \"slider\"\n    }\n  }\n}\n```\n\n### Adding custom fields\n\n1. Create a new component that extends `FieldComponent`. You should inject `COMPONENT_DATA` in order to receive `FieldData`, most importantly the underlining `FormControl`.\n2. Map the newly added component through the `CUSTOM_FIELDS` provider e.g. \n    ```ts\n    providers: [{\n        provide: CUSTOM_FIELDS,\n        useValue: {\n            'new-component': NewComponent\n        }\n    }]\n    ```\n3. You can now use the new field in a forms `definitions`.\n\n### Adding custom components\n\n1. Create a new component that extends `CustomComponent`. It receives the `CUSTOM_COMPONENT_DATA` which has the `form` and `id` properties.\n2. Map the newly added component through the `CUSTOM_COMPONENTS` provider e.g.\n   ```ts\n   providers: [{\n     provide: CUSTOM_COMPONENTS,\n     useValue: {\n       example: ExampleComponent  \n     }\n   }] \n   ```\n3. You can now use the new component in a segment e.g.\n    ```json\n    \"segments\": [{\n      \"components\": [{\"selector\": \"example\"}]\n    }]\n    ```\n\n### Handling arrays\n\nThe form builder supports both arrays of primitives and object arrays.\n\n#### Object arrays\n\nThe following is required to render an object array:\n\n1. An object array defined in the schema:\n    ```json\n    {\n      \"addresses\": {\n       \"type\": \"array\",\n       \"items\": {\n         \"type\": \"object\",\n         \"properties\": {\n           \"city\": {\n             \"type\": \"string\"\n           },\n           \"address\": {\n             \"type\": \"string\"\n           }\n         }\n       }\n      }\n    }\n    ```\n2. A dedicated segment with an array property:\n    ```json\n    {\n      \"segments\": [{\n        \"array\": \"/addresses\",\n        \"fields\": [\n          \"/city\",\n          \"/address\"\n        ] \n      }]\n    }\n    ```\n3. You can also optionally define options for each field in the definitions:\n    ```json\n    {\n      \"definitions\": {\n        \"addresses/city\": {\n          \"label\": \"City\"\n        }  \n      }    \n    } \n    ```\n\n#### Primitive arrays\n\nPrimitive arrays can be displayed in two variations as a dedicated segment or\nas a field. \n\nIf the property is defined with out an `items` value. It's expected to be used as a field. \nIn that case the following components can be used:\n\n- **select** in combination with `{multiple: true}`\n- **chips**\n- **draggable-list**\n\nIf an items value is defined then it's expected to be rendered as its own segments.\n\n## Plugins\n\n### Official plugins\n\n#### TinyMCE WYSIWYG Editor\n\nThis plugin registers a field `tinymce` for rendering the TinyMCE WYSIWYG Editor.\n\n##### Dependencies\n\n|Library|Version|\n|----|----|\n|Tinymce|5.x|\n\n##### Set up\n\n1. Install the plugin `npm i --save @jaspero/fb-tinymce`\n2. Add the plugin module `TinymceModule` to your module\n3. Install tinymce `npm i --save tinymce`\n4. Add the following to the `assets` array in `angular.json`\n    ```json\n    {\n        \"glob\": \"**/*\",\n        \"input\": \"node_modules/tinymce/themes/silver\",\n        \"output\": \"/themes/silver\"\n    },\n    {\n        \"glob\": \"**/*\",\n        \"input\": \"node_modules/tinymce/skins/ui/oxide\",\n        \"output\": \"/skins/ui/oxide\"\n    },\n    {\n        \"glob\": \"**/*\",\n        \"input\": \"node_modules/tinymce/skins/content/default\",\n        \"output\": \"/skins/content/default\"\n    },\n    {\n        \"glob\": \"**/*\",\n        \"input\": \"node_modules/tinymce/plugins\",\n        \"output\": \"/plugins\"\n    },\n    {\n        \"glob\": \"**/*\",\n        \"input\": \"node_modules/tinymce/icons\",\n        \"output\": \"/icons\"\n    }\n    ```\n5. Add the tinymce script to the `scripts` array in `angular.json`\n    `\"./node_modules/tinymce/tinymce.min.js\"`\n6. You can optionally add/extend your commonjs dependencies whitelist to get rid of console warnings\n    ```json\n        \"allowedCommonJsDependencies\": [\n          \"json-pointer\",\n          \"tinymce/plugins/wordcount\",\n          \"tinymce/plugins/table\",\n          \"tinymce/plugins/lists\",\n          \"tinymce/plugins/print\",\n          \"tinymce/plugins/link\",\n          \"tinymce/plugins/image\",\n          \"tinymce/plugins/imagetools\",\n          \"tinymce/plugins/fullscreen\",\n          \"tinymce/plugins/code\",\n          \"tinymce/plugins/autolink\",\n          \"tinymce/plugins/advlist\"\n        ]\n    ``` \n   \n#### RefTable\n\nThis plugin registers a field `ref-table` that enables the editing of referenced item in an array and adding new references.\n\n##### Dependencies\n\nThis plugin doesn't have any additional dependencies.\n\n##### Set up\n\n1. Install the plugin `npm i --save @jaspero/fb-ref-table`\n2. Add the plugin module `FbRefTableModule` to your module\n\n__Note:__ If doing something like `users/{{id}}/notifications` it's important to have an initial value for the id.\n\n#### Page Builder\n\nThis plugin registers a field `pb-blocks` for rendering a page builder module.\n\n##### Dependencies\n\nThis plugin doesn't have any additional dependencies.\n\n##### Inline Editor\n\nUsing `InlineEditorModule` allows for editing blocks inline (not just in the sidebar on the left). There are 3 directives you can utilize.\n\n- `ImageIEDirective(fbPbImageIE)` - This directive is for editing image urls. It adds an edit icon and optionally other custom components.\nIt strictly needs to be used on a block element that is the direct parent to an image element that has no siblings.\n- `SingleLineIEDirective(fbPbSingleLineIE)` - This directive is for editing a single element. It adds a toolbar that allows for element type changes,\nalignment and decoration. The directive needs to be placed on the parent element, and the target element should have no siblings.\n- `MultiLineIEDirective(fbPbMultiLineIE)` - This directive provides the same functionality as the single line directive but allows for multiline editing.\n\n#### Form UI\n\nThis plugin registers a field `fu-fields` for rendering a form builder module.\n\n##### Dependencies\n\nThis plugin doesn't have any additional dependencies.\n\n##### Set up\n\n1. Install the plugin `npm i --save @jaspero/fb-form-ui`\n2. Add the plugin module `TinymceModule` to your module\n3. Add translation files for your specific language. This is en:\n    ```json\n    {\n      \"FU\": {\n        \"ID\": \"ID\",\n        \"LABEL\": \"Label\",\n        \"VALUE\": \"Value\", \n        \"HINT\": \"Hint\",\n        \"ORGANIZE\": \"Organize\",\n        \"PLACEHOLDER\": \"Placeholder\",\n        \"ADJUST_SIZE\": \"Adjust Size\",\n        \"ADD_FIELD\": \"Add Field\",\n        \"REQUIRED\": \"Required\",\n        \"CHANGE_TYPE\": \"Change Type\",\n        \"EDIT\": \"Edit\",\n        \"REMOVE\": \"Remove\",\n        \"OPTIONS\": \"Options\",\n        \"SETTINGS\": \"Settings\",\n        \"DEFAULT_PLACEHOLDER\": \"There are no fields currently.\",\n        \"SIZE\": {\n          \"DESKTOP\": \"Desktop\",\n          \"TABLET\": \"Tablet\",\n          \"MOBILE\": \"Mobile\"\n        },\n        \"TYPE\": {\n          \"checkbox\": \"Checkbox\",\n          \"email\": \"Email\",\n          \"number\": \"Number\",\n          \"select\": \"Select\",\n          \"text\": \"Text\",\n          \"textarea\": \"Textarea\"\n        }\n      }\n    }\n    ```\n4. You can now use the field in your schemas like this:\n\n    __schema.properties__\n    ```json\n    {\"fields\": {\"type\": \"array\"}}\n    ```\n    __layout.segments__\n    ```json\n    {\n      \"fields\": {\n        \"component\": {\n          \"type\": \"fu-fields\"\n        }\n      }\n    }\n    ```\n    __layout.instance.segments__\n    ```json\n    {\n      \"type\": \"empty\",\n      \"fields\": [\"/fields\"]\n    }\n    ```\n\n    __Conditional Segments__\n    ```jsonc\n    {\n      \"fields\": [\n        \"/showTitle\",\n        {\n          \"field\": \"/title\", // Field to assign actions\n          \"deps\": [\"/showTitle\"], // Array of fields on which to listen for a change, if none are provided whole form is used as a listener\n          \"action\": // Single action object or an array of objects\n            [\n              {\n                \"type\": \"show\", // \"show\" | \"hide\" | \"set-to\"\n                \"eval\": \"(row) =\u003e row.showTitle\"\n              },\n              {\n                \"type\": \"set-to\",\n                \"eval\": \"(row) =\u003e !row.title\",\n                \"configuration\": {\n                  \"value\": \"Placeholder Title\"\n                }\n              }\n            ]\n        }\n      ]\n    }\n    ```\n\n##### Set up\n\n1. Install the plugin `npm i --save @jaspero/fb-page-builder`\n2. Add the plugin module `PageBuilderModule` to your module\n\n#### Monaco Editor\n\nThis plugin registers a field `monaco` for rendering the Microsoft Monaco Editor.\n\n##### Dependencies\n\n|Library|Version|\n|----|----|\n|@monaco-editor/loader|1.0.0|\n|monaco-editor|^0.23.0|\n\n##### Set up\n\n1. Install the plugin, monaco editor and its loader `npm i --save @jaspero/fb-monaco-editor monaco-editor @monaco-editor/loader`\n2. Add the plugin module `MonacoEditorModule` to your module\n3. Add the following to the `assets` array in `angular.json`\n    ```json\n    {\n        \"glob\": \"**/*\",\n        \"input\": \"node_modules/ngx-monaco-editor/assets/monaco\",\n        \"output\": \"./assets/monaco/\"\n    }\n    ```\n   \n\n## Development\n\n### Running locally\n\n1. Install dependencies `npm ci`\n2. Run the app with `npm start`\n\n### Creating a plugin\n\n1. Run `ng g library [plugin-name] --prefix=\"fb-[library-prefix]\"`\n2. Add `@jaspero/` prefix in the projects `package.json`\n3. Add a `release` property as well. Example from `tincymce` plugin.\n    ```json\n      \"release\": {\n        \"pkgRoot\": \"../../dist/@jaspero/fb-tinymce\",\n        \"branch\": \"master\",\n        \"verifyConditions\": [\n          \"@semantic-release/changelog\",\n          \"@semantic-release/npm\",\n          \"@semantic-release/git\"\n        ],\n        \"prepare\": [\n          \"@semantic-release/changelog\",\n          \"@semantic-release/npm\",\n          \"@semantic-release/git\"\n        ],\n        \"publish\": [\n          \"@semantic-release/npm\",\n          [\n            \"@semantic-release/github\",\n            {\n              \"assets\": [\n                \"dist/@jaspero/fb-tinymce\"\n              ]\n            }\n          ]\n        ],\n        \"plugins\": [\n          \"@semantic-release/commit-analyzer\",\n          \"@semantic-release/release-notes-generator\"\n        ]\n      }\n    ```\n4. Create `ng-package.prod.json` example from tinymce\n    ```json\n    {\n      \"$schema\": \"../../node_modules/ng-packagr/ng-package.schema.json\",\n      \"dest\": \"../../dist/@jaspero/fb-tinymce\",\n      \"lib\": {\n        \"entryFile\": \"src/public-api.ts\"\n      }\n    }\n    ```\n5. In `angular.json` extend the `architect.configurations.production` with `ng-package.prod.json`\n    ```json\n     \"configurations\": {\n       \"production\": {\n         \"tsConfig\": \"projects/page-builder/tsconfig.lib.prod.json\",\n         \"project\": \"projects/page-builder/ng-package.prod.json\"\n       }\n    }\n    ```\n6. If you need to register fields do it in the plugins module like this:\n    ```typescript\n    export class TinymceModule {\n      constructor(\n        private ctx: FormBuilderContextService\n      ) {\n        this.ctx.registerField(\n          'tinymce',\n          TinymceComponent\n        );\n      }\n    }\n    ```\n7. Add build scripts for the library in to the root `package.json`. Make sure to include the build command in the `build:library` script.\n8. Build the library and publish an initial version manually. This is required because since it's a scoped\npackage it needs to be explicitly flagged as public. You can do this by running `npm publish --access public` in `dist/@jaspero/[package-name]`.\n\n## License\n\nMIT © [Jaspero Ltd](mailto:info@jaspero.co)\n","funding_links":["https://github.com/sponsors/Jaspero"],"categories":["Table of contents"],"sub_categories":["Third Party Components"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJaspero%2Fschema-forms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FJaspero%2Fschema-forms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJaspero%2Fschema-forms/lists"}