diff --git a/README.md b/README.md index 22c205ab..692d2a75 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ For quick prototyping or simple projects, you can use a CDN: ``` +This exposes the `window.FormeoEditor` and `window.FormeoRenderer` globals. For production, pin a version (e.g. `formeo@5.13.1`) instead of `@latest` and add an `integrity` hash with `crossorigin="anonymous"` — see [Installation](docs/renderer/renderer.md#installation) in the renderer docs. + ## Usage ### Basic Setup @@ -171,7 +173,7 @@ editor.whenReady().then(() => { Formeo can be integrated with popular frontend frameworks: - **[React Integration Guide](https://github.com/Draggable/formeo/blob/main/docs/react-integration.md)** - Custom hooks, functional components, and Context API patterns -- **[Angular Integration Guide](https://github.com/Draggable/formeo/blob/main/docs/angular-integration.md)** - Services, components, and standalone patterns +- **[Angular Integration Guide](https://github.com/Draggable/formeo/blob/main/docs/angular-integration.md)** - A standalone component, SSR-safe setup, and reading submitted answers ## Theming @@ -214,6 +216,7 @@ Comprehensive documentation is available in the [docs](https://github.com/Dragga - **[Actions](https://github.com/Draggable/formeo/blob/main/docs/options/actions/README.md)** - Action handlers - **[Editor API](https://github.com/Draggable/formeo/blob/main/docs/editor/README.md)** - Editor methods and properties - **[TypeScript](https://github.com/Draggable/formeo/blob/main/docs/typescript.md)** - Shipped type definitions and typed events +- **[Using a CSS Framework](https://github.com/Draggable/formeo/blob/main/docs/css-frameworks.md)** - Style rendered forms with Bootstrap or any framework - **[Build Tools](https://github.com/Draggable/formeo/blob/main/docs/tools/README.md)** - Development and build utilities ## Development diff --git a/docs/README.md b/docs/README.md index 338330f9..198f0c7e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -124,4 +124,12 @@ Remove the rendered form from the page and stop its pagination (the page navigat Type definitions ship with the package; this page lists what is typed and how to narrow the before-hook details. +## [Using a CSS Framework](css-frameworks.md) + +The rendered markup and its classes, adding framework classes with `className`, and editor theming. + +## [Migrating from formBuilder](migrating-from-formbuilder.md) + +Batch-converting formBuilder form exports to Formeo's `formData` shape with `formBuilder2Formeo`. + ## [Build Tools](tools/) diff --git a/docs/angular-integration-example/README.md b/docs/angular-integration-example/README.md deleted file mode 100644 index 23fe94aa..00000000 --- a/docs/angular-integration-example/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Angular + Formeo Integration Example - -This directory holds only this README — there is no runnable Angular project here. - -The code samples referenced below (a standalone component, an injectable service, and module/config -wiring) live inline in `src/demo/js/frameworks/angular.js`, which the demo's "Angular" tab displays and -syntax-highlights. That file also explains why the demo simulates Angular patterns instead of loading -the real framework. - -For the full picture — the reasoning behind the simulation, and how to build a real Angular project -against Formeo — see [`docs/angular-integration.md`](../angular-integration.md). diff --git a/docs/angular-integration.md b/docs/angular-integration.md index 5357f898..e0b4c4b7 100644 --- a/docs/angular-integration.md +++ b/docs/angular-integration.md @@ -1,145 +1,147 @@ -# Angular + Formeo Integration Guide +# Angular + Formeo Integration -## Why the Demo Doesn't Load Real Angular v20 +A standalone Angular component that builds a form with `FormeoEditor`, previews it with `FormeoRenderer`, and +cleans up both on destroy. Verified against `formeo` 5.13.1; Angular 17+ syntax (standalone components, +`afterNextRender`). -The current Angular demo in `src/demo/js/frameworks/angular.js` simulates Angular patterns rather than loading the actual Angular framework for several technical reasons: - -### Technical Limitations - -1. **Dynamic ES Module Loading Complexity** - - Angular v20 requires complex module resolution - - Dependencies between @angular/core, @angular/common, @angular/platform-browser - - Browser ES module loading limitations - -2. **Bundle Size Impact** - - Angular v20 with dependencies is ~200KB+ minified - - Would significantly slow down demo loading - - Not suitable for quick demonstrations - -3. **Build Tool Requirements** - - Angular typically requires compilation (TypeScript → JavaScript) - - Template compilation and bundling - - Zone.js and other runtime dependencies - -4. **Runtime Environment Mismatch** - - Demo runs in vanilla browser environment - - Angular expects Node.js tooling ecosystem - -## Recommended Alternatives - -### Option 1: Complete Angular Project Example (Recommended) - -Create a separate Angular project that demonstrates real integration: +## Install ```bash -# Create new Angular project -ng new formeo-angular-example --routing --style=scss -cd formeo-angular-example - -# Install Formeo npm install formeo - -# Run development server -ng serve ``` -**Benefits:** -- Real Angular v20 environment -- Proper TypeScript support -- Full Angular CLI tooling -- Production-ready patterns - -### Option 2: Improved CDN Loading (Experimental) - -Update the demo to use Angular's standalone CDN bundles: - -```javascript -// Load Angular via CDN (Angular v20 standalone) -async function loadAngularFromCDN() { - const scripts = [ - 'https://unpkg.com/@angular/core@20/bundles/core.umd.js', - 'https://unpkg.com/@angular/common@20/bundles/common.umd.js', - 'https://unpkg.com/@angular/platform-browser@20/bundles/platform-browser.umd.js' - ]; - - for (const src of scripts) { - await loadScript(src); +Add the stylesheet in `angular.json`: + +```json +{ + "projects": { + "my-app": { + "architect": { + "build": { + "options": { + "styles": ["node_modules/formeo/dist/formeo.min.css", "src/styles.scss"] + } + } + } + } } } ``` -**Limitations:** -- Still complex dependency management -- Large download size -- Limited TypeScript support -- Not recommended for production +(Or `@import 'formeo/dist/formeo.min.css';` from a global stylesheet instead, if you'd rather not touch +`angular.json`.) -### Option 3: Web Components Approach - -Use Angular Elements to create a web component: +## The component ```typescript -// Create Angular Element -import { createCustomElement } from '@angular/elements'; -import { FormBuilderComponent } from './form-builder.component'; +import { Component, ElementRef, OnDestroy, ViewChild, afterNextRender } from '@angular/core' +import { FormeoEditor, FormeoRenderer } from 'formeo' + +@Component({ + selector: 'app-form-builder', + standalone: true, + template: ` +
+ +
+ `, +}) +export class FormBuilderComponent implements OnDestroy { + @ViewChild('editorRef', { static: true }) editorRef!: ElementRef + @ViewChild('previewRef', { static: true }) previewRef!: ElementRef + + private editor?: FormeoEditor + private renderer?: FormeoRenderer + + constructor() { + // afterNextRender only runs in the browser (see "Server-side rendering" below), after the view + // holding editorRef/previewRef has rendered. + afterNextRender(() => { + this.editor = new FormeoEditor({ editorContainer: this.editorRef.nativeElement }) + this.renderer = new FormeoRenderer({ + renderContainer: this.previewRef.nativeElement, + events: { + onSubmit: ({ event, userData }) => { + event.preventDefault() // formeo doesn't call this for you + console.log('submitted:', userData) + }, + }, + }) + }) + } + + preview(): void { + this.renderer?.render(this.editor?.formData) + } -const FormBuilderElement = createCustomElement(FormBuilderComponent, { injector }); -customElements.define('formeo-angular-builder', FormBuilderElement); + ngOnDestroy(): void { + this.editor?.destroy() + this.renderer?.destroy() + } +} ``` -**Benefits:** -- Can be loaded in any environment -- Encapsulated Angular functionality -- Framework-agnostic usage +Notes: -### Option 4: Stackblitz/CodeSandbox Integration +- `editorContainer`/`renderContainer` take an `Element` directly, so the `@ViewChild` refs' `.nativeElement` + work without a selector string. +- `editor.formData` is a getter — it always returns the editor's current form definition, so `preview()` just + reads it and hands it to `renderer.render()`. +- `editor.destroy()` and `renderer.destroy()` both exist and are safe to call more than once; call them in + `ngOnDestroy` so a routed-away component doesn't leak Sortable instances or DOM listeners. -Embed live Angular examples using online IDEs: +## Reading submitted answers -```html - -``` +The renderer's `onSubmit` event fires on the rendered `
`'s native `submit` event and hands you +`{ event, form, userData }`, where `userData` is the form's values keyed by field name (repeated names — checkbox +groups, multi-selects — become arrays). Formeo does not call `event.preventDefault()` itself, so call it yourself +if you're not doing a normal form POST. This fragment replaces the `onSubmit` handler inside the `FormeoRenderer` +constructor in the component above, and assumes an injected `HttpClient` (`private http = inject(HttpClient)`, +alongside `provideHttpClient()` in your app config): -## Current Demo Value +```typescript +events: { + onSubmit: ({ event, userData }) => { + event.preventDefault() + this.http.post('/api/submissions', userData).subscribe() + }, +} +``` -The current simulated demo is still valuable because it: +## Server-side rendering (SSR) -1. **Shows Integration Patterns**: Demonstrates proper Angular component structure -2. **Provides Copy-Paste Code**: Complete, working examples for real projects -3. **Educational Value**: Teaches Angular + Formeo integration concepts -4. **Performance**: Loads quickly without framework overhead +`FormeoEditor` and `FormeoRenderer` both touch `document`, so only construct them in the browser. `afterNextRender` +(used above) already guarantees this — Angular's server renderer never invokes its callback, so no extra guard is +needed. -## Implementation Recommendation +If you're not using `afterNextRender` (e.g. an older Angular version, or constructing Formeo somewhere other than +a component's injection context), guard the same code with `isPlatformBrowser`. This fragment replaces the +constructor's `afterNextRender(...)` call in the component above with an `ngAfterViewInit` lifecycle hook instead +(so the class also needs `implements AfterViewInit`, and the extra imports shown here): -For your use case, I recommend: +```typescript +import { AfterViewInit, Component, PLATFORM_ID, inject } from '@angular/core' +import { isPlatformBrowser } from '@angular/common' -1. **Keep the current demo** for quick pattern demonstration -2. **Add a complete Angular project** in `/docs/angular-integration-example/` -3. **Update the demo documentation** to clearly explain the simulation -4. **Add links to the full project** for developers who want real integration +export class FormBuilderComponent implements AfterViewInit { + private platformId = inject(PLATFORM_ID) -This approach provides both quick learning (demo) and complete implementation (separate project). + ngAfterViewInit(): void { + if (isPlatformBrowser(this.platformId)) { + this.editor = new FormeoEditor({ editorContainer: this.editorRef.nativeElement }) + } + } +} +``` -## Migration Path +## TypeScript -If you want to enhance the current demo: +Formeo ships its own type definitions (`dist/formeo.d.ts`, `dist/formeo.d.cts`) — nothing extra to install or +declare. See [TypeScript](typescript.md) for what's typed, including the editor and renderer constructors, their +options, and the `events` callbacks used above. -```javascript -// Enhanced demo with better Angular simulation -export async function loadAngularDemo(container) { - // 1. Show comprehensive setup instructions - // 2. Provide modern Angular v20 code examples - // 3. Demonstrate advanced patterns (signals, standalone components) - // 4. Include service-based architecture examples - // 5. Add TypeScript type definitions -} -``` +## The demo's Angular tab -The updated demo now includes: -- Modern Angular v20 patterns -- Standalone components -- Angular signals -- Service-based architecture -- Better TypeScript examples -- Comprehensive integration guide +The demo (`npm start`, then the Angular tab) shows the same construction pattern hand-rolled against plain DOM, +without loading Angular itself — see `src/demo/js/frameworks/angular.js`. It's there to demonstrate the pattern +quickly in the browser, not as a real Angular app; use the component above for that. diff --git a/docs/controls/README.md b/docs/controls/README.md index 429dbef2..7690f41e 100644 --- a/docs/controls/README.md +++ b/docs/controls/README.md @@ -26,7 +26,8 @@ const inputElement = { ## [Control Options](https://github.com/Draggable/formeo/tree/main/docs/options/controls) -See [Custom controls](custom-controls.md) for a full editor + renderer example. +See [Custom controls](custom-controls.md) for a full editor + renderer example, or +[Rich Text Editors](rich-text-editors.md) for a worked example wiring in CKEditor 5. ## Control Groups diff --git a/docs/controls/custom-controls.md b/docs/controls/custom-controls.md index 13c2c02c..e466acb9 100644 --- a/docs/controls/custom-controls.md +++ b/docs/controls/custom-controls.md @@ -208,3 +208,4 @@ would throw on. - [Custom Attribute Types](custom-attribute-types.md) - Attribute input types for a control's `attrs` - [Renderer: Custom Elements](../renderer/renderer.md#advanced-topics) - The renderer's `elements` option - [Control sets](#control-sets) - Add several fields at once +- [Rich Text Editors](rich-text-editors.md) - A full worked example: a CKEditor 5 control diff --git a/docs/controls/rich-text-editors.md b/docs/controls/rich-text-editors.md new file mode 100644 index 00000000..f3ba274b --- /dev/null +++ b/docs/controls/rich-text-editors.md @@ -0,0 +1,123 @@ +# Rich Text Editors + +A recipe for wiring a modern WYSIWYG editor into Formeo as a custom control, using +[CKEditor 5](https://ckeditor.com/ckeditor-5/) as the example. It builds on +[Custom Controls](custom-controls.md) — read that first for the full picture (defining a control, registering it, +rendering it, and getting its value into `userData`). This page only covers what's specific to a CDN-loaded rich +text editor: waiting for the script to load before initializing, and syncing the editor's HTML back to the +`