Questionnaire

A multi-step questionnaire with single-choice, multiple-choice, freeform, and skippable questions.

Question 1 of 3
What should the agent build next?

Choose a direction or describe another task.

Installation

ng g @spartan-ng/cli:ui questionnaire

Usage

import { form, FormField, FormRoot, required } from '@angular/forms/signals';
import { HlmQuestionnaireImports } from '@spartan-ng/helm/questionnaire';
<form hlmQuestionnaire [formRoot]="form" class="mx-auto max-w-md" [items]="items" defaultItem="direction" shortcuts="letters">
  <div hlmQuestionnaireProgress></div>
  <fieldset hlmQuestionnaireItem name="direction" required [formField]="form.direction">
    <legend hlmQuestionnaireTitle>What should the agent build next?</legend>
    <p hlmQuestionnaireDescription>Choose a direction or describe another task.</p>
    <div hlmQuestionnaireChoices>
      <label hlmQuestionnaireChoice value="tool-calls">
        <span class="font-medium">Tool call timeline</span>
        <span hlmQuestionnaireChoiceDescription>Show what the agent ran and what came back.</span>
      </label>
      <input hlmQuestionnaireInput aria-label="Another agent feature" placeholder="Describe another feature…" />
    </div>
    <p hlmQuestionnaireError></p>
  </fieldset>
  <div hlmQuestionnaireActions>
    <button hlmQuestionnairePrevious>Previous</button>
    <button hlmQuestionnaireSkip>Skip</button>
    <button hlmQuestionnaireNext>Next</button>
    <button hlmQuestionnaireSubmit>Save plan</button>
  </div>
</form>

Pass items for ordered progress, shortcuts, and navigation. Bind each item with [formField] and read answers from the signal model on submit. Native FormData still works because the inputs stay in the form.

Examples

Multiple Selection

Use multiple for an item that accepts more than one fixed answer.

What context should the agent inspect?

Select every source that may affect the implementation.

Freeform Answer

Compose hlmQuestionnaireInput with fixed choices when the user can provide another answer.

How should the agent approach this refactor?

Choose a strategy or write a more specific instruction.

Explicit Skip

Add hlmQuestionnaireSkip when an optional item may be intentionally left unanswered.

Question 1 of 3
What kind of change is this?

Choose the category that best describes the work.

Shortcuts

Assign a letter or number key to each answer with shortcuts .

What should the agent do next?

Use the displayed shortcut or navigate with the keyboard.

Custom Validation

Use Signal Forms required() and validate() to return to an invalid item and present its error.

How much detail should the answer include?

Choose the response depth.

1 / 2

Controlled

Control the active item from host state with [(item)] .

Current checkpoint: Change scope

Question 1 of 3
What may the agent change?

The host stores the active checkpoint while Questionnaire navigates.

Resume

Restore a saved active item and default answers, then reset changes back to that saved state.

Question 2 of 3
How should the migration be verified?

These checks were selected during the previous session.

Conditional Items

Disable items that do not apply to the user's earlier answers.

Question 1 of 2
Where should the agent run?

Cloud runs add an environment question to this flow.

Navigation State

Read item status from statusChange to opt into disabled navigation and custom action styling.

Question 1 of 2
What may the agent modify?

Next is intentionally disabled until an answer is selected.

Custom Progress

Use progress state to build a custom progress indicator.

Checkpoint 1 of 4
How large is the change?

Animated Items

Animate the active item while keeping progress and navigation stationary.

Question 1 of 3
What should the agent do?

Choose the task for this run.

Card

Compose Questionnaire with Card slots while keeping the question title and description semantic.

What should the agent work on?

Choose the task that should be handled next.

Question 1 of 2

Dialog

Compose Questionnaire inside a Dialog while keeping cancellation and dismissal host-owned.

Brain API

BrnQuestionnaireChoiceInput

Selector: input[brnQuestionnaireChoiceInput]

ExportAs: brnQuestionnaireChoiceInput

BrnQuestionnaireChoiceLabel

Selector: [brnQuestionnaireChoiceLabel]

ExportAs: brnQuestionnaireChoiceLabel

BrnQuestionnaireChoiceShortcut

Selector: [brnQuestionnaireChoiceShortcut]

ExportAs: brnQuestionnaireChoiceShortcut

BrnQuestionnaireChoice

Selector: label[brnQuestionnaireChoice]

ExportAs: brnQuestionnaireChoice

Inputs

PropTypeDefaultDescription
value* (required) string --
disabled boolean false -
defaultChecked boolean false -
checked boolean | undefined undefined -

Outputs

PropTypeDefaultDescription
checkedChange Event --

BrnQuestionnaireChoices

Selector: [brnQuestionnaireChoices]

ExportAs: brnQuestionnaireChoices

BrnQuestionnaireDescription

Selector: [brnQuestionnaireDescription]

ExportAs: brnQuestionnaireDescription

Inputs

PropTypeDefaultDescription
id string | undefined undefined -

BrnQuestionnaireError

Selector: [brnQuestionnaireError]

ExportAs: brnQuestionnaireError

Inputs

PropTypeDefaultDescription
id string | undefined undefined -
requiredMessage string 'Choose an answer to continue.' -
optionalMessage string 'Choose an answer or skip this question.' -

BrnQuestionnaireInput

Selector: input[brnQuestionnaireInput]

ExportAs: brnQuestionnaireInput

Inputs

PropTypeDefaultDescription
type BrnQuestionnaireInputType 'text' -
disabled boolean false -
value string | undefined undefined -
defaultValue string | undefined undefined -

BrnQuestionnaireItem

Selector: fieldset[brnQuestionnaireItem]

ExportAs: brnQuestionnaireItem

Inputs

PropTypeDefaultDescription
name* (required) string - `[formField]` overwrites `name` / `required` / `disabled` with field state. Prefer the host attributes so questionnaire identity and flow stay stable.
multiple boolean false -
required boolean false -
disabled boolean false -
itemInvalid boolean false Do not alias this to `invalid` — `[formField]` would bind field.invalid() and mark empty required items invalid before the user interacts.
aria-describedby string | undefined undefined -
aria-keyshortcuts string | undefined undefined -

Outputs

PropTypeDefaultDescription
statusChange BrnQuestionnaireItemStatus --

BrnQuestionnaireNext

Selector: button[brnQuestionnaireNext]

ExportAs: brnQuestionnaireNext

Inputs

PropTypeDefaultDescription
disabled boolean false -

BrnQuestionnairePrevious

Selector: button[brnQuestionnairePrevious]

ExportAs: brnQuestionnairePrevious

Inputs

PropTypeDefaultDescription
disabled boolean false -

BrnQuestionnaireProgress

Selector: [brnQuestionnaireProgress]

ExportAs: brnQuestionnaireProgress

Inputs

PropTypeDefaultDescription
aria-label string 'Questionnaire progress' -
valueText string 'Question %current of %total' -

BrnQuestionnaireSkip

Selector: button[brnQuestionnaireSkip]

ExportAs: brnQuestionnaireSkip

Inputs

PropTypeDefaultDescription
disabled boolean false -

BrnQuestionnaireSubmit

Selector: button[brnQuestionnaireSubmit]

ExportAs: brnQuestionnaireSubmit

Inputs

PropTypeDefaultDescription
disabled boolean false -

BrnQuestionnaireTitle

Selector: legend[brnQuestionnaireTitle]

ExportAs: brnQuestionnaireTitle

BrnQuestionnaire

Selector: form[brnQuestionnaire]

ExportAs: brnQuestionnaire

Inputs

PropTypeDefaultDescription
items readonly BrnQuestionnaireItemDefinition[] | undefined undefined -
defaultItem string | undefined undefined -
shortcuts BrnQuestionnaireShortcutMode | null null -
noValidate boolean true -
item string | null null -

Outputs

PropTypeDefaultDescription
itemChange string | null null -

Helm API

HlmQuestionnaireActions

Selector: [hlmQuestionnaireActions],hlm-questionnaire-actions

ExportAs: hlmQuestionnaireActions

HlmQuestionnaireChoiceDescription

Selector: [hlmQuestionnaireChoiceDescription]

ExportAs: hlmQuestionnaireChoiceDescription

HlmQuestionnaireChoice

Selector: label[hlmQuestionnaireChoice]

ExportAs: hlmQuestionnaireChoice

Inputs

PropTypeDefaultDescription
value* (required) string --
disabled boolean false -
defaultChecked boolean false -
checked boolean | undefined undefined -

Outputs

PropTypeDefaultDescription
checkedChange Event --

HlmQuestionnaireChoices

Selector: [hlmQuestionnaireChoices]

ExportAs: hlmQuestionnaireChoices

HlmQuestionnaireDescription

Selector: [hlmQuestionnaireDescription]

ExportAs: hlmQuestionnaireDescription

Inputs

PropTypeDefaultDescription
id string | undefined undefined -

HlmQuestionnaireError

Selector: [hlmQuestionnaireError]

ExportAs: hlmQuestionnaireError

Inputs

PropTypeDefaultDescription
message string | undefined undefined Custom error copy; falls back to the built-in required/optional message when unset.
id string | undefined undefined -
requiredMessage string 'Choose an answer to continue.' -
optionalMessage string 'Choose an answer or skip this question.' -

HlmQuestionnaireInput

Selector: input[hlmQuestionnaireInput]

ExportAs: hlmQuestionnaireInput

Inputs

PropTypeDefaultDescription
type BrnQuestionnaireInputType 'text' -
disabled boolean false -
value string | undefined undefined -
defaultValue string | undefined undefined -

HlmQuestionnaireItem

Selector: fieldset[hlmQuestionnaireItem]

ExportAs: hlmQuestionnaireItem

Inputs

PropTypeDefaultDescription
name* (required) string - `[formField]` overwrites `name` / `required` / `disabled` with field state. Prefer the host attributes so questionnaire identity and flow stay stable.
multiple boolean false -
required boolean false -
disabled boolean false -
itemInvalid boolean false Do not alias this to `invalid` — `[formField]` would bind field.invalid() and mark empty required items invalid before the user interacts.
aria-describedby string | undefined undefined -
aria-keyshortcuts string | undefined undefined -

Outputs

PropTypeDefaultDescription
statusChange BrnQuestionnaireItemStatus --

HlmQuestionnaireNext

Selector: button[hlmQuestionnaireNext]

ExportAs: hlmQuestionnaireNext

Inputs

PropTypeDefaultDescription
variant ButtonVariants['variant'] 'default' -
size ButtonVariants['size'] 'default' -
disabled boolean false -

HlmQuestionnairePrevious

Selector: button[hlmQuestionnairePrevious]

ExportAs: hlmQuestionnairePrevious

Inputs

PropTypeDefaultDescription
variant ButtonVariants['variant'] 'outline' -
size ButtonVariants['size'] 'default' -
disabled boolean false -

HlmQuestionnaireProgress

Selector: [hlmQuestionnaireProgress]

ExportAs: hlmQuestionnaireProgress

Inputs

PropTypeDefaultDescription
aria-label string 'Questionnaire progress' -
valueText string 'Question %current of %total' -

HlmQuestionnaireSkip

Selector: button[hlmQuestionnaireSkip]

ExportAs: hlmQuestionnaireSkip

Inputs

PropTypeDefaultDescription
variant ButtonVariants['variant'] 'outline' -
size ButtonVariants['size'] 'default' -
disabled boolean false -

HlmQuestionnaireSubmit

Selector: button[hlmQuestionnaireSubmit]

ExportAs: hlmQuestionnaireSubmit

Inputs

PropTypeDefaultDescription
variant ButtonVariants['variant'] 'default' -
size ButtonVariants['size'] 'default' -
disabled boolean false -

HlmQuestionnaireTitle

Selector: legend[hlmQuestionnaireTitle]

ExportAs: hlmQuestionnaireTitle

HlmQuestionnaire

Selector: form[hlmQuestionnaire]

ExportAs: hlmQuestionnaire

Inputs

PropTypeDefaultDescription
items readonly BrnQuestionnaireItemDefinition[] | undefined undefined -
defaultItem string | undefined undefined -
item string | null null -
shortcuts BrnQuestionnaireShortcutMode | null null -
noValidate boolean true -

Outputs

PropTypeDefaultDescription
itemChange string | null --
Radio Group Progress