PrimeNGのテーブルを複数の列でソートする

2020年3月8日(日)

環境

目次

  1. Angularプロジェクトを作成する
    1. Angular CLIのコマンドでプロジェクトを作成する
    2. このプロジェクトにPrimeNGをインストールする
    3. このプロジェクトにPrimeIconsをインストールする
    4. このプロジェクトに@angular/cdkをインストールする
  2. PrimeNGのテーブルを複数の列でソートする
    1. CSSをインポートする
    2. モジュールをインポートする
    3. HTMLを記述する
    4. コードを記述する
    5. サーバーにデプロイする

ディレクトリ構成

/
+-home
|   +-mizuki
|       +-.nvm
|       |   +-versions
|       |       +-node
|       |           +-v12.16.0
|       |
|       +-opt
|       |   +-eclipse-jee -> /home/mizuki/opt/eclipse-jee-201912
|       |   |
|       |   |-eclipse-jee-201912
|       |
|       +-workspace
|       |   +-angular
|       |   |   +-custom-sort
|       |   |       +-node_modules
|       |   |       |   +-primeng
|       |   |       |   +-primeicons
|       |   |       |   +-@angular/cdk
|       |   |       |
|       |   |       |-package-lock.json
|       |   |       |
|       |   |       +-src
|       |   |           +-app
|       |   |           |   +-class
|       |   |           |   |   |-row-item.ts
|       |   |           |   |   |-sort-item.ts
|       |   |           |   |
|       |   |           |   +-enum
|       |   |           |   |   |-sort-order.enum.ts
|       |   |           |   |
|       |   |           |   |-app.component.css
|       |   |           |   |-app.component.html
|       |   |           |   |-app.component.ts
|       |   |           |   |
|       |   |           |   |-app.module.ts
|       |   |           |
|       |   |           |-styles.css
|       |   |
|       |   +-node_modules
|       |   |   +-@angular
|       |   |   +-@angular-devkit
|       |   |
|       |   |-package-lock.json
|       |
|       +-www
|           +-html
|               +-static
|                   |-index.html
|
+-media
|   +-sf_sharedfolder
|
+-opt
    |-apache-tomcat -> /opt/apache-tomcat-9.0.29
    |
    +-apache-tomcat-9.0.29
    |   +-bin
    |   |   |-startup.sh
    |   |   |-shutdown.sh
    |   |   |-setenv.sh
    |   |
    |   +-conf
    |       |-tomcat-users.xml
    |
    |-java -> /opt/oracle-jdk-11.0.6
    |
    +-oracle-jdk-11.0.6
        +-bin
            |-java
          

GitHub repository

Sort PrimeNG table by multiple columns.

1. Angularプロジェクトを作成する

1-1. Angular CLIのコマンドでプロジェクトを作成する

$ pwd
/home/mizuki/workspace/angular

$ npx ng new custom-sort
? Would you like to add Angular routing? (y/N) [y]
? Which stylesheet format would you like to use? (Use arrow keys) [CSS]
> CSS
  SCSS   [ https://sass-lang.com/documentation/syntax#scss                ]
  Sass   [ https://sass-lang.com/documentation/syntax#the-indented-syntax ]
  Less   [ http://lesscss.org                                             ]
  Stylus [ http://stylus-lang.com                                         ]
        

1-2. このプロジェクトにPrimeNGをインストールする

プロジェクトのディレクトリでインストールを行う。

$ pwd
/home/mizuki/workspace/angular/custom-sort

インストール可能なバージョンを調べる。
$ npm info primeng versions
[
'8.1.0', '8.1.1', '8.1.3', '8.1.4',
]

バージョンを指定してインストールする。
$ npm install primeng@8.1.4

インストールされたバージョンを確認する。
$ npm ls primeng
custom-sort@0.0.0 /home/mizuki/workspace/angular/custom-sort
└── primeng@8.1.4
        

参考

1-3. このプロジェクトにPrimeIconsをインストールする

プロジェクトのディレクトリでインストールを行う。

$ pwd
/home/mizuki/workspace/angular/custom-sort

インストール可能なバージョンを調べる。
$ npm info primeicons versions
[
  '1.0.0', '2.0.0'
]

バージョンを指定してインストールする。
$ npm install primeicons@2.0.0

インストールされたバージョンを確認する。
$ npm ls primeicons
custom-sort@0.0.0 /home/mizuki/workspace/angular/custom-sort
└── primeicons@2.0.0
        

1-4. このプロジェクトに@angular/cdkをインストールする

プロジェクトのディレクトリでインストールを行う。

$ pwd
/home/mizuki/workspace/angular/custom-sort

インストール可能なバージョンを調べる。
$ npm info @angular/cdk versions
[
  '8.2.0', '8.2.1', '8.2.2', '8.2.3',
]

バージョンを指定してインストールする。
$ npm install @angular/cdk@8.2.3

インストールされたバージョンを確認する。
$ npm ls @angular/cdk
custom-sort@0.0.0 /home/mizuki/workspace/angular/custom-sort
└── @angular/cdk@8.2.3
        

2. PrimeNGのテーブルを複数の列でソートする

2-1. CSSをインポートする

src/styles.css

@import url("../node_modules/primeicons/primeicons.css");
@import url("../node_modules/primeng/resources/themes/nova-light/theme.css");
@import url("../node_modules/primeng/resources/primeng.min.css");
        

参考

2-2. モジュールをインポートする

1つのモジュールをインポートする。

import { TableModule } from 'primeng/table';
        

src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { TableModule } from 'primeng/table';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    TableModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
        

2-3. HTMLを記述する

src/app/app.component.html

<p-table [value]="rowList" [style]="{'width':'330px'}">
  <ng-template pTemplate="header">
    <tr>
      <th>
        <div style="width:100%;height:100%;display:flex;flex-direction:row;justify-content:flex-start;align-items:center;">
          <span style="flex-grow:1;">column1</span>
          <i *ngIf="sortOrderColumn1 === SORT_ORDER_ASC" class="pi pi-sort-numeric-down" (click)="onSort(1)"></i>
          <i *ngIf="sortOrderColumn1 === SORT_ORDER_DESC" class="pi pi-sort-numeric-up" (click)="onSort(1)"></i>
        </div>
      </th>
      <th>
        <div style="width:100%;height:100%;display:flex;flex-direction:row;justify-content:flex-start;align-items:center;">
          <span style="flex-grow:1;">column2</span>
          <i *ngIf="sortOrderColumn2 === SORT_ORDER_ASC" class="pi pi-sort-numeric-down" (click)="onSort(2)"></i>
          <i *ngIf="sortOrderColumn2 === SORT_ORDER_DESC" class="pi pi-sort-numeric-up" (click)="onSort(2)"></i>
        </div>
      </th>
      <th>
        <div style="width:100%;height:100%;display:flex;flex-direction:row;justify-content:flex-start;align-items:center;">
          <span style="flex-grow:1;">column3</span>
          <i *ngIf="sortOrderColumn3 === SORT_ORDER_ASC" class="pi pi-sort-numeric-down" (click)="onSort(3)"></i>
          <i *ngIf="sortOrderColumn3 === SORT_ORDER_DESC" class="pi pi-sort-numeric-up" (click)="onSort(3)"></i>
        </div>
      </th>
    </tr>
  </ng-template>
  <ng-template pTemplate="body" let-row>
    <tr>
      <td>{{row.column1}}</td>
      <td>{{row.column2}}</td>
      <td>{{row.column3}}</td>
    </tr>
  </ng-template>
</p-table>
        

2-4. コードを記述する

src/app/class/row-item.ts

$ pwd
/home/mizuki/workspace/angular/custom-sort

$ npx ng generate class class/row-item
        
export class RowItem {
  column1: string;
  column2: string;
  column3: string;
}
        

src/app/class/sort-item.ts

$ pwd
/home/mizuki/workspace/angular/custom-sort

$ npx ng generate class class/sort-item
        
export class SortItem {
  columnIndex: number;
  order: string;
  compare: Function;
}
        

src/app/enum/sort-order.enum.ts

$ pwd
/home/mizuki/workspace/angular/custom-sort

$ npx ng generate enum enum/sort-order
        
export enum SortOrder {
  ASC = 'asc',
  DESC = 'desc'
}
        

src/app/app.component.ts

import { Component, OnInit } from '@angular/core';

import { RowItem } from './class/row-item';
import { SortItem } from './class/sort-item';
import { SortOrder } from './enum/sort-order.enum';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  rowList: RowItem[];
  sortOrderColumn1: string;
  sortOrderColumn2: string;
  sortOrderColumn3: string;

  defaultOrderList: SortItem[];

  SORT_ORDER_ASC: string;
  SORT_ORDER_DESC: string;

  constructor() {
    this.defaultOrderList = this.getDefaultOrderList();

    this.SORT_ORDER_ASC = SortOrder.ASC;
    this.SORT_ORDER_DESC = SortOrder.DESC;
  }

  ngOnInit() {
    this.rowList = this.getRowList();
    this.sortOrderColumn1 = SortOrder.ASC;
    this.sortOrderColumn2 = SortOrder.ASC;
    this.sortOrderColumn3 = SortOrder.ASC;
    this.sortRowList();
  }

  getDefaultOrderList(): SortItem[] {
    let list: SortItem[] = [];
    let item: SortItem;

    item = new SortItem();
    item.columnIndex = 1;
    item.order = SortOrder.ASC;
    item.compare = function(a: RowItem, b: RowItem): number {
      if (a.column1 < b.column1) {
        return -1;
      }
      if (a.column1 > b.column1) {
        return 1;
      }
      return 0;
    };
    list.push(item);

    item = new SortItem();
    item.columnIndex = 2;
    item.order = SortOrder.ASC;
    item.compare = function(a: RowItem, b: RowItem): number {
      if (a.column2 < b.column2) {
        return -1;
      }
      if (a.column2 > b.column2) {
        return 1;
      }
      return 0;
    };
    list.push(item);

    item = new SortItem();
    item.columnIndex = 3;
    item.order = SortOrder.ASC;
    item.compare = function(a: RowItem, b: RowItem): number {
      if (a.column3 < b.column3) {
        return -1;
      }
      if (a.column3 > b.column3) {
        return 1;
      }
      return 0;
    };
    list.push(item);

    return list;
  }

  getRowList(): RowItem[] {
    let list: RowItem[] = [];
    let item: RowItem;

    item = new RowItem();
    item.column1 = 'あ';
    item.column2 = 'a';
    item.column3 = '1';
    list.push(item);
    item = new RowItem();
    item.column1 = 'あ';
    item.column2 = 'a';
    item.column3 = '2';
    list.push(item);
    item = new RowItem();
    item.column1 = 'あ';
    item.column2 = 'b';
    item.column3 = '3';
    list.push(item);
    item = new RowItem();
    item.column1 = 'い';
    item.column2 = 'b';
    item.column3 = '4';
    list.push(item);
    item = new RowItem();
    item.column1 = 'い';
    item.column2 = 'c';
    item.column3 = '5';
    list.push(item);
    item = new RowItem();
    item.column1 = 'い';
    item.column2 = 'c';
    item.column3 = '6';
    list.push(item);

    return list;
  }

  onSort(columnIndex: number) {
    this.sortRowList(columnIndex);
  }

  sortRowList(columnIndex: number = -1) {
    let firstOrder: SortItem[] = this.defaultOrderList.filter((item) => item.columnIndex === columnIndex);
    if (firstOrder.length > 0) {
      firstOrder[0].order = (firstOrder[0].order === SortOrder.ASC) ? SortOrder.DESC : SortOrder.ASC;

      switch(columnIndex) {
        case 1:
          this.sortOrderColumn1 = firstOrder[0].order;
          break;
        case 2:
          this.sortOrderColumn2 = firstOrder[0].order;
          break;
        case 3:
          this.sortOrderColumn3 = firstOrder[0].order;
          break;
      }
    }

    let customOrderList: SortItem[] = [
      ...firstOrder,
      ...this.defaultOrderList.filter((item) => item.columnIndex !== columnIndex)
    ];

    this.rowList.sort((a: RowItem, b: RowItem): number => {
      for (let order of customOrderList) {
        let compareResult: number = order.compare(a, b);
        if (compareResult !== 0) {
          return (order.order === SortOrder.ASC) ? compareResult : -(compareResult);
        }
      }
      return 0;
    });
    this.rowList = [...this.rowList];
  }
}
        

2-5. サーバーにデプロイする

リリース用のファイルを作成する。

$ pwd
/home/mizuki/workspace/angular/custom-sort

$ npx ng build --prod
        

distディレクトリ配下にプロジェクト名のディレクトリとファイルが出力される。

$ pwd
/home/mizuki/workspace/angular/custom-sort/dist/custom-sort

$ ls -al
合計 2704
drwxrwxr-x. 2 mizuki mizuki   4096  3月  8 22:36 .
drwxrwxr-x. 3 mizuki mizuki     25  3月  8 22:36 ..
-rw-rw-r--. 1 mizuki mizuki  16752  3月  8 22:36 3rdpartylicenses.txt
-rw-rw-r--. 1 mizuki mizuki  10355  3月  8 22:36 color.c7a33805ffda0d32bd2a.png
-rw-rw-r--. 1 mizuki mizuki    948  3月  8 22:36 favicon.ico
-rw-rw-r--. 1 mizuki mizuki    293  3月  8 22:36 hue.0614c27197fc3ce572e1.png
-rw-rw-r--. 1 mizuki mizuki    806  3月  8 22:36 index.html
-rw-rw-r--. 1 mizuki mizuki  13112  3月  8 22:36 line.567f57385ea3dde2c9ae.gif
-rw-rw-r--. 1 mizuki mizuki   9427  3月  8 22:36 loading.8732a6660b528fadfaeb.gif
-rw-rw-r--. 1 mizuki mizuki 765056  3月  8 22:36 main-es2015.37601e2170bdc46ec6f5.js
-rw-rw-r--. 1 mizuki mizuki 864239  3月  8 22:36 main-es5.37601e2170bdc46ec6f5.js
-rw-rw-r--. 1 mizuki mizuki  27604  3月  8 22:36 open-sans-v15-latin-300.177cc92d2e8027712a8c.ttf
-rw-rw-r--. 1 mizuki mizuki  55181  3月  8 22:36 open-sans-v15-latin-300.27ef0b062b2e221df16f.svg
-rw-rw-r--. 1 mizuki mizuki  18280  3月  8 22:36 open-sans-v15-latin-300.521d17bc9f3526c690e8.woff
-rw-rw-r--. 1 mizuki mizuki  14564  3月  8 22:36 open-sans-v15-latin-300.60c866748ff15f5b347f.woff2
-rw-rw-r--. 1 mizuki mizuki  15545  3月  8 22:36 open-sans-v15-latin-300.76b56857ebbae3a5a689.eot
-rw-rw-r--. 1 mizuki mizuki  15667  3月  8 22:36 open-sans-v15-latin-700.148a6749baa5f658a451.eot
-rw-rw-r--. 1 mizuki mizuki  55652  3月  8 22:36 open-sans-v15-latin-700.2e00b2635b51ba336b4b.svg
-rw-rw-r--. 1 mizuki mizuki  18476  3月  8 22:36 open-sans-v15-latin-700.623e3205570002af47fc.woff
-rw-rw-r--. 1 mizuki mizuki  28192  3月  8 22:36 open-sans-v15-latin-700.7e08cc656863d52bcb5c.ttf
-rw-rw-r--. 1 mizuki mizuki  14720  3月  8 22:36 open-sans-v15-latin-700.d08c09f2f169f4a6edbc.woff2
-rw-rw-r--. 1 mizuki mizuki  56266  3月  8 22:36 open-sans-v15-latin-regular.7aab4c13671282c90669.svg
-rw-rw-r--. 1 mizuki mizuki  15050  3月  8 22:36 open-sans-v15-latin-regular.9dce7f01715340861bdb.eot
-rw-rw-r--. 1 mizuki mizuki  17704  3月  8 22:36 open-sans-v15-latin-regular.bf2d0783515b7d75c35b.woff
-rw-rw-r--. 1 mizuki mizuki  26488  3月  8 22:36 open-sans-v15-latin-regular.c045b73d86803686f4cd.ttf
-rw-rw-r--. 1 mizuki mizuki  14048  3月  8 22:36 open-sans-v15-latin-regular.cffb686d7d2f4682df83.woff2
-rw-rw-r--. 1 mizuki mizuki    118  3月  8 22:36 password-meter.d59e6dc2616c53ce8e77.png
-rw-rw-r--. 1 mizuki mizuki  37053  3月  8 22:36 polyfills-es2015.5b10b8fd823b6392f1fd.js
-rw-rw-r--. 1 mizuki mizuki 129362  3月  8 22:36 polyfills-es5.3e8196928d184a6e5319.js
-rw-rw-r--. 1 mizuki mizuki  39748  3月  8 22:36 primeicons.2d2afb2719a1ee903e57.eot
-rw-rw-r--. 1 mizuki mizuki  39648  3月  8 22:36 primeicons.66ee0deb739ca71f0ecd.woff
-rw-rw-r--. 1 mizuki mizuki  39572  3月  8 22:36 primeicons.df0140f8e79ecfeffaf8.ttf
-rw-rw-r--. 1 mizuki mizuki 163568  3月  8 22:36 primeicons.e5e0e94474d5fd92e7e8.svg
-rw-rw-r--. 1 mizuki mizuki   1485  3月  8 22:36 runtime-es2015.c5fa8325f89fc516600b.js
-rw-rw-r--. 1 mizuki mizuki   1485  3月  8 22:36 runtime-es5.c5fa8325f89fc516600b.js
-rw-rw-r--. 1 mizuki mizuki 171599  3月  8 22:36 styles.699e9cf8808c28ae8c28.css
        

index.htmlのbase hrefを変更する。デフォルトではサーバールートを基準にして相対パスの解決が行われてしまう。

<base href="/">
        
<base href="/code/2020/03/dist/0801/">
        

プロジェクト名のディレクトリ配下にあるファイルをサーバーにアップロードする。

サンプルページを表示する

参考