Switch
Switches enable users to instantly activate or deactivate features, settings, or options with immediate visual confirmation. The component features a sliding thumb that moves along a track to communicate the current state.
import {SwitchModule} from "@qualcomm-ui/angular/switch"Examples
Simple
The simple API bundles all subcomponents together into a single directive.
<label label="Label" q-switch></label>
Child Directives
Provide child directives to customize specific elements while keeping the simple API's default structure.
<label q-switch>
<div q-switch-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-switch-root>
<input q-switch-hidden-input />
<div q-switch-control></div>
<span q-switch-label>Label</span>
</label>
States
Based on the inputs, the switch will render as checked or unchecked.
<label defaultChecked label="Label" q-switch></label>
<label label="Label" q-switch></label>
Disabled
When disabled is true, the switch becomes non-interactive and is typically rendered with reduced opacity to indicate its unavailable state.
<label defaultChecked disabled label="Label" q-switch></label>
<label disabled label="Label" q-switch></label>
Forms
Template Forms
- When using template-driven forms, add the required attribute to enable validation
- The errorText input (or
q-switch-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="Please accept the Terms of Service"
name="acceptTerms"
q-switch
required
[(ngModel)]="acceptTerms"
></label>
Reactive Forms
Use Reactive 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 {SwitchModule} from "@qualcomm-ui/angular/switch"
@Component({
imports: [SwitchModule, ReactiveFormsModule, ButtonModule],
selector: "switch-reactive-forms",
template: `
<form
class="flex w-56 flex-col gap-2"
[formGroup]="form"
(ngSubmit)="onSubmit()"
>
<label formControlName="acceptTerms" q-switch-root>
<input q-switch-hidden-input />
<div q-switch-control></div>
<span q-switch-label>Accept Terms of Service</span>
<span q-switch-error-text>
Please accept the Terms of Service to continue
</span>
</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 SwitchReactiveFormsDemo {
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 {ErrorTextComponent} from "@qualcomm-ui/angular/input"
import {SwitchModule} from "@qualcomm-ui/angular/switch"
// Custom validator that requires at least one switch 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: [
SwitchModule,
ReactiveFormsModule,
ButtonModule,
ErrorTextComponent,
],
selector: "switch-advanced-validation-demo",
template: `
<form
class="flex w-72 flex-col gap-2"
[formGroup]="form"
(ngSubmit)="onSubmit()"
>
<fieldset>
<legend class="text-neutral-primary font-heading-xxs mb-3">
Select your interests (at least one required):
</legend>
<div class="flex flex-col gap-2" formGroupName="interests">
<label formControlName="technology" q-switch-root>
<input q-switch-hidden-input />
<div q-switch-control></div>
<span q-switch-label>Technology</span>
</label>
<label formControlName="sports" q-switch-root>
<input q-switch-hidden-input />
<div q-switch-control></div>
<span q-switch-label>Sports</span>
</label>
<label formControlName="music" q-switch-root>
<input q-switch-hidden-input />
<div q-switch-control></div>
<span q-switch-label>Music</span>
</label>
<label formControlName="travel" q-switch-root>
<input q-switch-hidden-input />
<div q-switch-control></div>
<span q-switch-label>Travel</span>
</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 SwitchAdvancedValidationDemo {
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)
}
}Composite Guidelines
Only bind form controls to the q-switch or q-switch-root directives.
<!-- Won't work ❌ -->
<div q-switch-root>
<input q-switch-hidden-input [(ngModel)]="value" />
<div q-switch-control></div>
</div>
<!-- Works as expected ✅ -->
<div q-switch-root [(ngModel)]="value">
<input q-switch-hidden-input />
<div q-switch-control></div>
</div>The composite elements are only intended to be used as direct descendants of the q-switch or q-switch-root directives.
<!-- Won't work alone ❌ -->
<input q-switch-hidden-input />
<div q-switch-control></div>
<!-- Works as expected ✅ -->
<div q-switch-root>
<input q-switch-hidden-input />
<div q-switch-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-switch-label directive when authoring the text label. This is automatically associated with the input element.
- If you omit the label, you must provide an aria-label or aria-labelledby attribute on the
q-switch-hidden-inputelement.
API
q-switch
The q-switch directive extends the q-switch-root directive with the following properties:
| Prop | Type |
|---|---|
Optional error that describes the switch when the field is invalid. This
element is automatically associated with the switch for accessibility. | string |
To customize the element, provide it using the directive instead: <label q-switch> | |
Optional label describing the element. Recommended. This element is
automatically associated with the component's input element for accessibility. | string |
To customize the element, provide it using the directive instead: <label q-switch> | |
string<label q-switch>
<div q-switch-error-text>...</div>
</label>
string<label q-switch>
<div q-switch-label>...</div>
</label>
Composite API
q-switch-root
| Prop | Type | Default |
|---|---|---|
The initial checked state of the checkbox when rendered. Use when you don't
need to control the checked state of the checkbox. This property will be
ignored if you opt into controlled state via form control bindings. | boolean | |
The document's text/writing direction. | 'ltr' | 'rtl' | "ltr" |
Controls whether the input is disabled in template-driven forms. When true,
prevents user interaction and applies visual styling to indicate the disabled
state. | boolean | |
A root node to correctly resolve the Document in custom environments. i.e.,
Iframes, Electron. | () => | |
id attribute. If
omitted, a unique identifier will be generated for accessibility.) | string | |
If true and the checkbox is not checked, the checkbox will be in the
indeterminate state. | boolean | |
The name of the input field in a checkbox. Useful for form submission. | string | |
Whether the input is read-only. When true, prevents user interaction while
keeping the input focusable and visible. | boolean | |
Controls whether the input is required in template-driven forms. When
true, the input must have a value for form validation to pass. | boolean | |
Size of the component and its label. | 'sm' | 'md' | "md" |
The value of checkbox input. Useful for form submission. | string | "on" |
Event emitted when the checkbox is checked or unchecked. This is only emitted
on interaction. It doesn't emit in response to programmatic form control
changes. | boolean |
boolean'ltr' | 'rtl'
boolean() =>
| Node
| ShadowRoot
| Document
stringbooleanindeterminate state.stringbooleanboolean'sm' | 'md'
stringbooleanq-switch-label
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be generated for accessibility. | string |
stringq-switch-hidden-input
| Prop | Type |
|---|---|
id attribute. If
omitted, a unique identifier will be generated for accessibility. | string |
stringq-switch-control
q-switch-error-text
| Prop | Type | Default |
|---|---|---|
Error indicator icon. | | LucideIconData | CircleAlert |
id attribute. If
omitted, a unique identifier will be generated for accessibility. | string |
| LucideIconData
| string
string