components icon indicating copy to clipboard operation
components copied to clipboard

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

Open shahmirn opened this issue 7 years ago • 50 comments
trafficstars

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?

shahmirn avatar Mar 02 '18 17:03 shahmirn

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

rsenna avatar Apr 10 '18 12:04 rsenna

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

Hotell avatar May 29 '18 08:05 Hotell

Encountering this as well in 6.0.0.

Ian5015 avatar May 31 '18 16:05 Ian5015

Has there been any update to this?

ericbae avatar Jun 25 '18 11:06 ericbae

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

sergidt avatar Jun 28 '18 14:06 sergidt

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

jansivans avatar Jul 11 '18 07:07 jansivans

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

ThierryLehoux avatar Aug 06 '18 10:08 ThierryLehoux

Any update on this?

abbasharoon avatar Aug 10 '18 15:08 abbasharoon

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 avatar Sep 14 '18 21:09 adgoncal

Does not work with v7.0.0

ms-dosx86 avatar Oct 27 '18 04:10 ms-dosx86

v7.0.3 works perfectly

ms-dosx86 avatar Nov 07 '18 05:11 ms-dosx86

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

literalpie avatar Nov 27 '18 15:11 literalpie

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.

marcusadrian avatar Dec 26 '18 16:12 marcusadrian

Hi guys, any update on this one?

rzubrycki avatar Jan 08 '19 19:01 rzubrycki

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.

soldierdi avatar Jan 12 '19 21:01 soldierdi

v7.1.1 - still not working.

DmitryEfimenko avatar Feb 07 '19 08:02 DmitryEfimenko

not working y.y

LautaroLorenz avatar Feb 07 '19 19:02 LautaroLorenz

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

this resets matSort UI without triggering server requests.

Nikkio avatar Feb 09 '19 06:02 Nikkio

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.

PhilippMeissner avatar Feb 11 '19 08:02 PhilippMeissner

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 avatar Mar 07 '19 22:03 Anderman

@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 avatar Mar 08 '19 14:03 adgoncal

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

Anderman avatar Mar 08 '19 14:03 Anderman

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

iamFujiYama avatar Apr 01 '19 09:04 iamFujiYama

any update on this issue ? 8.0 problem same...

keatkeat87 avatar Jun 11 '19 11:06 keatkeat87

using 8.1.1 still getting the same issue

devkrish94 avatar Sep 19 '19 06:09 devkrish94

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

sharan-zweck avatar Sep 26 '19 11:09 sharan-zweck

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.

JayAhn2 avatar Nov 25 '19 13:11 JayAhn2

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 https://github.com/angular/components/blob/198911f5c0dd198f42ee0476a3291ec9858a0660/src/material/sort/sort-header.ts#L243
  2. sort change subscription - which is called also from click handler (1.) https://github.com/angular/components/blob/198911f5c0dd198f42ee0476a3291ec9858a0660/src/material/sort/sort-header.ts#L162

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

jakubnavratil avatar Feb 18 '20 22:02 jakubnavratil

This problem still persists. Pinging to get it attention.

joshcomley avatar Apr 28 '20 11:04 joshcomley

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.

WhiteStarLine avatar May 30 '20 13:05 WhiteStarLine