mattiash / angular-tablesort

Sort angularjs tables easily
MIT License
184 stars 66 forks source link

Programatically change sort option #88

Open pirtleshell opened 7 years ago

pirtleshell commented 7 years ago

Is there a function I can call that is the equivalent of clicking one of the columns names?

For instance, clicking a button outside of the table could call the function to change which column the sorting is based on.

My use case is that I have a UI outside the table that changes what data the table contains and it makes more sense for some data to be sorted by one column and others by a different one. I'd like to programatically change which column the table is sorted by when the user clicks to change the table data.

pirtleshell commented 7 years ago

I'm hoping there is (or can eventually be) a better way, but I worked out a hacky way to do it. I tried not to alter angular-tablesort.js but I ended up having to add one line. Here's how I did it:

  1. add tablesort's setSortField to its scope by adding $scope.setSortField = this.setSortField to angular-tablesort.js after the function is defined

  2. access the table's scope and current sort order in the desired controller by extracting it from the tablesort:sortOrder event. This will fire when the component mounts assuming one of your column heads has the ts-default attribute:

    let tableScope, currentSortOrder;
    $scope.$on('tablesort:sortOrder', function(event, sortOrder) {
    tableScope = event.targetScope;
    currentSortOrder = sortOrder;
    });

    This is pretty un-angular... Tablesort should maybe have a service that lets you communicate between controllers? This also wouldn't work well on a page with multiple sorting tables

  3. Now to change the sort order, you just need to call tableScope.setSortField(sortexpr, element, name, sortBy).

I wrote the following function to easily sort by a column given its zero-indexed number.

// first get the column sorting info after the table mounted
// cols is an array of objects containing the DOM element and ts-criteria of the each table head
const cols = angular.element('th').map(function(i, el) {
  const element = angular.element(el);
  const name = element.attr('ts-criteria');
  return {element, name};
});

// then you can call this function to sort by a column
$scope.sortByColumn = function(whichCol, order) {
  // order follows tablesort convention.
  // descending => order=true
  // ascending  => order=false
  const element = cols[whichCol].element;
  const name = cols[whichCol].name;
  let timesToSort = 0;

  // descending needs to set sort field twice if not already on proper column
  if (order && currentSortOrder.name !== name)
    timesToSort++;
  if (currentSortOrder.name !== name || currentSortOrder.order)
    timesToSort++;
  while (timesToSort--) tableScope.setSortField(name, element);
}

Now from your view you can have something like

<button ng-click='sortByColumn(3, true)'>Sort 4th column descending</button>

Obviously, the column must have a ts-criteria attribute set.

If you have more than one table on the page, then the indices will be off. However you can have cols only get the th tags of a specific table (angular.element('#specificTable th')). But you'll need to handle currentSortOrder and tableScope differently.


What do people think of this? Is there a better way to do it?