Spartans get ready! v1 is coming!
We are very close to our first stable release. Expect more announcements in the coming weeks. v1 was made possible by our partner Zerops.
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Autocomplete
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Data Table
- Date Picker
- Dialog
- Dropdown Menu
- Empty
- Form Field
- Hover Card
- Icon
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Label
- Menubar
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner (Toast)
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
Form Field
Display a form field to handle errors and hints.
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { HlmFormFieldImports } from '@spartan-ng/helm/form-field';
import { HlmInputImports } from '@spartan-ng/helm/input';
@Component({
selector: 'spartan-form-field-preview',
imports: [HlmInputImports, HlmFormFieldImports, ReactiveFormsModule],
template: `
<hlm-form-field>
<input class="w-80" hlmInput [formControl]="control" type="email" placeholder="Email" />
<hlm-hint>This is your email address.</hlm-hint>
<hlm-error>The email is required.</hlm-error>
</hlm-form-field>
`,
})
export class FormFieldPreview {
public control = new FormControl('', Validators.required);
}Installation
npx nx g @spartan-ng/cli:ui formfield
ng g @spartan-ng/cli:ui formfield
Usage
import { HlmFormFieldImports } from '@spartan-ng/helm/form-field';
import { HlmInputImports } from '@spartan-ng/helm/input';<hlm-form-field>
<input class="w-80" hlmInput type="email" placeholder="Email" />
<hlm-hint>This is your email address.</hlm-hint>
</hlm-form-field>Examples
Error
import { Component, type OnInit } from '@angular/core';
import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { HlmFormFieldImports } from '@spartan-ng/helm/form-field';
import { HlmInputImports } from '@spartan-ng/helm/input';
@Component({
selector: 'spartan-form-field-error',
imports: [ReactiveFormsModule, HlmFormFieldImports, HlmInputImports],
template: `
<hlm-form-field>
<input aria-label="Your Name" class="w-80" [formControl]="name" hlmInput type="text" placeholder="Your Name" />
<hlm-error>Your name is required</hlm-error>
</hlm-form-field>
`,
})
export class FormFieldErrorPreview implements OnInit {
public name = new FormControl('', Validators.required);
ngOnInit(): void {
this.name.markAsTouched();
}
}With Form
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { BrnSelectImports } from '@spartan-ng/brain/select';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmFormFieldImports } from '@spartan-ng/helm/form-field';
import { HlmInput } from '@spartan-ng/helm/input';
import { HlmSelectImports } from '@spartan-ng/helm/select';
@Component({
selector: 'spartan-form-field-form',
imports: [
ReactiveFormsModule,
HlmFormFieldImports,
HlmSelectImports,
HlmInput,
HlmSelectImports,
BrnSelectImports,
HlmButtonImports,
],
template: `
<form [formGroup]="form" class="space-y-6">
<hlm-form-field>
<input
aria-label="Your Name"
formControlName="name"
class="w-80"
hlmInput
type="text"
placeholder="Your Name"
/>
<hlm-error>Your name is required</hlm-error>
</hlm-form-field>
<hlm-form-field>
<brn-select class="inline-block" placeholder="Select some fruit" formControlName="fruit">
<hlm-select-trigger class="w-80">
<hlm-select-value />
</hlm-select-trigger>
<hlm-select-content>
<hlm-select-label>Fruits</hlm-select-label>
@for (option of options; track option.value) {
<hlm-option [value]="option.value">{{ option.label }}</hlm-option>
}
</hlm-select-content>
</brn-select>
<hlm-error>The fruit is required</hlm-error>
</hlm-form-field>
<button type="submit" hlmBtn>Submit</button>
</form>
`,
})
export class FormFieldFormPreview {
private readonly _formBuilder = inject(FormBuilder);
public form = this._formBuilder.group({
name: ['', Validators.required],
fruit: ['', Validators.required],
});
public options = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'blueberry', label: 'Blueberry' },
{ value: 'grapes', label: 'Grapes' },
{ value: 'pineapple', label: 'Pineapple' },
];
}Changing when error messages are shown
By default, these error messages are shown when the control is invalid and the user has interacted with (touched) the element or the parent form has been submitted. If you wish to override this behavior (e.g. to show the error as soon as the invalid control is dirty or when a parent form group is invalid), you can use the ErrorStateMatcher provider.
For convenience, ShowOnDirtyErrorStateMatcher is available in order to globally cause input errors to show when the input is dirty and invalid
providers: [
{ provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher }
]import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { ErrorStateMatcher, ShowOnDirtyErrorStateMatcher } from '@spartan-ng/brain/forms';
import { BrnSelectImports } from '@spartan-ng/brain/select';
import { HlmButtonImports } from '@spartan-ng/helm/button';
import { HlmFormFieldImports } from '@spartan-ng/helm/form-field';
import { HlmInput } from '@spartan-ng/helm/input';
import { HlmSelectImports } from '@spartan-ng/helm/select';
@Component({
selector: 'spartan-form-field-form-dirty',
imports: [
ReactiveFormsModule,
HlmFormFieldImports,
HlmSelectImports,
HlmInput,
HlmSelectImports,
BrnSelectImports,
HlmButtonImports,
],
template: `
<form [formGroup]="form" class="space-y-6">
<hlm-form-field>
<input
aria-label="Your Name"
formControlName="name"
class="w-80"
hlmInput
type="text"
placeholder="Your Name"
/>
<hlm-error>Your name is required</hlm-error>
</hlm-form-field>
<hlm-form-field>
<brn-select class="inline-block" placeholder="Select some fruit" formControlName="fruit">
<hlm-select-trigger class="w-80">
<hlm-select-value />
</hlm-select-trigger>
<hlm-select-content>
<hlm-select-label>Fruits</hlm-select-label>
@for (option of options; track option.value) {
<hlm-option [value]="option.value">{{ option.label }}</hlm-option>
}
</hlm-select-content>
</brn-select>
<hlm-error>The fruit is required</hlm-error>
</hlm-form-field>
<button type="submit" hlmBtn>Submit</button>
</form>
`,
providers: [{ provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher }],
})
export class FormFieldFormWithDirtyPreview {
private readonly _formBuilder = inject(FormBuilder);
public form = this._formBuilder.group({
name: ['', Validators.required],
fruit: ['', Validators.required],
});
public options = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'blueberry', label: 'Blueberry' },
{ value: 'grapes', label: 'Grapes' },
{ value: 'pineapple', label: 'Pineapple' },
];
}
export const providerShowOnDirtyErrorStateMatcher = `
providers: [
{ provide: ErrorStateMatcher, useClass: ShowOnDirtyErrorStateMatcher }
]
`;Helm API
HlmError
Selector: hlm-error
Inputs
| Prop | Type | Default | Description |
|---|---|---|---|
| class | ClassValue | - | - |
HlmFormField
Selector: hlm-form-field
Inputs
| Prop | Type | Default | Description |
|---|---|---|---|
| class | ClassValue | - | - |
HlmHint
Selector: hlm-hint
Inputs
| Prop | Type | Default | Description |
|---|---|---|---|
| class | ClassValue | - | - |