Is there a collation method for MatSort that accommodates languages with alphabetical orders differing from Unicode orders?

When it comes to the Norwegian and Danish Alphabets, the proper order of the characters is as follows:

  1. Æ
  2. Ø
  3. Å

However, MatSort follows the Unicode order for these characters:

  1. Å (197)
  2. Æ (198)
  3. Ø (216)

Is there a way to implement collation to address this issue?

Check out this stackblitz with a table that can be sorted by "No." and "Name":

https://stackblitz.com/edit/angular-an1uqc-8mdqns

Here's the data in the table:

 {position: 1, name: 'Alpha', description: 'Test123'},
  {position: 2, name: 'Bravo',  description: '0'},
  {position: 3, name: 'Charlie', description: 'aaa'},
  {position: 4, name: 'Delta',  description: ''},
  {position: 5, name: 'Echo',  description: '1'},
  {position: 6, name: 'Foxtrot',  description: '2'},
  {position: 7, name: 'ÆGamma',  description: ''},
  {position: 8, name: 'ØHotel',  description: '3'},
  {position: 9, name: 'ÅIndigo',  description: '1000'},

];

According to the Norwegian/Danish alphabet, the correct sorting order for the last three items (ÆGamma, ØHotel, and ÅIndigo) should be:

  1. ÆGamma
  2. ØHotel
  3. ÅIndigo

Yet, MatSort uses Unicode numbers for these characters and sorts them in this manner:

  1. ÅIndigo (197)
  2. ÆGamma (198)
  3. ØHotel (216)

Thank you for taking the time to read this! :]

Answer №1

To achieve a customized localized sort, you can utilize the matSortChange method. This involves implementing String.prototype.localCompare with the language identifier da-DK:

For the template:

<table
  mat-table [dataSource]="sortedData"
  matSort (matSortChange)="sortData($event)"
  class="mat-elevation-z8">

In the component:

sortData(sort: Sort) {
  const data = ELEMENT_DATA.slice();
  if (!sort.active || sort.direction === '') {
    this.sortedData = data;
    return;
  }

  this.sortedData = data.sort((a, b) => {
    const isAsc = sort.direction === 'asc';
    switch (sort.active) {
      case 'position': return compareNumber(a.position, b.position, isAsc);
      case 'name': return compareString(a.name, b.name, isAsc);
      case 'description': return compareString(a.description, b.description, isAsc);
      default: return 0;
    }
  });
}

// ...

function compareNumber(a: number, b: number, isAsc: boolean) {
  return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}

function compareString(a: string, b: string, isAsc: boolean) {
  return a.localeCompare(b, 'da-DK') * (isAsc ? 1 : -1);
}

Check out this live example.

The sorted output will be:

1 Alpha
// ...
6 Foxtrot
7 ÆGamma
8 ØHotel
9 ÅIndigo

And in reverse:

9 ÅIndigo
8 ØHotel
7 ÆGamma
6 Foxtrot
// ...
1 Alpha

Hopefully this explanation is helpful!

Answer №2

Dealing with German umlauts posed a challenge for me as well. To address this issue while utilizing MatTableDataSource, I devised a solution by creating a custom sortData() implementation:

@ViewChildren(MatSort) public matSortQueryList: QueryList<MatSort>;

public dataSource: MatTableDataSource<CUSTOMTYPE> = new MatTableDataSource<CUSTOMTYPE>([]);
private readonly defColl = new Intl.Collator('de');
private readonly _subs = new Subscription();

public ngAfterViewInit() {
  this.dataSource.data = CUSTOMDATA[];
  
  this._subs.add(
    this.matSortQueryList.changes.subscribe(elems => {
      if (elems.length && !this.dataSource.sort) {
        this.dataSource.sort = elems.first;
      }
    })
  );
  
  this.dataSource.sortData = (data: CUSTOMTYPE[], sort: MatSort) => {
    const sorted = data.sort((a, b) => {
      return this.defColl.compare(a.CUSTOMPROPERTY as string, b.CUSTOMPROPERTY as string);
    });

    return sort.direction === 'desc' ? sorted.reverse() : sorted;
  }
}

Additionally, I suggest leveraging Intl.Collator for improved performance over localeCompare.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The Capacitor Community Electron Platform, which combines IONIC, Angular, and Electron, encountered a TypeError: [ERR_INVALID_ARG_TYPE]. This error message indicates that the "path" argument must be in the

I have been attempting to follow the guidelines provided on to integrate the Capacitor Community Electron into a brand new Ionic Angular Test App. Everything is going smoothly until I reach the step where I need to add the platform as per the instructions ...

Showing nested arrays in API data using Angular

I would like to display the data from this API { "results": [ { "name": "Luke Skywalker", "height": "172", "mass": "77", & ...

Surprising fraction of behavior

Looking for some clarification on the types used in this code snippet: interface UserDTO { id: string; email: string; } const input: Partial<UserDTO> = {}; const userDTO: Partial<UserDTO> = { id: "", ...input }; const email = us ...

Capture Video on iOS devices using the MediaRecorder API and display it using HTML5 Video Player

Issue: I am facing an issue where I cannot record video or get a video stream from the camera on iOS through my Angular web application built using ng build. Investigation: In my investigation, I explored various websites that discuss Apple iOS features ...

Using Ionic 2 to close the FAB menu with a button press

Is there a way to automatically close the FAB menu when a button inside it is pressed? Take a look at the button: <ion-fab bottom center > <button ion-fab><b>Hello</b></button> <ion-fab-list side="top"> <button ion- ...

Error in ag-Grid CSV export feature displaying incorrect file names

Currently, I am coding in Typescript using Angular 2 along with ag-Grid (non-enterprise variant). An issue has arisen with the export feature of ag-Grid and I was wondering if someone could offer some assistance. If there is only one Grid on the form, ex ...

Creating a HTML element that functions as a text input using a div

I am encountering an issue with using a div as text input where the cursor flashes at the beginning of the string on a second attempt to edit the field. However, during the initial attempt, it does allow me to type from left to right. Another problem I am ...

The NestJS framework encountered an error due to a method being undefined in the

Encountering Error with NestJS Function create123: TypeError - Cannot read properties of undefined (reading 'create123') The constructor is displayed below \`export class AuthenticationService { constructor( private readonly usersServ ...

Enhance the annotation of JS types for arguments with default values

Currently, I am working within a code base that predominantly uses JS files, rather than TS. However, I have decided to incorporate tsc for type validation. In TypeScript, one method of inferring types for arguments is based on default values. For example ...

Angular project models

I'm exploring the Core/Shared/Feature design pattern for building large, scalable applications in Angular, and I find myself unsure about where models fit in. Is there a consensus on which module they belong in? I came across a post suggesting that ...

Using NavParams within a service component,

I'm facing a challenge in accessing NavParams within a provider, and simply importing NavParams is not solving the issue. Here's a brief overview of my application: users input their name and address, a pin is dropped on the map based on the add ...

Utilizing Ionic Storage to set default request headers through an HTTP interceptor in an Angular 5 and Ionic 3 application

I'm attempting to assign a token value to all request headers using the new angular 5 HTTP client. Take a look at my code snippet: import {Injectable} from '@angular/core'; import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from ...

Ways to display map results in multiple columns utilizing react

I am facing a challenge and seeking guidance on how to achieve a specific layout using React. My goal is to display results in two columns as shown below: item 1 | item 4 item 2 | item 5 item 3 | item 6 I have attempted to check the array length and dete ...

Tips for simulating a Ref

I have a Vue3 component where, within the setup(), I have defined the following function: const writeNote(note: Ref<Note>) => { console.log(`note ${note.id}`) } This function takes a Ref<Note>, with Note being an Interface. There are two s ...

Trigger the React useEffect only when the inputed string matches the previous one

Currently, I am in the process of creating my own custom useFetch hook. export const useFetch = <T extends unknown>( url: string, options?: RequestInit ) => { const [loading, setLoading] = useState(false); const [error, setError] = ...

Is there a way to change the font size with a click in JavaScript or Angular?

Here is a breakdown of the 4 steps: 1.) Begin by clicking on a category 2.) The filtered products will be displayed 3.) Select the desired products from the filter 4.) Once selected, the products will appear in the rightmost part of the screen within t ...

"Facing an issue where ts-node is not recognizing d.ts files, despite tsc being able to compile them

I am currently using typescript along with express and attempting to enhance the request object in express. Below is my server.ts file: import express, { Request, Response } from "express"; const app = express(); app.use(function(req: Request, res: Respo ...

Implementing intelligent parameter type enforcement according to configuration settings

I can't decide on a name for this concept, so please be patient while I explain it. There are three configuration objects: const configA = { type: 'A' as const, getPath: (query: { foo: string }) => `/${query.foo}` } const config ...

When utilized in a nested component, the service is found to be null

When developing a nested component, I encounter an issue where the injected service is null. How can I successfully use a service in a nested component? export class ActivityComponent implements OnInit { constructor( . . public accountServ ...

"During the constructor initialization, an Angular/TypeScript global variable that was assigned within an

Utilizing HTTP calls to my Web API to fetch 2 API keys for accessing another API. These API keys are fetched using 2 functions: getApiKey() and getAppId() Upon calling these functions within the constructor, the returned value, stored in a global variab ...