Text Input
Text inputs are fundamental form components that provide a versatile foundation for data collection across web and mobile experiences. They support multiple configurations including labels, hint text, placeholder content, and start or end icons to guide users through various input scenarios.
import {TextInputModule} from "@qualcomm-ui/angular/text-input"Examples
Simple
The simple API bundles all subcomponents together into a single component.
<q-text-input
class="w-72"
hint="Optional hint"
label="Label"
placeholder="Placeholder text"
/>
Icons
Add icons using icon props at the root.
<q-text-input
class="w-72"
defaultValue="Both icons"
endIcon="Calendar"
label="Both icons"
startIcon="AArrowDown"
/>
Child Directives
Provide child directives to customize specific elements while keeping the simple API's default structure.
import {Component} from "@angular/core"
import {TextInputModule} from "@qualcomm-ui/angular/text-input"
@Component({
imports: [TextInputModule],
selector: "text-input-child-directives-demo",
template: `
<q-text-input class="w-72" placeholder="Placeholder text">
<label q-text-input-label>Label</label>
<div q-text-input-hint>Optional hint</div>
</q-text-input>
`,
})
export class TextInputChildDirectivesDemo {}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.
<div class="w-72" q-text-input-root>
<label q-text-input-label>Label</label>
<div q-text-input-input-group>
<input placeholder="Placeholder text" q-text-input-input />
<button q-text-input-clear-trigger></button>
<span q-text-input-error-indicator></span>
</div>
<div q-text-input-hint>Optional hint</div>
</div>
Icons (Composite)
The q-text-input-input-group directive provides icon properties for the composite API.
<div
class="w-72"
defaultValue="Both icons with clear button"
q-text-input-root
>
<label q-text-input-label>Start + End icons</label>
<div endIcon="Calendar" q-text-input-input-group startIcon="AArrowDown">
<input placeholder="Placeholder text" q-text-input-input />
<button q-text-input-clear-trigger></button>
</div>
</div>
Composite Layout
The composite API is useful for alternative layouts and styles.
<div q-text-input-root size="sm">
<div class="flex items-center gap-4">
<label class="font-body-sm-bold w-48" q-text-input-label>
Project Name
</label>
<div q-text-input-input-group>
<input class="w-full" placeholder="QVSCE" q-text-input-input />
</div>
</div>
</div>
<div q-text-input-root size="sm">
<div class="flex items-center gap-4">
<label class="font-body-sm-bold w-48" q-text-input-label>
Project Version
</label>
<div q-text-input-input-group>
<input class="w-full" placeholder="v1.2.3" q-text-input-input />
</div>
</div>
</div>
Clear Trigger
- When using the simple API, the clear button renders automatically. Change this by setting the clearable prop to false.
- When using the composite API, you render the
q-text-input-clear-triggerdeclaratively within the input group. - The clear button only shows when the input field has text.
<q-text-input aria-label="Simple" defaultValue="Simple" />
<div defaultValue="Composite" q-text-input-root>
<div q-text-input-input-group>
<input aria-label="Composite" q-text-input-input />
<button q-text-input-clear-trigger></button>
</div>
</div>
Sizes
Customize size using the size prop. The default size is md.
<q-text-input
aria-label="sm"
class="w-56"
defaultValue="sm"
size="sm"
startIcon="Search"
/>
<q-text-input
aria-label="md"
class="w-60"
defaultValue="md"
size="md"
startIcon="Search"
/>
<q-text-input
aria-label="lg"
class="w-64"
defaultValue="lg"
size="lg"
startIcon="Search"
/>
Error Text and Indicator
Error messages are displayed using two props:
The error text and indicator will only render when invalid is true.
<q-text-input
#textInput
class="w-64"
errorText="You must enter a value"
label="Label"
placeholder="Enter a value"
required
[invalid]="!value()"
[(ngModel)]="value"
/>
Forms
Control and validate the input value using Angular form control bindings.
Template Forms
When using template-driven forms with ngModel, perform validation manually in your component and pass the result to the invalid property.
TIP
Form validation timing is controlled by the updateOn property on the form control.
<q-text-input
class="w-72"
errorText="Must be at least 3 characters long"
label="Label"
placeholder="Enter a value"
[invalid]="value().length < 2"
[(ngModel)]="value"
/>
States
When using template forms, the disabled, readOnly, and invalid properties govern the interactive state of the control.
<q-text-input disabled label="Disabled" placeholder="Disabled" />
<q-text-input label="Read only" placeholder="Read only" readOnly />
<q-text-input
errorText="Invalid"
invalid
label="Invalid"
placeholder="Invalid"
/>
Required
When using template forms, pass the required property to apply the RequiredValidator to the form control.
<q-text-input
class="w-72"
label="Required"
placeholder="Enter a value"
required
[(ngModel)]="value"
/>
Reactive Forms
Use Reactive Forms for better control of form state and validation.
import {Component, inject, signal} from "@angular/core"
import {
FormBuilder,
type FormGroup,
ReactiveFormsModule,
Validators,
} from "@angular/forms"
import {ButtonModule} from "@qualcomm-ui/angular/button"
import {TextInputModule} from "@qualcomm-ui/angular/text-input"
@Component({
imports: [TextInputModule, ButtonModule, ReactiveFormsModule],
selector: "text-input-reactive-forms-demo",
template: `
<form
class="mx-auto flex w-96 flex-col gap-3"
[formGroup]="addressForm"
(ngSubmit)="onSubmit()"
>
<q-text-input
class="w-full"
errorText="Street address is required"
formControlName="streetAddress"
label="Stress Address"
placeholder="123 Main St"
[invalid]="isFieldInvalid('streetAddress')"
/>
<q-text-input
class="w-full"
errorText="City is required"
formControlName="city"
label="City"
placeholder="San Diego"
[invalid]="isFieldInvalid('city')"
/>
<div class="grid grid-cols-2 gap-4">
<q-text-input
class="w-full"
errorText="State must be 2 characters"
formControlName="state"
label="State"
placeholder="CA"
[invalid]="isFieldInvalid('state')"
/>
<q-text-input
class="w-full"
errorText="Zip code must be at least 5 characters"
formControlName="zipCode"
label="Zip Code"
placeholder="10001"
[invalid]="isFieldInvalid('zipCode')"
/>
</div>
<div class="mt-2 flex w-full justify-end">
<button
emphasis="primary"
q-button
type="submit"
variant="fill"
[disabled]="isSubmitting()"
>
Save Address
</button>
</div>
</form>
`,
})
export class TextInputReactiveFormsDemo {
readonly isSubmitting = signal(false)
readonly addressForm: FormGroup
private fb = inject(FormBuilder)
constructor() {
this.addressForm = this.fb.group({
city: ["", [Validators.required]],
state: [
"CA",
[Validators.required, Validators.minLength(2), Validators.maxLength(2)],
],
streetAddress: ["", [Validators.required]],
zipCode: ["", [Validators.required, Validators.minLength(5)]],
})
}
isFieldInvalid(fieldName: string): boolean {
const field = this.addressForm.get(fieldName)
return !!(field && field.invalid && (field.dirty || field.touched))
}
onSubmit() {
if (this.addressForm.valid) {
this.isSubmitting.set(true)
console.log("Form submitted:", this.addressForm.value)
// Simulate API call
setTimeout(() => {
this.isSubmitting.set(false)
}, 1000)
} else {
// Mark all fields as touched to show validation errors
this.addressForm.markAllAsTouched()
}
}
}State Guidelines
The disabled, invalid, and required properties have no effect when using Reactive Forms. Use the equivalent Reactive Form bindings instead:
disabledField = new FormControl("")
invalidField = new FormControl("invalid-email", {
validators: [Validators.required, Validators.email],
})
requiredField = new FormControl("", {validators: [Validators.required]})
ngOnInit() {
this.disabledField.disable()
this.invalidField.markAsDirty()
}
Composite API & Forms
When using the composite API with Angular Forms, always apply form control bindings to the q-text-input-root directive.
<div
class="w-72"
q-text-input-root
[invalid]="isInvalid()"
[(ngModel)]="value"
>
<label q-text-input-label>Composite Forms</label>
<div q-text-input-input-group>
<input placeholder="Enter a value" q-text-input-input />
<button q-text-input-clear-trigger></button>
<span q-text-input-error-indicator></span>
</div>
<div q-text-input-error-text>Must be at least three characters long</div>
</div>
Explorer
Component Anatomy
Hover to highlight, click to view API
API
<q-text-input>
The <q-text-input> component extends the q-text-input-root directive with the following properties:
stringstringbooleantrue, renders a clear button that resets the input value on click.
The button only appears when the input has a value.string<div q-text-input-error-text>...</div>
string<div q-text-input-hint>...</div>
string<div q-text-input-label>...</div>
stringComposite API
q-text-input-root
string'ltr' | 'rtl'
boolean| string
| LucideIconData
<div q-text-input-input-group>
<div q-input-end-icon [icon]="..."></div>
</div>
() =>
| Node
| ShadowRoot
| Document
booleanstringbooleanboolean| 'sm'
| 'md'
| 'lg'
| string
| LucideIconData
<div q-text-input-input-group>
<div q-input-start-icon [icon]="..."></div>
</div>
stringclass'qui-input__root'data-disableddata-focusdata-invaliddata-size| 'sm'
| 'md'
| 'lg'
data-text-input-part'root'q-text-input-input-group
class'qui-input__input-group'data-disableddata-focusdata-invaliddata-readonlydata-size| 'sm'
| 'md'
| 'lg'
data-text-input-part'input-group'q-text-input-label
class'qui-input__label'data-disableddata-focusdata-invaliddata-size| 'sm'
| 'md'
| 'lg'
data-text-input-part'label'q-text-input-hint
class'qui-input__hint'data-disableddata-text-input-part'hint'hiddenbooleanq-text-input-clear-trigger
class'qui-input__clear-trigger'data-disableddata-size| 'sm'
| 'md'
| 'lg'
data-text-input-part'clear-trigger'q-text-input-input
ngModel
or formControl to this element. Apply them to the root element instead.string[attr.aria-label]stringstringclass'qui-input__input'data-emptydata-focusdata-invaliddata-readonlydata-size| 'sm'
| 'md'
| 'lg'
data-text-input-part'input'q-text-input-error-text
class'qui-input__error-text'data-text-input-part'error-text'hiddenbooleanq-text-input-error-indicator
| LucideIconData
| string
class'qui-input__error-indicator'data-size| 'sm'
| 'md'
| 'lg'
data-text-input-part'error-indicator'hiddenboolean