Skip to content

Commit 6912d75

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 49de56c commit 6912d75

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
@Optional() public _sort: MatSort,
146146
@Inject('MAT_SORT_HEADER_COLUMN_DEF') @Optional()
147147
public _columnDef: MatSortHeaderColumnDef,
@@ -157,20 +157,7 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
157157
throw getSortHeaderNotContainedWithinSortError();
158158
}
159159

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

176163
ngOnInit() {
@@ -236,27 +223,17 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
236223

237224
/** Triggers the sort on this sort header and removes the indicator hint. */
238225
_toggleOnInteraction() {
239-
240226
this._sort.sort(this);
241227

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

257234
_handleClick() {
258235
if (!this._isDisabled()) {
259-
this._toggleOnInteraction();
236+
this._sort.sort(this);
260237
}
261238
}
262239

@@ -323,6 +300,32 @@ export class MatSortHeader extends _MatSortHeaderMixinBase
323300
return !this._isDisabled() || this._isSorted();
324301
}
325302

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

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)