Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/components-examples/material/select/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
export {SelectCustomTriggerExample} from './select-custom-trigger/select-custom-trigger-example';
export {SelectCustomTriggerSignalFormExample} from './select-custom-trigger-signal-form/select-custom-trigger-signal-form-example';
export {SelectDisabledExample} from './select-disabled/select-disabled-example';
export {SelectDisabledSignalFormExample} from './select-disabled-signal-form/select-disabled-signal-form-example';
export {SelectErrorStateMatcherExample} from './select-error-state-matcher/select-error-state-matcher-example';
export {SelectErrorStateMatcherSignalFormExample} from './select-error-state-matcher-signal-form/select-error-state-matcher-signal-form-example';
export {SelectFormExample} from './select-form/select-form-example';
export {SelectFormSignalFormExample} from './select-form-signal-form/select-form-signal-form-example';
export {SelectHintErrorExample} from './select-hint-error/select-hint-error-example';
export {SelectMultipleExample} from './select-multiple/select-multiple-example';
export {SelectMultipleSignalFormExample} from './select-multiple-signal-form/select-multiple-signal-form-example';
export {SelectNoRippleExample} from './select-no-ripple/select-no-ripple-example';
export {SelectOptgroupExample} from './select-optgroup/select-optgroup-example';
export {SelectOptgroupSignalFormExample} from './select-optgroup-signal-form/select-optgroup-signal-form-example';
export {SelectOverviewExample} from './select-overview/select-overview-example';
export {SelectPanelClassExample} from './select-panel-class/select-panel-class-example';
export {SelectPanelClassSignalFormExample} from './select-panel-class-signal-form/select-panel-class-signal-form-example';
export {SelectResetExample} from './select-reset/select-reset-example';
export {SelectValueBindingExample} from './select-value-binding/select-value-binding-example';
export {SelectReactiveFormExample} from './select-reactive-form/select-reactive-form-example';
export {SelectInitialValueExample} from './select-initial-value/select-initial-value-example';
export {SelectSelectableNullExample} from './select-selectable-null/select-selectable-null-example';
export {SelectSelectableNullSignalFormExample} from './select-selectable-null-signal-form/select-selectable-null-signal-form-example';
export {SelectHintErrorSignalFormExample} from './select-hint-error-signal-form/select-hint-error-signal-form-example';
export {SelectHarnessExample} from './select-harness/select-harness-example';
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.example-additional-selection {
opacity: 0.75;
font-size: 0.75em;
line-height: 1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<mat-form-field>
<mat-label>Toppings</mat-label>
<mat-select [formField]="toppingsField" multiple>
<mat-select-trigger>
@let toppings = toppingsField().value();
{{toppings[0] || ''}}
@if (toppings.length > 1) {
<span class="example-additional-selection">
(+{{toppings.length - 1}} {{toppings.length === 2 ? 'other' : 'others'}})
</span>
}
</mat-select-trigger>
@for (topping of toppingList; track topping) {
<mat-option [value]="topping">{{topping}}</mat-option>
}
</mat-select>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {Component, signal} from '@angular/core';
import {MatSelectModule} from '@angular/material/select';
import {MatFormFieldModule} from '@angular/material/form-field';
import {form, FormField} from '@angular/forms/signals';

/** @title Select with custom trigger text (signal forms) */
@Component({
selector: 'select-custom-trigger-signal-form-example',
templateUrl: 'select-custom-trigger-signal-form-example.html',
styleUrl: 'select-custom-trigger-signal-form-example.css',
imports: [MatFormFieldModule, MatSelectModule, FormField],
})
export class SelectCustomTriggerSignalFormExample {
protected toppingsField = form(signal<string[]>([]));

protected toppingList = [
'Extra cheese',
'Mushroom',
'Onion',
'Pepperoni',
'Sausage',
'Tomato',
] as const;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {MatFormFieldModule} from '@angular/material/form-field';
imports: [MatFormFieldModule, MatSelectModule, FormsModule, ReactiveFormsModule],
})
export class SelectCustomTriggerExample {
toppings = new FormControl('');
toppings = new FormControl<string[]>([]);

toppingList: string[] = ['Extra cheese', 'Mushroom', 'Onion', 'Pepperoni', 'Sausage', 'Tomato'];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<p>
<mat-checkbox [formField]="form.disableField">Disable select</mat-checkbox>
</p>

<h4>mat-select</h4>
<mat-form-field>
<mat-label>Choose an option</mat-label>
<mat-select [formField]="form.option">
<mat-option value="option1">Option 1</mat-option>
<mat-option value="option2" disabled>Option 2 (disabled)</mat-option>
<mat-option value="option3">Option 3</mat-option>
</mat-select>
</mat-form-field>

<h4>native html select</h4>
<mat-form-field>
<mat-label>Choose an option</mat-label>
<select matNativeControl [formField]="form.vehicle">
<option value="" selected></option>
<option value="volvo">Volvo</option>
<option value="saab" disabled>Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
</mat-form-field>

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {Component, signal} from '@angular/core';
import {disabled, form, FormField} from '@angular/forms/signals';
import {MatInputModule} from '@angular/material/input';
import {MatSelectModule} from '@angular/material/select';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatCheckboxModule} from '@angular/material/checkbox';

/** @title Disabled select (signal forms) */
@Component({
selector: 'select-disabled-signal-form-example',
templateUrl: 'select-disabled-signal-form-example.html',
imports: [MatCheckboxModule, FormField, MatFormFieldModule, MatSelectModule, MatInputModule],
})
export class SelectDisabledSignalFormExample {
protected form = form(signal({option: '', vehicle: '', disableField: false}), p => {
disabled(p.option, {when: ({valueOf}) => valueOf(p.disableField)});
disabled(p.vehicle, {when: ({valueOf}) => valueOf(p.disableField)});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<h4>mat-select</h4>
<mat-form-field>
<mat-label>Choose one</mat-label>
<mat-select [formField]="selectFormField" [errorStateMatcher]="matcher">
<mat-option>Clear</mat-option>
<mat-option value="valid">Valid option</mat-option>
<mat-option value="invalid">Invalid option</mat-option>
</mat-select>
<mat-hint>Errors appear instantly!</mat-hint>
@if (selectFormField().getError('required')) {
<mat-error>You must make a selection</mat-error>
}
@if (selectFormField().getError('pattern') && !selectFormField().getError('required')) {
<mat-error>Your selection is invalid</mat-error>
}
</mat-form-field>

<h4>native html select</h4>
<mat-form-field class="demo-full-width">
<mat-label>Choose one</mat-label>
<select matNativeControl [formField]="nativeSelectFormField" [errorStateMatcher]="matcher">
<option value=""></option>
<option value="valid">Valid option</option>
<option value="invalid">Invalid option</option>
</select>
@if (nativeSelectFormField().getError('required')) {
<mat-error>You must make a selection</mat-error>
}
@if (nativeSelectFormField().getError('pattern') && !nativeSelectFormField().getError('required')) {
<mat-error>Your selection is invalid</mat-error>
}
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {Component, signal} from '@angular/core';
import {ErrorStateMatcher} from '@angular/material/core';
import {MatInputModule} from '@angular/material/input';
import {MatSelectModule} from '@angular/material/select';
import {MatFormFieldModule} from '@angular/material/form-field';
import {Field, form, FormField, pattern, required} from '@angular/forms/signals';

/** Error when invalid control is dirty, touched, or submitted. */
export class MyErrorStateMatcher implements ErrorStateMatcher {
isErrorState() {
return false;
}
isSignalErrorState(field: Field<unknown> | null): boolean {
const isSubmitting = (field && field().submitting()) ?? false;
return !!(field && field().invalid() && (field().dirty() || field().touched() || isSubmitting));
}
}

/** @title Select with a custom ErrorStateMatcher (signal forms) */
@Component({
selector: 'select-error-state-matcher-signal-form-example',
templateUrl: 'select-error-state-matcher-signal-form-example.html',
imports: [MatFormFieldModule, MatSelectModule, FormField, MatInputModule],
})
export class SelectErrorStateMatcherSignalFormExample {
private _validPattern = /^valid$/;

protected selectFormField = form(signal('valid'), p => {
required(p);
pattern(p, this._validPattern);
});

protected nativeSelectFormField = form(signal('valid'), p => {
required(p);
pattern(p, this._validPattern);
});

protected matcher = new MyErrorStateMatcher();
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<h4>mat-select</h4>
<mat-form-field>
<mat-label>Choose one</mat-label>
<mat-select [formControl]="selected" [errorStateMatcher]="matcher">
<mat-select [formControl]="selectFormControl" [errorStateMatcher]="matcher">
<mat-option>Clear</mat-option>
<mat-option value="valid">Valid option</mat-option>
<mat-option value="invalid">Invalid option</mat-option>
</mat-select>
<mat-hint>Errors appear instantly!</mat-hint>
@if (selected.hasError('required')) {
@if (selectFormControl.hasError('required')) {
<mat-error>You must make a selection</mat-error>
}
@if (selected.hasError('pattern') && !selected.hasError('required')) {
@if (selectFormControl.hasError('pattern') && !selectFormControl.hasError('required')) {
<mat-error>Your selection is invalid</mat-error>
}
</mat-form-field>
Expand All @@ -20,7 +20,7 @@ <h4>native html select</h4>
<mat-label>Choose one</mat-label>
<select matNativeControl [formControl]="nativeSelectFormControl" [errorStateMatcher]="matcher">
<option value=""></option>
<option value="valid" selected>Valid option</option>
<option value="valid">Valid option</option>
<option value="invalid">Invalid option</option>
</select>
@if (nativeSelectFormControl.hasError('required')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ export class MyErrorStateMatcher implements ErrorStateMatcher {
imports: [MatFormFieldModule, MatSelectModule, FormsModule, ReactiveFormsModule, MatInputModule],
})
export class SelectErrorStateMatcherExample {
selected = new FormControl('valid', [Validators.required, Validators.pattern('valid')]);

selectFormControl = new FormControl('valid', [Validators.required, Validators.pattern('valid')]);

nativeSelectFormControl = new FormControl('valid', [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<form>
<h4>mat-select</h4>
<mat-form-field>
<mat-label>Favorite food</mat-label>
<mat-select [formField]="selectedValue">
@for (food of foods; track food) {
<mat-option [value]="food.value">{{food.viewValue}}</mat-option>
}
</mat-select>
</mat-form-field>
<p> Selected food: {{selectedValue().value()}} </p>
<h4>native html select</h4>
<mat-form-field>
<mat-label>Favorite car</mat-label>
<select matNativeControl [formField]="selectedCar">
<option value="" selected></option>
@for (car of cars; track car) {
<option [value]="car.value">{{car.viewValue}}</option>
}
</select>
</mat-form-field>
<p> Selected car: {{selectedCar().value()}} </p>
</form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {Component, signal} from '@angular/core';
import {MatInputModule} from '@angular/material/input';
import {MatSelectModule} from '@angular/material/select';
import {MatFormFieldModule} from '@angular/material/form-field';
import {form, FormField} from '@angular/forms/signals';

interface Food {
value: string;
viewValue: string;
}

interface Car {
value: string;
viewValue: string;
}

/**
* @title Select in a form (signal forms)
*/
@Component({
selector: 'select-form-signal-form-example',
templateUrl: 'select-form-signal-form-example.html',
imports: [FormField, MatFormFieldModule, MatSelectModule, MatInputModule],
})
export class SelectFormSignalFormExample {
protected selectedValue = form(signal(''));
protected selectedCar = form(signal(''));

protected foods: Food[] = [
{value: 'steak-0', viewValue: 'Steak'},
{value: 'pizza-1', viewValue: 'Pizza'},
{value: 'tacos-2', viewValue: 'Tacos'},
];

protected cars: Car[] = [
{value: 'volvo', viewValue: 'Volvo'},
{value: 'saab', viewValue: 'Saab'},
{value: 'mercedes', viewValue: 'Mercedes'},
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface Car {
}

/**
* @title Select in a form
* @title Select in a form (template-driven forms)
*/
@Component({
selector: 'select-form-example',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<h4>mat select</h4>
<mat-form-field>
<mat-label>Favorite animal</mat-label>
<mat-select [formField]="animalControl">
<mat-option>--</mat-option>
@for (animal of animals; track animal) {
<mat-option [value]="animal.name">{{animal.name}}</mat-option>
}
</mat-select>
@if (animalControl().getError('required')) {
<mat-error>Please choose an animal</mat-error>
}
@if (animalSound(); as animalSound) {
<mat-hint>{{animalSound}}</mat-hint>
}
</mat-form-field>

<h4>native html select</h4>
<mat-form-field>
<mat-label>Select your car (required)</mat-label>
<select matNativeControl [formField]="selectFormControl">
<option label="--select something --"></option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
@if (selectFormControl().getError('required')) {
<mat-error>This field is required</mat-error>
}
<mat-hint>You can pick up your favorite car here</mat-hint>
</mat-form-field>

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {Component, computed, signal} from '@angular/core';
import {MatInputModule} from '@angular/material/input';
import {MatSelectModule} from '@angular/material/select';
import {MatFormFieldModule} from '@angular/material/form-field';
import {form, FormField, required} from '@angular/forms/signals';

interface Animal {
name: string;
sound: string;
}

/** @title Select with form field features (signal forms) */
@Component({
selector: 'select-hint-error-signal-form-example',
templateUrl: 'select-hint-error-signal-form-example.html',
imports: [MatFormFieldModule, MatSelectModule, FormField, MatInputModule],
})
export class SelectHintErrorSignalFormExample {
protected animalControl = form(signal<string>(''), p => {
required(p);
});
protected selectFormControl = form(signal(''), p => {
required(p);
});

protected animalSound = computed<string | undefined>(
() => this.animals.find(a => a.name === this.animalControl().value())?.sound,
);

protected animals: Animal[] = [
{name: 'Dog', sound: 'Woof!'},
{name: 'Cat', sound: 'Meow!'},
{name: 'Cow', sound: 'Moo!'},
{name: 'Fox', sound: 'Wa-pa-pa-pa-pa-pa-pow!'},
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<mat-form-field>
<mat-label>Toppings</mat-label>
<mat-select [formField]="toppings" multiple>
@for (topping of toppingList; track topping) {
<mat-option [value]="topping">{{topping}}</mat-option>
}
</mat-select>
</mat-form-field>
Loading
Loading