Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Sort] Programatically setting active/direction does not update UI #10242

Open
shahmirn opened this issue Mar 2, 2018 · 50 comments
Open

[Sort] Programatically setting active/direction does not update UI #10242

shahmirn opened this issue Mar 2, 2018 · 50 comments
Labels
area: material/sort P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects

Comments

@shahmirn
Copy link

shahmirn commented Mar 2, 2018

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?

@rsenna
Copy link

rsenna commented Apr 10, 2018

I'm seeing the same issue. Works in 5.1.0, does not in 5.2.*.

@andrewseguin andrewseguin changed the title Setting active and direction on MatSort should update the UI [Sort] Programatically setting active/direction does not update UI Apr 23, 2018
@andrewseguin andrewseguin added the P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent label Apr 23, 2018
@andrewseguin andrewseguin added this to Bugs in Sort Apr 23, 2018
@Hotell
Copy link

Hotell commented May 29, 2018

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

@Ian5015
Copy link

Ian5015 commented May 31, 2018

Encountering this as well in 6.0.0.

@ericbae
Copy link

ericbae commented Jun 25, 2018

Has there been any update to this?

@sergidt
Copy link

sergidt commented Jun 28, 2018

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

@jansivans
Copy link

6.3.3 - bug still here: https://stackblitz.com/edit/angular-a4yakd

@ThierryLehoux
Copy link

Could be usefull when reflect state to URL via query params.

@abbasharoon
Copy link

Any update on this?

@adgoncal
Copy link
Contributor

adgoncal commented Sep 14, 2018

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:

https://github.com/angular/material2/blob/9ab2c905e2e81999347742f880bfd851f9e32022/src/lib/sort/sort-header.ts#L230

https://github.com/angular/material2/blob/9ab2c905e2e81999347742f880bfd851f9e32022/src/lib/sort/sort-header.ts#L145-L159

https://github.com/angular/material2/blob/9ab2c905e2e81999347742f880bfd851f9e32022/src/lib/sort/sort-header.ts#L204-L212

adgoncal added a commit to adgoncal/material2 that referenced this issue Sep 17, 2018
Fix mat-sort-header arrow not displaying after sorting programmatically (eg. `matSort.sort()`)

Related to angular#10242, angular#12754
@ms-dosx86
Copy link

Does not work with v7.0.0

vivian-hu-zz pushed a commit that referenced this issue Nov 6, 2018
Fix mat-sort-header arrow not displaying after sorting programmatically (eg. `matSort.sort()`)

Related to #10242, #12754
@ms-dosx86
Copy link

v7.0.3 works perfectly

@literalpie
Copy link
Contributor

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

@marcusadrian
Copy link

marcusadrian commented Dec 26, 2018

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.

@rzubrycki
Copy link

Hi guys, any update on this one?

@soldierdi
Copy link

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.

@DmitryEfimenko
Copy link

v7.1.1 - still not working.

@LautaroLorenz
Copy link

not working y.y

@Nikkio
Copy link

Nikkio commented Feb 9, 2019

this.sort.active = ''; this.sort.direction = 'asc' as SortDirection;
this.sort.sortChange.emit();

this resets matSort UI without triggering server requests.

@PhilippMeissner
Copy link

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.

@Anderman
Copy link

Anderman commented Mar 7, 2019

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 });
  }

@adgoncal
Copy link
Contributor

adgoncal commented Mar 8, 2019

@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();
  }
}

@Anderman
Copy link

Anderman commented Mar 8, 2019

@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.

@jakubnavratil
Copy link

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.

  1. click handler
  2. sort change subscription - which is called also from click handler (1.)
    .subscribe(() => {

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! :)

@joshcomley
Copy link

This problem still persists. Pinging to get it attention.

@WhiteStarLine
Copy link

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.

@lukekroon
Copy link

<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.

#7838 (comment)

v9.1+

@shahmirn
Copy link
Author

shahmirn commented Jun 3, 2020

@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

@NanduSurendranNair
Copy link

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();

@binson1989
Copy link

binson1989 commented Aug 7, 2020

Still facing this issue with angular material v10.0.1.

@andrewseguin any update on a possible fix?

@ccamba
Copy link

ccamba commented Sep 1, 2020

@Nikkio in my case only with using this.sort.sortChange.emit(); works. Thanks

@codemile
Copy link

codemile commented Oct 10, 2020

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.

@telb99
Copy link

telb99 commented Dec 29, 2020

Still an issue in 10.2.4
Wasted several hours before finding this issue...

@Melmoth-the-Wanderer
Copy link

Melmoth-the-Wanderer commented Feb 20, 2021

Can someone confirm this works in v11?
@andrewseguin please talk to us

edit: this is still an issue in v 11.2.0

@rudzikdawid
Copy link

rudzikdawid commented Mar 19, 2021

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:

  1. Open stackblitz example
  2. Click inside table header in column symbol after that: sort arrow shuld appears inside symbol column
  3. Click in reset filter button below the table
  4. sort arrow is still visible in symbol column header but shuld be in name column header

Description:
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

@stnor
Copy link

stnor commented Oct 6, 2021

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);

@enekonieto
Copy link

Still an issue in 13.1.3

@erksmaas
Copy link

Would be great if this could be fixed

@PhilippMeissner
Copy link

Hold your horses, it's only been 4 years.

@BruneXX
Copy link

BruneXX commented Jun 24, 2022

Wow, this is still an issue.. :´(

@technbuzz
Copy link

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.

@chacabuk
Copy link

chacabuk commented Nov 1, 2022

setTimeout(() => { this.dataSource.sort = this.sort; }, 100);

@PhilippMeissner
Copy link

PhilippMeissner commented Nov 2, 2022

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.

@hunterlan
Copy link

Guys, any plans for this bug? 👀

@daviekr
Copy link

daviekr commented Dec 28, 2022

bump

crisbeto added a commit to crisbeto/material2 that referenced this issue May 24, 2023
…ection change

Fixes that the sort header's state wasn't updating the when active column or the direction changed automatically.

Fixes angular#10242.
crisbeto added a commit to crisbeto/material2 that referenced this issue May 24, 2023
…ection change

Fixes that the sort header's state wasn't updating the when active column or the direction changed automatically.

Fixes angular#10242.
@andrewseguin andrewseguin removed their assignment Jun 3, 2023
@mgremm
Copy link

mgremm commented Feb 26, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: material/sort P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent
Projects
No open projects
Sort
  
Bugs