diff --git a/src/lib/sort/sort-header.scss b/src/lib/sort/sort-header.scss
index 088a5429f68e..da39b1a0c910 100644
--- a/src/lib/sort/sort-header.scss
+++ b/src/lib/sort/sort-header.scss
@@ -8,6 +8,10 @@ $mat-sort-header-arrow-transition: 225ms cubic-bezier(0.4, 0, 0.2, 1);
.mat-sort-header-container {
display: flex;
cursor: pointer;
+
+ .mat-sort-header-disabled & {
+ cursor: default;
+ }
}
.mat-sort-header-position-before {
@@ -20,7 +24,7 @@ $mat-sort-header-arrow-transition: 225ms cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
align-items: center;
padding: 0;
- cursor: pointer;
+ cursor: inherit;
outline: 0;
font: inherit;
color: currentColor;
diff --git a/src/lib/sort/sort-header.ts b/src/lib/sort/sort-header.ts
index 67e395b3906e..62a7babbdce6 100644
--- a/src/lib/sort/sort-header.ts
+++ b/src/lib/sort/sort-header.ts
@@ -30,10 +30,18 @@ import {MatSort, MatSortable} from './sort';
import {MatSortHeaderIntl} from './sort-header-intl';
import {getSortHeaderNotContainedWithinSortError} from './sort-errors';
import {AnimationCurves, AnimationDurations} from '@angular/material/core';
+import {CanDisable, mixinDisabled} from '@angular/material/core';
+
const SORT_ANIMATION_TRANSITION =
AnimationDurations.ENTERING + ' ' + AnimationCurves.STANDARD_CURVE;
+// Boilerplate for applying mixins to the sort header.
+/** @docs-private */
+export class MatSortHeaderBase {}
+export const _MatSortHeaderMixinBase = mixinDisabled(MatSortHeaderBase);
+
+
/**
* Applies sorting behavior (click to change sort) and styles to an element, including an
* arrow to display the current sort direction.
@@ -50,12 +58,14 @@ const SORT_ANIMATION_TRANSITION =
templateUrl: 'sort-header.html',
styleUrls: ['sort-header.css'],
host: {
- '(click)': '_sort.sort(this)',
+ '(click)': '_handleClick()',
'[class.mat-sort-header-sorted]': '_isSorted()',
+ '[class.mat-sort-header-disabled]': '_isDisabled()',
},
encapsulation: ViewEncapsulation.None,
preserveWhitespaces: false,
changeDetection: ChangeDetectionStrategy.OnPush,
+ inputs: ['disabled'],
animations: [
trigger('indicator', [
state('asc', style({transform: 'translateY(0px)'})),
@@ -93,7 +103,7 @@ const SORT_ANIMATION_TRANSITION =
])
]
})
-export class MatSortHeader implements MatSortable {
+export class MatSortHeader extends _MatSortHeaderMixinBase implements MatSortable, CanDisable {
private _rerenderSubscription: Subscription;
/**
@@ -118,13 +128,15 @@ export class MatSortHeader implements MatSortable {
changeDetectorRef: ChangeDetectorRef,
@Optional() public _sort: MatSort,
@Optional() public _cdkColumnDef: CdkColumnDef) {
+
+ super();
+
if (!_sort) {
throw getSortHeaderNotContainedWithinSortError();
}
- this._rerenderSubscription = merge(_sort.sortChange, _intl.changes).subscribe(() => {
- changeDetectorRef.markForCheck();
- });
+ this._rerenderSubscription = merge(_sort.sortChange, _sort._stateChanges, _intl.changes)
+ .subscribe(() => changeDetectorRef.markForCheck());
}
ngOnInit() {
@@ -140,9 +152,20 @@ export class MatSortHeader implements MatSortable {
this._rerenderSubscription.unsubscribe();
}
+ /** Handles click events on the header. */
+ _handleClick() {
+ if (!this._isDisabled()) {
+ this._sort.sort(this);
+ }
+ }
+
/** Whether this MatSortHeader is currently sorted in either ascending or descending order. */
_isSorted() {
return this._sort.active == this.id &&
(this._sort.direction === 'asc' || this._sort.direction === 'desc');
}
+
+ _isDisabled() {
+ return this._sort.disabled || this.disabled;
+ }
}
diff --git a/src/lib/sort/sort.md b/src/lib/sort/sort.md
index 5b331deeb797..8844a88f13c8 100644
--- a/src/lib/sort/sort.md
+++ b/src/lib/sort/sort.md
@@ -19,14 +19,19 @@ direction to sort (`asc` or `desc`).
By default, a sort header starts its sorting at `asc` and then `desc`. Triggering the sort header
after `desc` will remove sorting.
-To reverse the sort order for all headers, set the `matSortStart` to `desc` on the `matSort`
-directive. To reverse the order only for a specific header, set the `start` input only on the header
+To reverse the sort order for all headers, set the `matSortStart` to `desc` on the `matSort`
+directive. To reverse the order only for a specific header, set the `start` input only on the header
instead.
-To prevent the user from clearing the sort sort state from an already sorted column, set
-`matSortDisableClear` to `true` on the `matSort` to affect all headers, or set `disableClear` to
+To prevent the user from clearing the sort sort state from an already sorted column, set
+`matSortDisableClear` to `true` on the `matSort` to affect all headers, or set `disableClear` to
`true` on a specific header.
+#### Disabling sorting
+
+If you want to prevent the user from changing the sorting order of any column, you can use the
+`matSortDisabled` binding on the `mat-sort`, or the `disabled` on an single `mat-sort-header`.
+
#### Using sort with the mat-table
When used on an `mat-table` header, it is not required to set an `mat-sort-header` id on because
diff --git a/src/lib/sort/sort.spec.ts b/src/lib/sort/sort.spec.ts
index ca214c4ae735..182c5e6c9dd0 100644
--- a/src/lib/sort/sort.spec.ts
+++ b/src/lib/sort/sort.spec.ts
@@ -109,6 +109,43 @@ describe('MatSort', () => {
testSingleColumnSortDirectionSequence(fixture, ['desc', 'asc', '']);
});
+ it('should allow for the cycling the sort direction to be disabled per column', () => {
+ const button = fixture.nativeElement.querySelector('#defaultSortHeaderA button');
+
+ component.sort('defaultSortHeaderA');
+ expect(component.matSort.direction).toBe('asc');
+ expect(button.getAttribute('disabled')).toBeFalsy();
+
+ component.disabledColumnSort = true;
+ fixture.detectChanges();
+
+ component.sort('defaultSortHeaderA');
+ expect(component.matSort.direction).toBe('asc');
+ expect(button.getAttribute('disabled')).toBe('true');
+ });
+
+ it('should allow for the cycling the sort direction to be disabled for all columns', () => {
+ const button = fixture.nativeElement.querySelector('#defaultSortHeaderA button');
+
+ component.sort('defaultSortHeaderA');
+ expect(component.matSort.active).toBe('defaultSortHeaderA');
+ expect(component.matSort.direction).toBe('asc');
+ expect(button.getAttribute('disabled')).toBeFalsy();
+
+ component.disableAllSort = true;
+ fixture.detectChanges();
+
+ component.sort('defaultSortHeaderA');
+ expect(component.matSort.active).toBe('defaultSortHeaderA');
+ expect(component.matSort.direction).toBe('asc');
+ expect(button.getAttribute('disabled')).toBe('true');
+
+ component.sort('defaultSortHeaderB');
+ expect(component.matSort.active).toBe('defaultSortHeaderA');
+ expect(component.matSort.direction).toBe('asc');
+ expect(button.getAttribute('disabled')).toBe('true');
+ });
+
it('should reset sort direction when a different column is sorted', () => {
component.sort('defaultSortHeaderA');
expect(component.matSort.active).toBe('defaultSortHeaderA');
@@ -211,11 +248,16 @@ function testSingleColumnSortDirectionSequence(fixture: ComponentFixture
-
+
A
@@ -233,6 +275,8 @@ class SimpleMatSortApp {
start: SortDirection = 'asc';
direction: SortDirection = '';
disableClear: boolean;
+ disabledColumnSort = false;
+ disableAllSort = false;
@ViewChild(MatSort) matSort: MatSort;
@ViewChild('defaultSortHeaderA') matSortHeaderDefaultA: MatSortHeader;
diff --git a/src/lib/sort/sort.ts b/src/lib/sort/sort.ts
index 355df422c7b5..4ad410ff887e 100644
--- a/src/lib/sort/sort.ts
+++ b/src/lib/sort/sort.ts
@@ -6,14 +6,24 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Directive, EventEmitter, Input, isDevMode, Output} from '@angular/core';
+import {
+ Directive,
+ EventEmitter,
+ Input,
+ isDevMode,
+ Output,
+ OnChanges,
+ OnDestroy,
+} from '@angular/core';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
+import {CanDisable, mixinDisabled} from '@angular/material/core';
import {SortDirection} from './sort-direction';
import {
getSortInvalidDirectionError,
getSortDuplicateSortableIdError,
getSortHeaderMissingIdError
} from './sort-errors';
+import {Subject} from 'rxjs/Subject';
/** Interface for a directive that holds sorting state consumed by `MatSortHeader`. */
export interface MatSortable {
@@ -36,15 +46,24 @@ export interface Sort {
direction: SortDirection;
}
+// Boilerplate for applying mixins to MatSort.
+/** @docs-private */
+export class MatSortBase {}
+export const _MatSortMixinBase = mixinDisabled(MatSortBase);
+
/** Container for MatSortables to manage the sort state and provide default sort parameters. */
@Directive({
selector: '[matSort]',
- exportAs: 'matSort'
+ exportAs: 'matSort',
+ inputs: ['disabled: matSortDisabled']
})
-export class MatSort {
+export class MatSort extends _MatSortMixinBase implements CanDisable, OnChanges, OnDestroy {
/** Collection of all registered sortables that this directive manages. */
sortables = new Map();
+ /** Used to notify any child components listening to state changes. */
+ _stateChanges = new Subject();
+
/** The id of the most recently sorted MatSortable. */
@Input('matSortActive') active: string;
@@ -125,6 +144,14 @@ export class MatSort {
if (nextDirectionIndex >= sortDirectionCycle.length) { nextDirectionIndex = 0; }
return sortDirectionCycle[nextDirectionIndex];
}
+
+ ngOnChanges() {
+ this._stateChanges.next();
+ }
+
+ ngOnDestroy() {
+ this._stateChanges.complete();
+ }
}
/** Returns the sort direction cycle to use given the provided parameters of order and clear. */