@@ -15,6 +15,7 @@ import {combineLatest} from 'rxjs/operators/combineLatest';
15
15
import { map } from 'rxjs/operators/map' ;
16
16
import { startWith } from 'rxjs/operators/startWith' ;
17
17
import { empty } from 'rxjs/observable/empty' ;
18
+ import { _isNumberValue } from '@angular/cdk/coercion' ;
18
19
19
20
/**
20
21
* 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> {
88
89
private _paginator : MatPaginator | null ;
89
90
90
91
/**
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.
92
94
* This default function assumes that the sort header IDs (which defaults to the column name)
93
95
* matches the data's properties (e.g. column Xyz represents data['Xyz']).
94
96
* May be set to a custom function for different behavior.
@@ -98,21 +100,54 @@ export class MatTableDataSource<T> implements DataSource<T> {
98
100
sortingDataAccessor : ( ( data : T , sortHeaderId : string ) => string | number ) =
99
101
( data : T , sortHeaderId : string ) : string | number => {
100
102
const value : any = data [ sortHeaderId ] ;
103
+ return _isNumberValue ( value ) ? Number ( value ) : value ;
104
+ }
101
105
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 ) ;
107
123
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
+ } ) ;
109
144
}
110
145
111
146
/**
112
147
* Checks if a data object matches the data source's filter string. By default, each data object
113
148
* is converted to a string of its properties and returns true if the filter has
114
149
* 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
116
151
* filter matching.
117
152
* @param data Data object used to check against the filter.
118
153
* @param filter Filter string that has been set on the data source.
@@ -172,7 +207,7 @@ export class MatTableDataSource<T> implements DataSource<T> {
172
207
_filterData ( data : T [ ] ) {
173
208
// If there is a filter string, filter out data that does not contain it.
174
209
// 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.
176
211
this . filteredData =
177
212
! this . filter ? data : data . filter ( obj => this . filterPredicate ( obj , this . filter ) ) ;
178
213
@@ -188,16 +223,9 @@ export class MatTableDataSource<T> implements DataSource<T> {
188
223
*/
189
224
_orderData ( data : T [ ] ) : T [ ] {
190
225
// 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 ; }
195
227
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 ) ;
201
229
}
202
230
203
231
/**
0 commit comments