@@ -15,6 +15,7 @@ import {combineLatest} from 'rxjs/operators/combineLatest';
1515import { map } from 'rxjs/operators/map' ;
1616import { startWith } from 'rxjs/operators/startWith' ;
1717import { empty } from 'rxjs/observable/empty' ;
18+ import { _isNumberValue } from '@angular/cdk/coercion' ;
1819
1920/**
2021 * Data source that accepts a client-side data array and includes native support of filtering,
@@ -88,7 +89,8 @@ export class MatTableDataSource<T> implements DataSource<T> {
8889 private _paginator : MatPaginator | null ;
8990
9091 /**
91- * Data accessor function that is used for accessing data properties for sorting.
92+ * Data accessor function that is used for accessing data properties for sorting through
93+ * the default sortData function.
9294 * This default function assumes that the sort header IDs (which defaults to the column name)
9395 * matches the data's properties (e.g. column Xyz represents data['Xyz']).
9496 * May be set to a custom function for different behavior.
@@ -98,21 +100,54 @@ export class MatTableDataSource<T> implements DataSource<T> {
98100 sortingDataAccessor : ( ( data : T , sortHeaderId : string ) => string | number ) =
99101 ( data : T , sortHeaderId : string ) : string | number => {
100102 const value : any = data [ sortHeaderId ] ;
103+ return _isNumberValue ( value ) ? Number ( value ) : value ;
104+ }
101105
102- // If the value is a string and only whitespace, return the value.
103- // Otherwise +value will convert it to 0.
104- if ( typeof value === 'string' && ! value . trim ( ) ) {
105- return value ;
106- }
106+ /**
107+ * Gets a sorted copy of the data array based on the state of the MatSort. Called
108+ * after changes are made to the filtered data or when sort changes are emitted from MatSort.
109+ * By default, the function retrieves the active sort and its direction and compares data
110+ * by retrieving data using the sortingDataAccessor. May be overridden for a custom implementation
111+ * of data ordering.
112+ * @param data The array of data that should be sorted.
113+ * @param sort The connected MatSort that holds the current sort state.
114+ */
115+ sortData : ( ( data : T [ ] , sort : MatSort ) => T [ ] ) = ( data : T [ ] , sort : MatSort ) : T [ ] => {
116+ const active = sort . active ;
117+ const direction = sort . direction ;
118+ if ( ! active || direction == '' ) { return data ; }
119+
120+ return data . sort ( ( a , b ) => {
121+ let valueA = this . sortingDataAccessor ( a , active ) ;
122+ let valueB = this . sortingDataAccessor ( b , active ) ;
107123
108- return isNaN ( + value ) ? value : + value ;
124+ // If both valueA and valueB exist (truthy), then compare the two. Otherwise, check if
125+ // one value exists while the other doesn't. In this case, existing value should come first.
126+ // This avoids inconsistent results when comparing values to undefined/null.
127+ // If neither value exists, return 0 (equal).
128+ let comparatorResult = 0 ;
129+ if ( valueA && valueB ) {
130+ // Check if one value is greater than the other; if equal, comparatorResult should remain 0.
131+ if ( valueA > valueB ) {
132+ comparatorResult = 1 ;
133+ } else if ( valueA < valueB ) {
134+ comparatorResult = - 1 ;
135+ }
136+ } else if ( valueA ) {
137+ comparatorResult = 1 ;
138+ } else if ( valueB ) {
139+ comparatorResult = - 1 ;
140+ }
141+
142+ return comparatorResult * ( direction == 'asc' ? 1 : - 1 ) ;
143+ } ) ;
109144 }
110145
111146 /**
112147 * Checks if a data object matches the data source's filter string. By default, each data object
113148 * is converted to a string of its properties and returns true if the filter has
114149 * at least one occurrence in that string. By default, the filter string has its whitespace
115- * trimmed and the match is case-insensitive. May be overriden for a custom implementation of
150+ * trimmed and the match is case-insensitive. May be overridden for a custom implementation of
116151 * filter matching.
117152 * @param data Data object used to check against the filter.
118153 * @param filter Filter string that has been set on the data source.
@@ -172,7 +207,7 @@ export class MatTableDataSource<T> implements DataSource<T> {
172207 _filterData ( data : T [ ] ) {
173208 // If there is a filter string, filter out data that does not contain it.
174209 // Each data object is converted to a string using the function defined by filterTermAccessor.
175- // May be overriden for customization.
210+ // May be overridden for customization.
176211 this . filteredData =
177212 ! this . filter ? data : data . filter ( obj => this . filterPredicate ( obj , this . filter ) ) ;
178213
@@ -188,16 +223,9 @@ export class MatTableDataSource<T> implements DataSource<T> {
188223 */
189224 _orderData ( data : T [ ] ) : T [ ] {
190225 // If there is no active sort or direction, return the data without trying to sort.
191- if ( ! this . sort || ! this . sort . active || this . sort . direction == '' ) { return data ; }
192-
193- const active = this . sort . active ;
194- const direction = this . sort . direction ;
226+ if ( ! this . sort ) { return data ; }
195227
196- return data . slice ( ) . sort ( ( a , b ) => {
197- let valueA = this . sortingDataAccessor ( a , active ) ;
198- let valueB = this . sortingDataAccessor ( b , active ) ;
199- return ( valueA < valueB ? - 1 : 1 ) * ( direction == 'asc' ? 1 : - 1 ) ;
200- } ) ;
228+ return this . sortData ( data . slice ( ) , this . sort ) ;
201229 }
202230
203231 /**
0 commit comments