Skip to content

Commit 5c8d760

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 89b5fa8 commit 5c8d760

File tree

3 files changed

+39
-27
lines changed

3 files changed

+39
-27
lines changed

src/material/sort/sort-header.ts

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
141141
private _disableClear: boolean;
142142

143143
constructor(public _intl: MatSortHeaderIntl,
144-
changeDetectorRef: ChangeDetectorRef,
144+
private _changeDetectorRef: ChangeDetectorRef,
145145
// `MatSort` is not optionally injected, but just asserted manually w/ better error.
146146
// tslint:disable-next-line: lightweight-tokens
147147
@Optional() public _sort: MatSort,
@@ -159,20 +159,7 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
159159
throw getSortHeaderNotContainedWithinSortError();
160160
}
161161

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

178165
ngOnInit() {
@@ -238,27 +225,17 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
238225

239226
/** Triggers the sort on this sort header and removes the indicator hint. */
240227
_toggleOnInteraction() {
241-
242228
this._sort.sort(this);
243229

244230
// Do not show the animation if the header was already shown in the right position.
245231
if (this._viewState.toState === 'hint' || this._viewState.toState === 'active') {
246232
this._disableViewStateAnimation = true;
247233
}
248-
249-
// If the arrow is now sorted, animate the arrow into place. Otherwise, animate it away into
250-
// the direction it is facing.
251-
const viewState: ArrowViewStateTransition = this._isSorted() ?
252-
{fromState: this._arrowDirection, toState: 'active'} :
253-
{fromState: 'active', toState: this._arrowDirection};
254-
this._setAnimationTransitionState(viewState);
255-
256-
this._showIndicatorHint = false;
257234
}
258235

259236
_handleClick() {
260237
if (!this._isDisabled()) {
261-
this._toggleOnInteraction();
238+
this._sort.sort(this);
262239
}
263240
}
264241

@@ -325,6 +302,32 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
325302
return !this._isDisabled() || this._isSorted();
326303
}
327304

305+
/** Handles changes in the sorting state. */
306+
private _handleStateChanges() {
307+
this._rerenderSubscription =
308+
merge(this._sort.sortChange, this._sort._stateChanges, this._intl.changes).subscribe(() => {
309+
if (this._isSorted()) {
310+
this._updateArrowDirection();
311+
312+
// Do not show the animation if the header was already shown in the right position.
313+
if (this._viewState.toState === 'hint' || this._viewState.toState === 'active') {
314+
this._disableViewStateAnimation = true;
315+
}
316+
317+
this._setAnimationTransitionState({fromState: this._arrowDirection, toState: 'active'});
318+
this._showIndicatorHint = false;
319+
}
320+
321+
// If this header was recently active and now no longer sorted, animate away the arrow.
322+
if (!this._isSorted() && this._viewState && this._viewState.toState === 'active') {
323+
this._disableViewStateAnimation = false;
324+
this._setAnimationTransitionState({fromState: 'active', toState: this._arrowDirection});
325+
}
326+
327+
this._changeDetectorRef.markForCheck();
328+
});
329+
}
330+
328331
static ngAcceptInputType_disableClear: BooleanInput;
329332
static ngAcceptInputType_disabled: BooleanInput;
330333
}

src/material/sort/sort.spec.ts

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

200209
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(): "none" | "ascending" | "descending";
6969
_getArrowDirectionState(): string;
7070
_getArrowViewState(): string;

0 commit comments

Comments
 (0)