Open shahmirn opened 6 years ago
I'm seeing the same issue. Works in 5.1.0, does not in 5.2.*.
Same issue here, it's kinda deal breaker, as you cannot set values dynamically or imperatively. I'm currently implementing a data table which reflects state to URL via query params, everything works ( even pagination set dynamically, cannot say the same for sort )
doesn't work with 5.2.5 nor 6.x
demo: https://stackblitz.com/edit/github-issue-ng-material-sorting-10242
Encountering this as well in 6.0.0.
Has there been any update to this?
same here!!
Is a very important feature and is not working. I attach and example where I change the sort field dynamically after two seconds and you can see what undesired behavior is shown:
https://stackblitz.com/edit/angular-owjp47?file=app/sort-overview-example.html
6.3.3 - bug still here: https://stackblitz.com/edit/angular-a4yakd
Could be usefull when reflect state to URL via query params.
Any update on this?
I spent some time debugging this. Here are my observations:
When you click on a mat-sort-header
that is not currently active, eventually it will call _setAnimationTransitionState({fromState: '<direction>', toState: 'active'})
on the clicked mat-sort-header
.
However, when you programmatically call matSort.sort({...})
, the mat-sort-header
that should become active never calls _setAnimationTransitionState()
.
I believe this is an issue on _rerenderSubscription()
.
I managed to make it work with the following ugly hack:
this.matSort.sort({
id: <value>,
start: <direction>,
disableClear: true,
});
// ugly hack!
const activeSortHeader = this.matSort.sortables.get(value);
activeSortHeader['_setAnimationTransitionState']({
fromState: <direction>,
toState: 'active',
});
https://stackblitz.com/edit/angular-vuvckf?file=app%2Ftable-overview-example.ts
References:
Does not work with v7.0.0
v7.0.3 works perfectly
I believe the original intent of this issue was that mat-sort does not respond to programatic changes in active and direction. The discussion on this issue seems to be assuming that the sorting is done by calling the sort
function, not changing the two attributes.
stackblitz that shows the issue I am talking about
has this issue been fully changed to just be about the header not updating? If so, I will create a new issue for updating sort via active
and direction
Still not working in version 7.1.0, arrow doesn't update showing the wrong sort column. Strangely disappears on wrong column when hovering over without appearing on right column.
Hi guys, any update on this one?
Hello everyone,
does anyone know how the input properties "matSortActive" and "matSortDirection" for the matSort directive work. When I set them the respective sort arrow is not updated. It would be nice to update the sort arrows with the help of observables using these two input properties.
v7.1.1 - still not working.
not working y.y
this.sort.active = ''; this.sort.direction = 'asc' as SortDirection;
this.sort.sortChange.emit();
this resets matSort UI without triggering server requests.
this.sort.active = ''; this.sort.direction = 'asc' as SortDirection; this.sort.sortChange.emit();
this resets matSort UI without triggering server requests.
Well, this is basically the same solution as @austin5456 provided but remains a workaround and should not be necessary in a perfect world.
The ArrowIcon is set in the clickhandler of matSortHeader. Is see only two ugly hacks. add a stylesheet entry
::ng-deep .mat-sort-header-sorted .mat-sort-header-arrow {
opacity: 1 !important;
}
or call
this.matSortHeader._setAnimationTransitionState({ toState: 'active' });
You can use something like this.
@ViewChild(MatSort) public matSort: MatSort;
and
public setSort(id: string, start?: 'asc' | 'desc') {
start = start || 'asc';
const matSort = this.dataSource.sort;
const toState = 'active';
const disableClear = false;
//reset state so that start is the first sort direction that you will see
matSort.sort({ id: null, start, disableClear });
matSort.sort({ id, start, disableClear });
//ugly hack
(matSort.sortables.get(id) as MatSortHeader)._setAnimationTransitionState({ toState });
}
@Anderman that is very similar to what I have been using for a few months:
import { Component, AfterViewInit, OnDestroy } from '@angular/core';
import { Sort, MatSort, MatSortHeader } from '@angular/material';
import { Subject, Observable, of } from 'rxjs';
import {
catchError,
distinctUntilChanged,
map,
takeUntil,
withLatestFrom,
} from 'rxjs/operators';
@Component({
// ...
})
export class MyComponent implements AfterViewInit, OnDestroy {
@ViewChild(MatSort) matSort: MatSort;
private destroyed$ = new Subject<void>();
private sort$: Observable<Sort>;
ngAfterViewInit() {
// HACK: fix sort header arrow after programmatically changing sort order
// https://github.com/angular/material2/issues/10242
// TODO: Remove this hack when the bug is fixed on @angular/material
this.matSort._stateChanges
.pipe(
withLatestFrom(this.sort$),
map(([_, sort]) => sort),
takeUntil(this.destroyed$),
catchError(error => {
return of(error);
})
)
.subscribe((sort: Sort) => {
const sortables = this.matSort.sortables;
const activeSortHeader = <MatSortHeader>(
sortables.get(sort.active)
);
activeSortHeader._setAnimationTransitionState({
fromState: activeSortHeader._arrowDirection,
toState: 'active',
});
});
}
ngOnDestroy() {
this.destroyed$.next();
this.destroyed$.complete();
}
}
@adgoncal In some messages the problem seems to be solved or suggests other solutions that do not work. I spent to much time on this problem by trying some of those solutions. At least it was not clear for me how to make a workarond.
Hi all, sorry for my poor English skill , but I wanted to sharing my solution.
I also encountered this bug and tried use _setAnimationTransitionState Func, But I found the UI transition is not smooth, ( U need mouse focus then the arrow will change ...)
and then , I tried use _handleClick() function ( it seems can trigger click target column's click event )
so , i provider another dirty hack : The point is set "opposite direction" which you want the arrow be
this.sort.active = _TARGET_HEADER_NAME_
// I want set the arrow as 'asc', so set the direction 'desc' once,
// then call _handleClick to trigger click event
this.sort.direction = 'desc' as SortDirection
const _SortHeader = this.sort.sortables.get('_TARGET_HEADER_NAME_') as MatSortHeader
_SortHeader._handleClick()
If your sort using server , need add a flag to control server communication like this :
// _doNotRequestToServer is the flag
this.sort.sortChange.subscribe(
() => {
return doSort([ {
[this.sort.active]: this.sort.direction
} ], this._doNotRequestToServer);
}
);
I wish maybe it can help u :)
any update on this issue ? 8.0 problem same...
using 8.1.1 still getting the same issue
The ArrowIcon is set in the clickhandler of matSortHeader. Is see only two ugly hacks. add a stylesheet entry
::ng-deep .mat-sort-header-sorted .mat-sort-header-arrow { opacity: 1 !important; }
or call
this.matSortHeader._setAnimationTransitionState({ toState: 'active' });
You can use something like this.
@ViewChild(MatSort) public matSort: MatSort;
and
public setSort(id: string, start?: 'asc' | 'desc') { start = start || 'asc'; const matSort = this.dataSource.sort; const toState = 'active'; const disableClear = false; //reset state so that start is the first sort direction that you will see matSort.sort({ id: null, start, disableClear }); matSort.sort({ id, start, disableClear }); //ugly hack (matSort.sortables.get(id) as MatSortHeader)._setAnimationTransitionState({ toState }); }
This is the best workaround for the timebeing
Hi guys,
Here is my implementation for this issue.
You can control the exact sorting behavior(ASC / DESC / CLEAR) with this code. I wrapped the logic with 'if statement' to prevent the change of 'matSort' after certain sorting behavior executed.
@ViewChild(MatSort, { static: false }) matSort: MatSort;
sortData(id: string, start?: 'asc' | 'desc') {
const disableClear = false;
const currentDirection = this.matSort.direction;
if (start === 'asc' && currentDirection !== 'asc') {
this.matSort.sort({ id: null, start, disableClear });
this.matSort.sort({ id, start, disableClear });
} else if (start === 'desc' && currentDirection !== 'desc') {
this.matSort.sort({ id: null, start, disableClear });
this.matSort.sort({ id, start, disableClear });
}
}
clearSort(id: string) {
if (this.matSort.direction === 'asc') {
this.matSort.sort({ id, start: 'desc', disableClear: false });
} else if (this.matSort.direction === 'desc') {
this.matSort.sort({ id, start: 'asc', disableClear: false });
}
}
<div mat-menu-item mat-filter-item [disableRipple]="true">
<button mat-raised-button (click)="sortData('name', 'asc')">ASC</button>
<button mat-raised-button (click)="sortData('name', 'desc')">DESC</button>
<button mat-raised-button (click)="clearSort('name')">Clear</button>
</div>
For your info, check this out in StackBlitz
Thanks.
Hi Andrew @andrewseguin :)
Can we prioritize this issue a little more? According to reactions, this is most wanted FIX for Sort
component (https://github.com/angular/components/issues?utf8=%E2%9C%93&q=project%3Aangular%2Fcomponents%2F18+is%3Aopen+sort%3Areactions-%2B1-desc+)
API reveals sort
method, which works fine, the only thing missing is call for updating animation state - so actual arrow is hidden after sorting programaticaly.
In SortHeaderComponent, there are two places, where UI is updated.
To fix this, we just need to move most of the click handler logic to subscription logic, as it is also called, becase click handler calls that sort
method.
This way everything is handled in subscription and click handler only calls sort
method.
So we can move this logic from click handler:
// Do not show the animation if the header was already shown in the right position.
if (this._viewState.toState === 'hint' || this._viewState.toState === 'active') {
this._disableViewStateAnimation = true;
}
// If the arrow is now sorted, animate the arrow into place. Otherwise, animate it away into
// the direction it is facing.
const viewState: ArrowViewStateTransition = this._isSorted() ?
{fromState: this._arrowDirection, toState: 'active'} :
{fromState: 'active', toState: this._arrowDirection};
this._setAnimationTransitionState(viewState);
To subscription handler after these lines
if (this._isSorted()) {
this._updateArrowDirection();
}
and run it only when condition this._sort.active == this.id
is met.
There propably needs to be one more condition or something. but the main idea is that this is actually really easy fix for the most wanted issue of Sort component.
Thanks for considering this and thanks for your hard work at Angular team, this product is amazing! :)
This problem still persists. Pinging to get it attention.
Just spent 2 days trying to get a server-side change to update the UI. It only worked if the user clicked on a sort arrow. JayAhn2's code describes the problem as I see it and provides a workaround that worked immediately. Hope this gets addressed at some stage. Cheers.
<table matSort (matSortChange)="sortData($event)" [matSortActive]="sortActive" [matSortDirection]="sortDirection" matSortDisableClear>
Setting the variables sortActive and sortDirection in the component does the job for me. It updates the arrow as well.
https://github.com/angular/components/issues/7838#issuecomment-380380985
v9.1+
@lukekroon It doesn't work in the same scenario as the original bug, which is code in the callback of .subscribe()
https://stackblitz.com/edit/angular-material2-issue-qjaqm4?file=app%2Fapp.component.ts
I have tried with the below snippet and it is working as expected. Once the state of the material table is set, then executing it made the UI to update the sort arrow.
Using angular material version 7.3.7 Here id refers to the current active sort column name.
(matSort.sortables.get(id) as MatSortHeader)._updateArrowDirection();
Still facing this issue with angular material v10.0.1.
@andrewseguin any update on a possible fix?
@Nikkio in my case only with using this.sort.sortChange.emit(); works. Thanks
Hi everyone,
I've found probably the easiest workaround for this issue. The only side effect is that it requires the table to be redrawn when sort changes, but it's extremely simple to implement and works in most cases.
<table *ngFor="let hack of [sort]"
mat-table
matSort
[matSortActive]="sort?.active"
(matSortChange)="setSort($event)"
[matSortDirection]="sort?.direction">
When ever you change the sort: Sort
property on the component in an immutable way, then it will trigger the <table>
to be recreated and the matSort
component renders the active sort header correctly, because it does render it correctly the first time.
I find this solution is better future proofed, because when they later fix this issue, then you can just remove the *ngFor
and it should continue to work without the performance lag.
I tried many of the above hacks, but suffered from many other side effects in my source code from trying to handle both external and internal sort change events. The above hack solves those issues, because (matSortChange)
does not emit a value for the first time the table is rendered. So it only fires when the user clicks on a header, and doesn't suffer from feedback loops some of the higher solutions introduced in my source code.
Still an issue in 10.2.4 Wasted several hours before finding this issue...
Can someone confirm this works in v11? @andrewseguin please talk to us
edit: this is still an issue in v 11.2.0
angular 11.2.6 material 11.2.5
works better but still there is a little problem with refresh ui after model matSortActive
, matSortDirection
change:
https://stackblitz.com/edit/angular-i8z8ny
Issue:
symbol
after that: sort arrow
shuld appears inside symbol
columnreset filter
button below the tablesort arrow
is still visible in symbol
column header but shuld be in name
column headerDescription:
button reset filter
triggers method onResetFilter
from parent controller.
onResetFilter() {
this.matSortActive = "name";
this.matSortDirection = "asc";
}
calling this method sometimes triggers ui changes, sometimes not.
@pavelmarozau @andrewseguin
I had a repaint issue. This solved it for me:
const sortHeader: MatSortHeader = this.sort.sortables.get(this.sort.active) as MatSortHeader;
sortHeader._setAnimationTransitionState({fromState: sortHeader._arrowDirection, toState: 'active'});
this.sort.sortChange.emit(sort);
Still an issue in 13.1.3
Would be great if this could be fixed
Hold your horses, it's only been 4 years.
Wow, this is still an issue.. :´(
I have a table with sorting, filtering, tabs, pagination with server where it is run by URL being source of truth. Everything works but this bug is ruining all it's potential. I wonder what's stopping its implementation.
setTimeout(() => { this.dataSource.sort = this.sort; }, 100);
setTimeout(() => { this.dataSource.sort = this.sort; }, 100);
If you really want to rely on setTimeout
you may as well set the delay to 0
. The callback will still be put in the appropriate queue and called, but without the assumingly unneeded delay.
Guys, any plans for this bug? 👀
bump
Upgrading from Angular 7 to 15 broke my mat-table's programmatically determined sort. I tried many things, but in the end I fixed my programmatically generated sort by changing
@ViewChild(MatSort)
matSort: MatSort = new MatSort;
to
@ViewChild(MatSort, { static: true }) matSort: MatSort;
No this.sort.sortChange.emit(sort);
needed.
Bug, feature request, or proposal:
I'm programmatically setting the active and direction on matSort, but it's not updating the UI
What is the expected behavior?
programmatically setting the active and direction on matSort updates the UI
What is the current behavior?
programmatically setting the active and direction on matSort, but it's not updating the UI
What are the steps to reproduce?
https://stackblitz.com/edit/angular-material2-issue-mc4cve?file=app/app.component.ts
What is the use-case or motivation for changing an existing behavior?
I believe this is a regression. This was working in 5.1.0
Which versions of Angular, Material, OS, TypeScript, browsers are affected?
Is there anything else we should know?