Checkbox
Checkboxes provide a straightforward way for users to make multiple selections from a set of options, whether they're choosing preferences, confirming actions, or filtering content.
import {CheckboxModule} from "@qualcomm-ui/angular/checkbox"Examples
Simple
The simple API bundles the subcomponents together into a single directive.
<label label="Label" q-checkbox></label>
Child Directives
Provide child directives to customize specific elements while keeping the simple API's default structure.
<label q-checkbox>
<div q-checkbox-label>Label</div>
</label>
Composite
Build with the composite API for granular control. This API requires you to provide each subcomponent, but gives you full control over the structure and layout.
<label q-checkbox-root>
<input q-checkbox-hidden-input />
<div q-checkbox-control></div>
<span q-checkbox-label>Label</span>
</label>
States
Based on the inputs, the checkbox will render as checked, indeterminate, or unchecked (default). The controlled value, if supplied as true, takes precedence over the indeterminate input.
<label defaultChecked label="Label" q-checkbox></label>
<label label="Label" q-checkbox></label>
<label indeterminate label="Label" q-checkbox></label>
Disabled State
When disabled is true, the checkbox becomes non-interactive and is typically rendered with reduced opacity to indicate its unavailable state.
<label defaultChecked disabled label="Label" q-checkbox></label>
<label disabled label="Label" q-checkbox></label>
<label disabled indeterminate label="Label" q-checkbox></label>
Sizes
The checkbox supports three sizes: sm, md (default), and lg.
import {Component} from "@angular/core"
import {CheckboxModule} from "@qualcomm-ui/angular/checkbox"
@Component({
imports: [CheckboxModule],
selector: "checkbox-sizes-demo",
template: `
<div class="flex flex-col items-start gap-4">
<label label="Small (sm)" q-checkbox size="sm"></label>
<label label="Medium (md)" q-checkbox size="md"></label>
<label label="Large (lg)" q-checkbox size="lg"></label>
</div>
`,
})
export class CheckboxSizesDemo {}Hint
Add a hint to provide additional context below the checkbox.
<label
hint="Keep me signed in for 30 days"
label="Remember me"
q-checkbox
></label>
Group
Use CheckboxGroup to group related checkboxes with a shared label, hint text, and validation state.
Forms
Template Forms
- When using template forms, add the required attribute to enable validation
- The errorText input (or
q-checkbox-error-textdirective) displays automatically when the field is invalid and the user has interacted with it
<label
errorText="Please accept the Terms of Service to continue"
label="Accept Terms of Service"
name="acceptTerms"
q-checkbox
required
[(ngModel)]="acceptTerms"
></label>
Reactive Forms
Use Reactive Forms instead of Template-driven Forms for better control over form state and validation.
import {Component, inject} from "@angular/core"
import {FormBuilder, ReactiveFormsModule, Validators} from "@angular/forms"
import {ButtonModule} from "@qualcomm-ui/angular/button"
import {CheckboxModule} from "@qualcomm-ui/angular/checkbox"
@Component({
imports: [CheckboxModule, ReactiveFormsModule, ButtonModule],
selector: "checkbox-reactive-forms",
template: `
<form
class="flex w-56 flex-col gap-2"
[formGroup]="form"
(ngSubmit)="onSubmit()"
>
<label
errorText="Please accept the Terms of Service to continue"
formControlName="acceptTerms"
label="Accept Terms of Service"
q-checkbox
></label>
<div class="mt-1 grid grid-cols-2 grid-rows-1 gap-3">
<button
emphasis="primary"
q-button
size="sm"
type="button"
variant="outline"
(click)="reset()"
>
Reset
</button>
<button
emphasis="primary"
q-button
size="sm"
type="submit"
variant="fill"
>
Submit
</button>
</div>
</form>
`,
})
export class CheckboxReactiveFormsDemo {
private fb = inject(FormBuilder)
form = this.fb.group({
acceptTerms: [false, Validators.requiredTrue],
})
onSubmit() {
if (this.form.valid) {
console.log("Form submitted:", {
...this.form.value,
})
}
}
reset() {
this.form.reset({
acceptTerms: false,
})
}
}Multi-Field Validation
Reactive forms let you validate across multiple fields simultaneously, perfect for complex business rules like date ranges or dependent field relationships.
import {Component, inject, signal} from "@angular/core"
import {
type AbstractControl,
FormBuilder,
FormGroup,
ReactiveFormsModule,
type ValidationErrors,
type ValidatorFn,
} from "@angular/forms"
import {ButtonModule} from "@qualcomm-ui/angular/button"
import {CheckboxModule} from "@qualcomm-ui/angular/checkbox"
import {ErrorTextComponent} from "@qualcomm-ui/angular/input"
// Custom validator that requires at least one checkbox to be selected
function atLeastOneSelectedValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!(control instanceof FormGroup)) {
return null
}
const values = Object.values(control.value)
const hasSelection = values.some((value) => value === true)
return hasSelection ? null : {atLeastOneRequired: true}
}
}
@Component({
imports: [
CheckboxModule,
ReactiveFormsModule,
ButtonModule,
ErrorTextComponent,
],
selector: "checkbox-reactive-forms-advanced-validation",
template: `
<form
class="flex w-72 flex-col gap-2"
[formGroup]="form"
(ngSubmit)="onSubmit()"
>
<fieldset class="m-0 border-0 p-0">
<legend class="text-neutral-primary font-heading-xxs mb-1 p-0">
Select your interests (at least one required):
</legend>
<div class="flex flex-col gap-1" formGroupName="interests">
<label
formControlName="technology"
label="Technology"
q-checkbox
></label>
<label formControlName="sports" label="Sports" q-checkbox></label>
<label formControlName="music" label="Music" q-checkbox></label>
<label formControlName="travel" label="Travel" q-checkbox></label>
@if (interestsGroup.invalid && interestsGroup.touched) {
<div q-error-text>Please select at least one interest</div>
}
</div>
</fieldset>
<div class="mt-1 grid grid-cols-2 grid-rows-1 gap-3">
<button
emphasis="primary"
q-button
size="sm"
type="button"
variant="outline"
(click)="reset()"
>
Reset
</button>
<button
emphasis="primary"
q-button
size="sm"
type="submit"
variant="fill"
>
Submit
</button>
</div>
</form>
`,
})
export class CheckboxAdvancedValidationDemo {
readonly formSubmitted = signal<boolean>(false)
private fb = inject(FormBuilder)
form = this.fb.group({
interests: this.fb.group(
{
music: false,
sports: false,
technology: false,
travel: false,
},
{validators: atLeastOneSelectedValidator()},
),
})
get interestsGroup() {
return this.form.get("interests")!
}
get selectedInterests() {
const interests = this.interestsGroup.value
return Object.entries(interests)
.filter(([_, selected]) => selected)
.map(([interest, _]) => interest)
}
onSubmit() {
this.formSubmitted.set(true)
if (this.form.valid) {
console.log("Form submitted:", {
...this.form.value,
selectedInterests: this.selectedInterests,
})
} else {
this.form.markAllAsTouched()
}
}
reset() {
this.form.reset({
interests: {
music: false,
sports: false,
technology: false,
travel: false,
},
})
this.formSubmitted.set(false)
}
}Composition
The composite elements are only intended to be used as direct descendants of the q-checkbox directive.
<!-- Won't work alone ❌ -->
<input q-checkbox-hidden-input />
<div q-checkbox-control></div>
<!-- Works as expected ✅ -->
<div q-checkbox-root>
<input q-checkbox-hidden-input />
<div q-checkbox-control></div>
</div>Accessibility
- The root directive should always be attached to a
<label>element for accessibility:- it makes the entire component clickable, not just the input/control.
- it provides implicit association between the text and input element, which is ideal for screen readers.
- Always use the associated label or q-checkbox-label directive when authoring the text label. This is automatically associated with the input element.
- If you omit the visible label, give the checkbox an accessible name with
aria-labeloraria-labelledbyon the simpleq-checkbox. If you use the composite API, providearia-labeloraria-labelledbyon theq-checkbox-hidden-inputelement. Avoid[attr.aria-label]and[attr.aria-labelledby]; use[aria-label]or[aria-labelledby]for dynamic values.
<label aria-label="Subscribe to updates" q-checkbox></label>
Explorer
API
q-checkbox
The q-checkbox directive extends the q-checkbox-root directive with the following properties:
stringstringboolean'ltr' | 'rtl'
booleanstring<label q-checkbox>
<div q-checkbox-error-text>...</div>
</label>
() =>
| Node
| ShadowRoot
| Document
string<label q-checkbox>
<div q-checkbox-hint>...</div>
</label>
stringbooleanindeterminate state.stringaria-label or aria-labelledby attribute on the q-checkbox-hidden-input
element.<label q-checkbox>
<div q-checkbox-label>...</div>
</label>
stringbooleanboolean| 'sm'
| 'md'
| 'lg'
stringbooleanComposite API
q-checkbox-root
boolean'ltr' | 'rtl'
boolean() =>
| Node
| ShadowRoot
| Document
stringbooleanindeterminate state.stringbooleanboolean| 'sm'
| 'md'
| 'lg'
stringbooleanclass'qui-checkbox__root'data-activedata-checkbox-part'root'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-state| 'checked'
| 'indeterminate'
| 'unchecked'
q-checkbox-label
stringclass'qui-checkbox__label'data-activedata-checkbox-part'label'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-size| 'sm'
| 'md'
| 'lg'
data-state| 'checked'
| 'indeterminate'
| 'unchecked'
q-checkbox-control
stringclass'qui-checkbox__control'data-activedata-checkbox-part'control'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-size| 'sm'
| 'md'
| 'lg'
data-state| 'checked'
| 'indeterminate'
| 'unchecked'
q-checkbox-hidden-input
ngModel or formControl to this element.
Apply them to the root element instead.stringstringstringclass'qui-checkbox__hidden-input'data-activedata-checkbox-part'hidden-input'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-state| 'checked'
| 'indeterminate'
| 'unchecked'
styleq-checkbox-indicator
class'qui-checkbox__indicator'data-activedata-checkbox-part'indicator'data-disableddata-focusdata-focus-visibledata-hoverdata-invaliddata-readonlydata-size| 'sm'
| 'md'
| 'lg'
data-state| 'checked'
| 'indeterminate'
| 'unchecked'
hiddenboolean