Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(material/checkbox): Update checkbox docs & examples #29234

Merged
merged 1 commit into from
Jun 12, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ <h2 class="example-h2">Result</h2>

<section class="example-section">
<mat-checkbox
class="example-margin"
[(ngModel)]="checked"
[(indeterminate)]="indeterminate"
[labelPosition]="labelPosition"
[disabled]="disabled">
class="example-margin"
[(ngModel)]="checked"
[(indeterminate)]="indeterminate"
[labelPosition]="labelPosition()"
[disabled]="disabled()"
>
I'm a checkbox
</mat-checkbox>
</section>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Component} from '@angular/core';
import {MatRadioModule} from '@angular/material/radio';
import {ChangeDetectionStrategy, Component, model} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {MatCardModule} from '@angular/material/card';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {MatRadioModule} from '@angular/material/radio';

/**
* @title Configurable checkbox
Expand All @@ -13,10 +13,11 @@ import {MatCardModule} from '@angular/material/card';
styleUrl: 'checkbox-configurable-example.css',
standalone: true,
imports: [MatCardModule, MatCheckboxModule, FormsModule, MatRadioModule],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckboxConfigurableExample {
checked = false;
indeterminate = false;
labelPosition: 'before' | 'after' = 'after';
disabled = false;
readonly checked = model(false);
readonly indeterminate = model(false);
readonly labelPosition = model<'before' | 'after'>('after');
readonly disabled = model(false);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<mat-checkbox
required
[checked]="true"
name="first-name"
value="first-value"
aria-label="First checkbox">
required
[checked]="true"
name="first-name"
value="first-value"
aria-label="First checkbox"
>
First
</mat-checkbox>
<mat-checkbox indeterminate="true" [disabled]="disabled" aria-label="Second checkbox">
<mat-checkbox indeterminate="true" [disabled]="disabled()" aria-label="Second checkbox">
Second
</mat-checkbox>
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe('CheckboxHarnessExample', () => {
});

it('should toggle checkbox', async () => {
fixture.componentInstance.disabled = false;
fixture.componentRef.setInput('disabled', false);
fixture.changeDetectorRef.markForCheck();
const [checkedCheckbox, uncheckedCheckbox] = await loader.getAllHarnesses(MatCheckboxHarness);
await checkedCheckbox.toggle();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component} from '@angular/core';
import {ChangeDetectionStrategy, Component, input} from '@angular/core';
import {MatCheckboxModule} from '@angular/material/checkbox';

/**
Expand All @@ -9,7 +9,8 @@ import {MatCheckboxModule} from '@angular/material/checkbox';
templateUrl: 'checkbox-harness-example.html',
standalone: true,
imports: [MatCheckboxModule],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckboxHarnessExample {
disabled = true;
readonly disabled = input(true);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@

<section class="example-section">
<span class="example-list-section">
<mat-checkbox class="example-margin"
[checked]="allComplete"
[color]="task.color"
[indeterminate]="someComplete()"
(change)="setAll($event.checked)">
{{task.name}}
<mat-checkbox
class="example-margin"
[checked]="task().completed"
[indeterminate]="partiallyComplete()"
(change)="update($event.checked)"
>
{{task().name}}
</mat-checkbox>
</span>
<span class="example-list-section">
<ul>
@for (subtask of task.subtasks; track subtask) {
@for (subtask of task().subtasks; track subtask; let i = $index) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also use $index directly without having to alias it. Not a big deal to leave it as is though.

<li>
<mat-checkbox [(ngModel)]="subtask.completed"
[color]="subtask.color"
(ngModelChange)="updateAllComplete()">
<mat-checkbox [checked]="subtask.completed" (change)="update($event.checked, i)">
{{subtask.name}}
</mat-checkbox>
</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {Component} from '@angular/core';
import {ThemePalette} from '@angular/material/core';
import {ChangeDetectionStrategy, Component, computed, signal} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {MatCheckboxModule} from '@angular/material/checkbox';

export interface Task {
name: string;
completed: boolean;
color: ThemePalette;
subtasks?: Task[];
}

Expand All @@ -19,37 +17,37 @@ export interface Task {
styleUrl: 'checkbox-overview-example.css',
standalone: true,
imports: [MatCheckboxModule, FormsModule],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckboxOverviewExample {
task: Task = {
name: 'Indeterminate',
readonly task = signal<Task>({
name: 'Parent task',
completed: false,
color: 'primary',
subtasks: [
{name: 'Primary', completed: false, color: 'primary'},
{name: 'Accent', completed: false, color: 'accent'},
{name: 'Warn', completed: false, color: 'warn'},
{name: 'Child task 1', completed: false},
{name: 'Child task 2', completed: false},
{name: 'Child task 3', completed: false},
],
};
});

allComplete: boolean = false;

updateAllComplete() {
this.allComplete = this.task.subtasks != null && this.task.subtasks.every(t => t.completed);
}

someComplete(): boolean {
if (this.task.subtasks == null) {
readonly partiallyComplete = computed(() => {
const task = this.task();
if (!task.subtasks) {
return false;
}
return this.task.subtasks.filter(t => t.completed).length > 0 && !this.allComplete;
}
return task.subtasks.some(t => t.completed) && !task.subtasks.every(t => t.completed);
});

setAll(completed: boolean) {
this.allComplete = completed;
if (this.task.subtasks == null) {
return;
}
this.task.subtasks.forEach(t => (t.completed = completed));
update(completed: boolean, index?: number) {
this.task.update(task => {
if (index === undefined) {
task.completed = completed;
task.subtasks?.forEach(t => (t.completed = completed));
} else {
task.subtasks![index].completed = completed;
task.completed = task.subtasks?.every(t => t.completed) ?? true;
}
return {...task};
});
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Component} from '@angular/core';
import {FormBuilder, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {JsonPipe} from '@angular/common';
import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
import {FormBuilder, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {MatCheckboxModule} from '@angular/material/checkbox';

/** @title Checkboxes with reactive forms */
Expand All @@ -10,13 +10,14 @@ import {MatCheckboxModule} from '@angular/material/checkbox';
styleUrl: 'checkbox-reactive-forms-example.css',
standalone: true,
imports: [FormsModule, ReactiveFormsModule, MatCheckboxModule, JsonPipe],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CheckboxReactiveFormsExample {
toppings = this._formBuilder.group({
private readonly _formBuilder = inject(FormBuilder);

readonly toppings = this._formBuilder.group({
pepperoni: false,
extracheese: false,
mushroom: false,
});

constructor(private _formBuilder: FormBuilder) {}
}
6 changes: 5 additions & 1 deletion src/material/checkbox/checkbox-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {ThemePalette} from '@angular/material/core';

/** Default `mat-checkbox` options that can be overridden. */
export interface MatCheckboxDefaultOptions {
/** Default theme color palette to be used for checkboxes. */
/**
* Default theme color palette to be used for checkboxes. This API is supported in M2 themes
* only, it has no effect in M3 themes. For information on applying color variants in M3, see
* https://material.angular.io/guide/theming#using-component-color-variants
*/
color?: ThemePalette;
/** Default checkbox click action for checkboxes. */
clickAction?: MatCheckboxClickAction;
Expand Down
15 changes: 13 additions & 2 deletions src/material/checkbox/checkbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ enhanced with Material Design styling and animations.
<!-- example(checkbox-overview) -->

### Checkbox label

The checkbox label is provided as the content to the `<mat-checkbox>` element. The label can be
positioned before or after the checkbox by setting the `labelPosition` property to `'before'` or
`'after'`.
Expand All @@ -14,16 +15,19 @@ If you don't want the label to appear next to the checkbox, you can use
specify an appropriate label.

### Use with `@angular/forms`

`<mat-checkbox>` is compatible with `@angular/forms` and supports both `FormsModule`
and `ReactiveFormsModule`.

### Indeterminate state

`<mat-checkbox>` supports an `indeterminate` state, similar to the native `<input type="checkbox">`.
While the `indeterminate` property of the checkbox is true, it will render as indeterminate
regardless of the `checked` value. Any interaction with the checkbox by a user (i.e., clicking) will
remove the indeterminate state.

### Click action config

When user clicks on the `mat-checkbox`, the default behavior is toggle `checked` value and set
`indeterminate` to `false`. This behavior can be customized by
[providing a new value](https://angular.io/guide/dependency-injection)
Expand All @@ -38,22 +42,29 @@ providers: [
The possible values are:

#### `noop`

Do not change the `checked` value or `indeterminate` value. Developers have the power to
implement customized click actions.

#### `check`

Toggle `checked` value of the checkbox, ignore `indeterminate` value. If the
checkbox is in `indeterminate` state, the checkbox will display as an `indeterminate` checkbox
regardless the `checked` value.

#### `check-indeterminate`

Default behavior of `mat-checkbox`. Always set `indeterminate` to `false`
when user click on the `mat-checkbox`.
This matches the behavior of native `<input type="checkbox">`.

### Theming
The color of a `<mat-checkbox>` can be changed by using the `color` property. By default, checkboxes
use the theme's accent color. This can be changed to `'primary'` or `'warn'`.

The color of a `<mat-checkbox>` can be changed by specifying a `$color-variant` when applying the
`mat.checkbox-theme` or `mat.checkbox-color` mixins (see the
[theming guide](/guide/theming#using-component-color-variants) to learn more.) By default,
checkboxes use the theme's primary palette. This can be changed to `'secondary'`, `'tertiary'`,
or `'error'`.

### Accessibility

Expand Down
18 changes: 11 additions & 7 deletions src/material/checkbox/checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,28 @@
* found in the LICENSE file at https://angular.io/license
*/

import {FocusableOption} from '@angular/cdk/a11y';
import {
ANIMATION_MODULE_TYPE,
AfterViewInit,
Attribute,
booleanAttribute,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
forwardRef,
Inject,
Input,
NgZone,
numberAttribute,
OnChanges,
Optional,
Output,
SimpleChanges,
ViewChild,
ViewEncapsulation,
ANIMATION_MODULE_TYPE,
booleanAttribute,
forwardRef,
numberAttribute,
} from '@angular/core';
import {
AbstractControl,
Expand All @@ -36,8 +37,7 @@ import {
ValidationErrors,
Validator,
} from '@angular/forms';
import {_MatInternalFormField, MatRipple} from '@angular/material/core';
import {FocusableOption} from '@angular/cdk/a11y';
import {MatRipple, _MatInternalFormField} from '@angular/material/core';
import {
MAT_CHECKBOX_DEFAULT_OPTIONS,
MAT_CHECKBOX_DEFAULT_OPTIONS_FACTORY,
Expand Down Expand Up @@ -202,7 +202,11 @@ export class MatCheckbox

// TODO(crisbeto): this should be a ThemePalette, but some internal apps were abusing
// the lack of type checking previously and assigning random strings.
/** Palette color of the checkbox. */
/**
* Palette color of the checkbox. This API is supported in M2 themes only, it has no effect in M3
* themes. For information on applying color variants in M3, see
* https://material.angular.io/guide/theming#using-component-color-variants
*/
@Input() color: string | undefined;

/**
Expand Down
Loading