Skip to content

Commit 62b0e96

Browse files
committed
fix(sort): view not updated when sort state is changed through binding
Fixes the visible state of the sort header not being updated if it gets changed through the `matSortActive` binding. Fixes #19467.
1 parent 96c24f5 commit 62b0e96

File tree

3 files changed

+40
-32
lines changed

3 files changed

+40
-32
lines changed

src/material/sort/sort-header.ts

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
138138
private _disableClear: boolean;
139139

140140
constructor(public _intl: MatSortHeaderIntl,
141-
changeDetectorRef: ChangeDetectorRef,
141+
private _changeDetectorRef: ChangeDetectorRef,
142142
@Optional() public _sort: MatSort,
143143
@Inject('MAT_SORT_HEADER_COLUMN_DEF') @Optional()
144144
public _columnDef: MatSortHeaderColumnDef,
@@ -154,20 +154,7 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
154154
throw getSortHeaderNotContainedWithinSortError();
155155
}
156156

157-
this._rerenderSubscription = merge(_sort.sortChange, _sort._stateChanges, _intl.changes)
158-
.subscribe(() => {
159-
if (this._isSorted()) {
160-
this._updateArrowDirection();
161-
}
162-
163-
// If this header was recently active and now no longer sorted, animate away the arrow.
164-
if (!this._isSorted() && this._viewState && this._viewState.toState === 'active') {
165-
this._disableViewStateAnimation = false;
166-
this._setAnimationTransitionState({fromState: 'active', toState: this._arrowDirection});
167-
}
168-
169-
changeDetectorRef.markForCheck();
170-
});
157+
this._handleStateChanges();
171158

172159
// We use the focus monitor because we also want to style
173160
// things differently based on the focus origin.
@@ -231,23 +218,9 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
231218

232219
/** Triggers the sort on this sort header and removes the indicator hint. */
233220
_handleClick() {
234-
if (this._isDisabled()) { return; }
235-
236-
this._sort.sort(this);
237-
238-
// Do not show the animation if the header was already shown in the right position.
239-
if (this._viewState.toState === 'hint' || this._viewState.toState === 'active') {
240-
this._disableViewStateAnimation = true;
221+
if (!this._isDisabled()) {
222+
this._sort.sort(this);
241223
}
242-
243-
// If the arrow is now sorted, animate the arrow into place. Otherwise, animate it away into
244-
// the direction it is facing.
245-
const viewState: ArrowViewStateTransition = this._isSorted() ?
246-
{fromState: this._arrowDirection, toState: 'active'} :
247-
{fromState: 'active', toState: this._arrowDirection};
248-
this._setAnimationTransitionState(viewState);
249-
250-
this._showIndicatorHint = false;
251224
}
252225

253226
/** Whether this MatSortHeader is currently sorted in either ascending or descending order. */
@@ -304,6 +277,32 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
304277
return !this._isDisabled() || this._isSorted();
305278
}
306279

280+
/** Handles changes in the sorting state. */
281+
private _handleStateChanges() {
282+
this._rerenderSubscription =
283+
merge(this._sort.sortChange, this._sort._stateChanges, this._intl.changes).subscribe(() => {
284+
if (this._isSorted()) {
285+
this._updateArrowDirection();
286+
287+
// Do not show the animation if the header was already shown in the right position.
288+
if (this._viewState.toState === 'hint' || this._viewState.toState === 'active') {
289+
this._disableViewStateAnimation = true;
290+
}
291+
292+
this._setAnimationTransitionState({fromState: this._arrowDirection, toState: 'active'});
293+
this._showIndicatorHint = false;
294+
}
295+
296+
// If this header was recently active and now no longer sorted, animate away the arrow.
297+
if (!this._isSorted() && this._viewState && this._viewState.toState === 'active') {
298+
this._disableViewStateAnimation = false;
299+
this._setAnimationTransitionState({fromState: 'active', toState: this._arrowDirection});
300+
}
301+
302+
this._changeDetectorRef.markForCheck();
303+
});
304+
}
305+
307306
static ngAcceptInputType_disableClear: BooleanInput;
308307
static ngAcceptInputType_disabled: BooleanInput;
309308
}

src/material/sort/sort.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,15 @@ describe('MatSort', () => {
196196
component.dispatchMouseEvent('defaultA', 'mouseenter');
197197
component.expectViewAndDirectionStates(expectedStates);
198198
});
199+
200+
it('should be correct when sorting programmatically', () => {
201+
component.active = 'defaultB';
202+
component.direction = 'asc';
203+
fixture.detectChanges();
204+
205+
expectedStates.set('defaultB', {viewState: 'asc-to-active', arrowDirection: 'active-asc'});
206+
component.expectViewAndDirectionStates(expectedStates);
207+
});
199208
});
200209

201210
it('should be able to cycle from asc -> desc from either start point', () => {

tools/public_api_guard/material/sort.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export declare class MatSortHeader extends _MatSortHeaderMixinBase implements Ca
6464
set disableClear(v: boolean);
6565
id: string;
6666
start: 'asc' | 'desc';
67-
constructor(_intl: MatSortHeaderIntl, changeDetectorRef: ChangeDetectorRef, _sort: MatSort, _columnDef: MatSortHeaderColumnDef, _focusMonitor: FocusMonitor, _elementRef: ElementRef<HTMLElement>);
67+
constructor(_intl: MatSortHeaderIntl, _changeDetectorRef: ChangeDetectorRef, _sort: MatSort, _columnDef: MatSortHeaderColumnDef, _focusMonitor: FocusMonitor, _elementRef: ElementRef<HTMLElement>);
6868
_getAriaSortAttribute(): "ascending" | "descending" | null;
6969
_getArrowDirectionState(): string;
7070
_getArrowViewState(): string;

0 commit comments

Comments
 (0)