Angular Signature Pad | ng-hub-ui-signature
Accessible Angular signature field that stores mouse, touch and pen input as scalable SVG form values, with undo, redo and PNG export.
API reference
Here's the full contract for signature: everything you can bind, listen to, project and theme, gathered in one place. Wire up what you need and style what you want — it's standalone and signal-friendly.
Inputs
Dial signature in with 18 inputs. Bind them like any Angular @Input.
| Name | Type | Default | Description |
|---|---|---|---|
label | string | '' | Visible field label, and the accessible name of the drawing surface: the canvas is wired to it with aria-labelledby, so the announced name cannot drift from the visible text. Clicking it focuses the surface. |
formText | string | '' | Helper text for the field. Where it is shown is [formTextType]'s business: below the drawing surface by default, or behind a question mark at the end of the label row. A projected <ng-template hubFormText> replaces the string in the block below, and keeps its place there even in tooltip mode, because a tooltip takes a string and would drop the markup. |
formTextType | 'bottom' | 'tooltip' | 'bottom' | Where [formText] is shown. bottom puts it under the drawing surface, where it is read without being asked for; tooltip hangs it behind a question mark at the end of the label row, for anything longer than a sentence. The mark is rendered whenever there is helper text, including on a bare surface named only by [ariaLabel]. Its bubble is appended to <body>, so tooltip mode also needs the ng-hub-ui-utils tooltip stylesheet. |
height | number | 160 | Logical canvas height in CSS pixels. |
strokeColor | string | 'currentColor' | Colour recorded in new SVG strokes. The currentColor default is resolved against the drawing surface before the stroke is captured, so the archive stores a concrete colour rather than a keyword the canvas cannot parse. |
strokeWidth | number | 2 | Base width recorded in new SVG strokes. |
readonly | boolean | false | Prevents drawing while preserving the signature. |
controls | boolean | true | Shows clear, undo and redo actions. |
labels | Partial<HubSignatureLabels> | {} | Overrides the application-wide translated action labels for this field only. |
ariaLabel | string | '' | Accessible name for a surface with no visible [label]. A field that has a label takes its name from that label through aria-labelledby, so the two cannot disagree, and this input is not consulted there. Left empty it resolves through [labels], then HUBUI.SIGNATURE.ARIA_LABEL, then the English fallback Signature. |
labelType | HubLabelType | 'stacked' | How the label sits against the field: 'stacked' or 'horizontal'. 'floating' falls back to stacked, as it does on the family's other non-text fields — a floating label reuses the space an empty text control's value would occupy and is driven by :placeholder-shown, which a canvas has neither of. |
classlist | string | '' | Extra classes applied to the host element, <hub-signature> itself — not to the drawing surface, which keeps its own .hub-signature__canvas. Style the canvas from a descendant selector, or theme it through the --hub-signature-* slots. |
formControlName | string | — | Name of the control inside the surrounding form group. Inherited from HubFormControl, which declares it as an input of its own, so ReactiveFormsModule must still be imported where the field is used: without the directive nothing raises an error and the field simply never syncs with the form. |
required | boolean | null | null | Renders the required asterisk and sets aria-required on the surface. Two-way. On a reactive binding it is derived from the control's validators and kept in step with them, so binding it by hand there is overwritten and warns in dev mode; set it explicitly only on a template-driven or unbound field. |
disabled | boolean | false | Refuses drawing, disables the action row and sets aria-disabled. Two-way, and also written by setDisabledState(). On a formControlName field prefer control.disable() / control.enable(): Angular's own directive declares a disabled input too, so a binding reaches both and leaves two sources of truth for one state. |
showValid | boolean | false | Opt-in success state. A touched and valid field takes the success border and ring on the drawing surface, and renders [validFeedback] if one is given. Defaults to the global provideHubForms({ showValid }); the success state is never automatic. Has no effect while the field is invalid. |
validFeedback | string | null | null | Success message shown under the field. Needs both halves of the opt-in: [showValid] on, and a control that is touched and valid. |
invalidFeedbackTemplateFn | ((key: string, value: any) => string) | null | null | Per-field override for the invalid-feedback message builder, ahead of the global provideHubForms() one. For markup rather than a string, project an <ng-template hubValidationError key="required"> instead — the template wins over both. |
Outputs
React to what signature does — 5 events to hook your logic onto.
| Name | Type | Description |
|---|---|---|
valueChange | OutputEmitterRef<string> | Emits the SVG value after a user-originated change — a committed stroke, clear(), undo() or redo() — and the empty string once nothing is left. A programmatic write through writeValue() or fromStrokes() emits nothing, so this never echoes the form back at itself. |
drawStart | OutputEmitterRef<HubSignatureDrawEvent> | Emitted when a user begins a stroke, with the pointer or the keyboard. Lets a host react to drawing activity — pausing an autosave, arming a submit button — without polling the value. HubSignatureDrawEvent is PointerEvent | KeyboardEvent; narrow with instanceof when the input device matters. |
drawEnd | OutputEmitterRef<HubSignatureDrawEvent> | Emitted when a user finishes a stroke, after it is committed. Fires only for user drawing, so a programmatic write never looks like one, and a stroke that was cancelled rather than finished — pointercancel, Escape, focus leaving the surface — emits nothing. |
disabledChange | OutputEmitterRef<boolean> | The write half of the two-way [(disabled)]. It also fires when Angular calls setDisabledState() — that is, when the bound control is disabled or enabled — so a host binding [(disabled)] on a reactive field sees the control drive it. |
requiredChange | OutputEmitterRef<boolean | null> | The write half of the two-way [(required)]. On a reactive binding it fires whenever the control's status changes and its required validator has been added or removed, which is where the value comes from there. |
Methods
Drive signature from code — 10 methods on its programmatic surface.
| Name | Signature | Returns | Description |
|---|---|---|---|
clear | clear(): void | — | Removes every stroke and emits the empty value. |
undo | undo(): void | — | Removes the most recently committed stroke. |
redo | redo(): void | — | Restores the most recently undone stroke. |
cancelStroke | cancelStroke(event?: PointerEvent): void | — | Throws away the stroke in progress without reporting it. Bound to pointercancel, and the path taken by Escape and by focus leaving the surface: a cancelled interaction must not reach the form value. |
toSvg | toSvg(): string | — | Returns the scalable SVG form value. |
toDataUrl | toDataUrl(type?: string): string | — | Exports the rendered signature, PNG by default. |
resizeCanvas | resizeCanvas(): void | — | Re-measures the element, rebuilds the device-pixel bitmap and repaints. [height] runs it for you; call it by hand when the field becomes visible after being laid out at zero width — inside a closed modal, an inactive tab, a collapsed accordion — since the one automatic measurement happens at first render and there is no ResizeObserver. Call it while the field is empty: strokes keep the coordinates they were captured in, so re-measuring a signed field to a new width reframes the signature and leaves toSvg() emitting a viewBox that no longer matches the geometry inside it. |
isEmpty | isEmpty(): boolean | boolean | Whether anything has been drawn. Lets a form validate the field without parsing the serialized SVG, which was previously the only way to ask. |
toStrokes | toStrokes(): HubSignatureStroke[] | HubSignatureStroke[] | The committed strokes as structured geometry. toSvg() remains the canonical form value — this is for callers that need the points themselves, such as replaying a signature or migrating from a library that stored point groups. |
fromStrokes | fromStrokes(strokes: readonly HubSignatureStroke[]): void | — | Repaints the field from structured geometry. A programmatic write, like writeValue(): it does not report a user change to Angular forms. |
Templates
Make it yours — 2 template slots let you project custom markup.
| Name | Description | Example |
|---|---|---|
hubFormText | Helper text as markup, replacing the [formText] string in the block under the drawing surface. Both are exports of ng-hub-ui-forms, so the markup written for a hub-input moves here unchanged. It keeps its place in the block even under formTextType="tooltip", because the tooltip takes a string and would drop the markup — the mark still opens with the [formText] string. | <ng-template hubFormText>Sign with a pointer, or carry the pen with <kbd>Space</kbd>.</ng-template> |
hubValidationError | The message for one validation error key, ahead of both [invalidFeedbackTemplateFn] and the global provideHubForms() builder. Only the key it names is overridden; every other error still falls back to the builder. The error payload arrives as the implicit context value. | <ng-template hubValidationError key="required">Sign before filing the contract.</ng-template> |