PrimeNGのTableで行選択とチェックボックス選択を同時に行う

2020年1月19日(日)

環境

目次

  1. nvmをバージョンアップする
    1. インストールスクリプトを実行する
  2. nodeをバージョンアップする
    1. 利用可能なnodeのバージョンを調べる
    2. LTS版の最新のnodeをバージョンを指定してインストールする
    3. 最新のnodeをバージョンを指定してインストールする
    4. インストール済みのnodeのバージョンを表示する
    5. 古いnodeのバージョンをアンインストールする
  3. Angular CLIをバージョンアップする
    1. 利用可能なAngular CLIのバージョンを調べる
    2. 同一メジャーバージョンの最新のパッチバージョンに更新する
  4. Angularプロジェクトを作成する
    1. Angular CLIのコマンドでプロジェクトを作成する
    2. このプロジェクトにPrimeNGをインストールする
    3. このプロジェクトにPrimeIconsをインストールする
    4. このプロジェクトに@angular/cdkをインストールする
  5. TableとCheckboxを表示する
    1. CSSをインポートする
    2. モジュールをインポートする
    3. HTMLを記述する
    4. コードを記述する
    5. サーバーにデプロイする

ディレクトリ構成

/
+-home
|   +-mizuki
|       +-.nvm
|       |   +-versions
|       |       +-node
|       |           +-v12.14.1
|       |           +-v13.6.0
|       |
|       +-opt
|       |   +-eclipse-jee -> /home/mizuki/opt/eclipse-jee-201912
|       |   |
|       |   |-eclipse-jee-201912
|       |
|       +-workspace
|       |   +-angular
|       |   |   +-primeng-table-selection-with-checkbox
|       |   |       +-node_modules
|       |   |       |   +-primeng
|       |   |       |   +-primeicons
|       |   |       |   +-@angular/cdk
|       |   |       |
|       |   |       |-package-lock.json
|       |   |       |
|       |   |       +-src
|       |   |           +-app
|       |   |           |   |-app.component.css
|       |   |           |   |-app.component.html
|       |   |           |   |-app.component.ts
|       |   |           |   |
|       |   |           |   +-class
|       |   |           |   |   |-member.ts
|       |   |           |   |
|       |   |           |   +-service
|       |   |           |   |   |-member.service.ts
|       |   |           |   |
|       |   |           |   |-app.module.ts
|       |   |           |
|       |   |           |-styles.css
|       |   |
|       |   +-node_modules
|       |   |   +-@angular
|       |   |       +-cli
|       |   |
|       |   |-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.5
    |
    +-oracle-jdk-11.0.5
        +-bin
            |-java
          

GitHub repository

Simultaneously select rows and check boxes in the PrimeNG Table.

1. nvmをバージョンアップする

1-1. インストールスクリプトを実行する

バージョンアップの場合もインストールスクリプトを実行すればよい。

To install or update nvm, you should run the install script.

https://github.com/nvm-sh/nvm#install--update-script

0.35.1 から 0.35.2 にバージョンアップする。

$ pwd
/home/mizuki

$ nvm --version
0.35.1

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
        

新しい端末を起動してインストールされたバージョンを確認する。

$ pwd
/home/mizuki

$ nvm --version
0.35.2
        

参考

2. nodeをバージョンアップする

2-1. 利用可能なnodeのバージョンを調べる

$ pwd
/home/mizuki

$ nvm ls-remote
->     v12.14.0   (LTS: Erbium)
       v12.14.1   (Latest LTS: Erbium)
       v13.5.0
       v13.6.0
        

2-2. LTS版の最新のnodeをバージョンを指定してインストールする

$ pwd
/home/mizuki

$ nvm install 12.14.1
        

2-3. 最新のnodeをバージョンを指定してインストールする

$ pwd
/home/mizuki

$ nvm install 13.6.0
        

2-4. インストール済みのnodeのバージョンを表示する

$ pwd
/home/mizuki

$ nvm ls
        v12.14.0
        v12.14.1
        v13.5.0
->      v13.6.0
default -> 12.14.0 (-> v12.14.0)
node -> stable (-> v13.6.0) (default)
stable -> 13.6 (-> v13.6.0) (default)
iojs -> N/A (default)
unstable -> N/A (default)
lts/* -> lts/erbium (-> v12.14.1)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.18.0 (-> N/A)
lts/erbium -> v12.14.1
        

2-5. 古いnodeのバージョンをアンインストールする

$ pwd
/home/mizuki

$ nvm uninstall 13.5.0
$ nvm uninstall 12.14.0

$ nvm ls
        v12.14.1
->      v13.6.0
default -> 12.14.0 (-> N/A)
node -> stable (-> v13.6.0) (default)
stable -> 13.6 (-> v13.6.0) (default)
iojs -> N/A (default)
unstable -> N/A (default)
lts/* -> lts/erbium (-> v12.14.1)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.18.0 (-> N/A)
lts/erbium -> v12.14.1

"default"のエイリアスをLTS版の最新のnodeに設定して使えるようにする。
$ nvm alias default 12.14.1
default -> 12.14.1 (-> v12.14.1)
$ nvm use default
Now using node v12.14.1 (npm v6.13.4)

$ nvm ls
->      v12.14.1
        v13.6.0
default -> 12.14.1 (-> v12.14.1)
node -> stable (-> v13.6.0) (default)
stable -> 13.6 (-> v13.6.0) (default)
iojs -> N/A (default)
unstable -> N/A (default)
lts/* -> lts/erbium (-> v12.14.1)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.18.0 (-> N/A)
lts/erbium -> v12.14.1

$ node -v
v12.14.1
        

3. Angular CLIをバージョンアップする

3-1. インストールされているAngular CLIのバージョンを調べる

$ pwd
/home/mizuki/workspace

$ npm ls @angular/cli
/home/mizuki/workspace
└── @angular/cli@8.3.21
        

3-2. 利用可能なAngular CLIのバージョンを調べる

$ pwd
/home/mizuki/workspace

$ npm info @angular/cli versions
[
  '8.3.19', '8.3.20',       '8.3.21',       '8.3.22',
  '8.3.23', '9.0.0-next.0', '9.0.0-next.1', '9.0.0-next.2',
]
        

3-3. 同一メジャーバージョンの最新のパッチバージョンに更新する

$ pwd
/home/mizuki/workspace

$ npm install @angular/cli@^8

$ npm ls @angular/cli
/home/mizuki/workspace
└── @angular/cli@8.3.23
        

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

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

$ pwd
/home/mizuki/workspace/angular

$ npx ng new primeng-table-selection-with-checkbox
? 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                                         ]
        

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

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

$ pwd
/home/mizuki/workspace/angular/primeng-table-selection-with-checkbox

インストール可能なバージョンを調べる。
$ npm info primeng versions
[
  '8.1.0', '8.1.1', '9.0.0-rc.1', '9.0.0-rc.2'
]

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

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

参考

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

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

$ pwd
/home/mizuki/workspace/angular/primeng-table-selection-with-checkbox

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

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

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

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

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

$ pwd
/home/mizuki/workspace/angular/primeng-table-selection-with-checkbox

インストール可能なバージョンを調べる。
$ 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
primeng-table-selection-with-checkbox@0.0.0 /home/mizuki/workspace/angular/primeng-table-selection-with-checkbox
└── @angular/cdk@8.2.3
        

5. TableとCheckboxを表示する

5-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");
        

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

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

import { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TableModule } from 'primeng/table';
import { CheckboxModule } from 'primeng/checkbox';
        

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 { FormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TableModule } from 'primeng/table';
import { CheckboxModule } from 'primeng/checkbox';

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

5-3. HTMLを記述する

src/app/app.component.html

<p-table [style]="{'width':'200px'}" [value]="memberList" [scrollable]="true" scrollHeight="200px" selectionMode="multiple" [(selection)]="selectedMemberList" dataKey="id" (onRowSelect)="onRowSelect($event)" (onRowUnselect)="onRowUnselect($event)">
  <ng-template pTemplate="colgroup">
    <colgroup>
      <col>
      <col width="50px">
    </colgroup>
  </ng-template>
  <ng-template pTemplate="header">
    <tr>
      <th>Member Name</th>
      <th>
        <p-checkbox [(ngModel)]="allMemberSelected" binary="true" (onChange)="onMemberListHeaderCheckboxChange()"></p-checkbox>
      </th>
    </tr>
  </ng-template>
  <ng-template pTemplate="body" let-rowData let-rowIndex="rowIndex">
    <tr [pSelectableRow]="rowData" [pSelectableRowIndex]="rowIndex">
      <td>{{rowData.name}}</td>
      <td class="row-select">
        <div (click)="onMemberListRowCheckboxClick($event)">
          <p-checkbox [(ngModel)]="rowData.selected" binary="true" (onChange)="onMemberListRowCheckboxChange(rowData)"></p-checkbox>
        </div>
      </td>
    </tr>
  </ng-template>
</p-table>
        

src/app/app.component.css

.row-select {
  text-align: center;
}
        

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

src/app/class/member.ts

$ pwd
/home/mizuki/workspace/angular/primeng-table-selection-with-checkbox

$ npx ng generate class class/member
        
export class Member {
  id: number;
  name: string;
  selected: boolean;

  constructor() {
    this.id = 0;
    this.name = '';
    this.selected = false;
  }

  toString(): string {
    let propertyList: string[] = [];
    propertyList.push('id[' + this.id + ']');
    propertyList.push('name[' + this.name + ']');
    propertyList.push('selected[' + this.selected + ']');
    return propertyList.join(',');
  }
}
        

src/app/service/member.service.ts

$ pwd
/home/mizuki/workspace/angular/primeng-table-selection-with-checkbox

$ npx ng generate service service/member
        
import { Injectable } from '@angular/core';

import { Member } from 'src/app/class/member';

@Injectable({
  providedIn: 'root'
})
export class MemberService {
  private _id: number;

  constructor() {
    this._id = 1;
  }

  nextId(): number {
    return this._id++;
  }

  getMemberNameList(): string[] {
    let memberNameList: string[] = [];

    memberNameList.push('譜久村聖');
    memberNameList.push('生田衣梨奈');
    memberNameList.push('石田亜佑美');
    memberNameList.push('佐藤優樹');
    memberNameList.push('小田さくら');
    memberNameList.push('野中美希');
    memberNameList.push('牧野真莉愛');
    memberNameList.push('羽賀朱音');
    memberNameList.push('加賀楓');
    memberNameList.push('横山玲奈');
    memberNameList.push('森戸知沙希');
    memberNameList.push('北川莉央');
    memberNameList.push('岡村ほまれ');
    memberNameList.push('山﨑愛生');

    return memberNameList;
  }

  getMemberList(): Member[] {
    let list: Member[] = [];

    this.getMemberNameList().forEach((item) => {
      let member: Member = new Member();
      member.id = this.nextId();
      member.name = item;

      list.push(member);
    });

    return list;
  }
}
        

src/app/app.component.ts

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

import { Member } from './class/member';
import { MemberService } from './service/member.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  memberList: Member[];
  selectedMemberList: Member[];
  allMemberSelected: boolean;

  constructor(
    private memberService: MemberService
  ) {
    this.memberList = [];
    this.selectedMemberList = [];
    this.allMemberSelected = false;
  }

  ngOnInit() {
    this.memberList = this.memberService.getMemberList();
    this.selectedMemberList = [];
    this.allMemberSelected = false;
  }

  /**
   * 全選択のチェックボックス 変更。
   */
  onMemberListHeaderCheckboxChange() {
    console.log('onMemberListHeaderCheckboxChange()');
    console.log('allMemberSelected[' + this.allMemberSelected + ']');

    if (this.allMemberSelected) {
      let newSelectedMemberList: Member[] = [];
      this.memberList.forEach((item) => {
        item.selected = true;
        newSelectedMemberList.push(item);
      });
      this.selectedMemberList = newSelectedMemberList;
    }
    else {
      this.memberList.forEach((item) => item.selected = false);
      this.selectedMemberList = [];
    }
  }

  /**
   * 行選択のチェックボックスのクリックを<p-table>に伝搬させない。
   * 
   * @param event MouseEvent
   */
  onMemberListRowCheckboxClick(event: MouseEvent) {
    console.log('onMemberListRowCheckboxClick()');
    event.stopPropagation();
  }

  /**
   * event.originalEvent: Browser event
   * event.data: Selected data
   * event.type: Type of selection, valid values are "row", "radiobutton" and "checkbox"
   * event.index: Index of the row
   */
  onRowSelect(event: any) {
    console.log('onRowSelect()');
    console.log('event.originalEvent[' + event.originalEvent + ']');
    console.log('event.data[' + event.data + ']');
    console.log('event.type[' + event.type + ']');
    console.log('event.index[' + event.index + ']');

    //行選択のチェックボックスを更新する。
    let member: Member = event.data;
    member.selected = true;

    //全選択のチェックボックスを更新する。
    this.updateAllMemberSelected();
  }

  /**
   * event.originalEvent: Browser event
   * event.data: Unselected data
   * event.type: Type of unselection, valid values are "row" and "checkbox"
   */
  onRowUnselect(event: any) {
    console.log('onRowUnselect()');
    console.log('event.originalEvent[' + event.originalEvent + ']');
    console.log('event.data[' + event.data + ']');
    console.log('event.type[' + event.type + ']');

    //行選択のチェックボックスを更新する。
    let member: Member = event.data;
    member.selected = false;

    //全選択のチェックボックスを更新する。
    this.allMemberSelected = false;
  }

  /**
   * 行選択のチェックボックス 変更。
   * 
   * @param member 選択行のメンバー
   */
  onMemberListRowCheckboxChange(member: Member) {
    console.log('onMemberListRowCheckboxChange()');
    console.log('name[' + member.name + ']');
    console.log('selected[' + member.selected + ']');

    //<p-table>の行選択を更新する。
    if (member.selected) {
      let newSelectedMemberList: Member[] = this.selectedMemberList.filter((item) => {
        return (item.id !== member.id);
      });
      newSelectedMemberList.push(member);
      this.selectedMemberList = newSelectedMemberList;
    }
    else {
      let newSelectedMemberList: Member[] = this.selectedMemberList.filter((item) => {
        return (item.id !== member.id);
      });
      this.selectedMemberList = newSelectedMemberList;
    }

    //全選択のチェックボックスを更新する。
    this.updateAllMemberSelected();
  }

  /**
   * 全選択のチェックボックスを更新する。
   */
  updateAllMemberSelected() {
    let allSelected: boolean = true;
    this.memberList.forEach((item) => allSelected = allSelected && item.selected);
    this.allMemberSelected = allSelected;
  }
}
        

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

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

$ pwd
/home/mizuki/workspace/angular/primeng-table-selection-with-checkbox

$ npx ng build --prod
        

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

$ pwd
/home/mizuki/workspace/angular/primeng-table-selection-with-checkbox/dist/primeng-table-selection-with-checkbox

$ ls -al
合計 2872
drwxrwxr-x. 2 mizuki mizuki   4096  2月 15 12:51 .
drwxrwxr-x. 3 mizuki mizuki     51  2月 15 12:51 ..
-rw-rw-r--. 1 mizuki mizuki  17811  2月 15 12:51 3rdpartylicenses.txt
-rw-rw-r--. 1 mizuki mizuki  10355  2月 15 12:51 color.c7a33805ffda0d32bd2a.png
-rw-rw-r--. 1 mizuki mizuki    948  2月 15 12:51 favicon.ico
-rw-rw-r--. 1 mizuki mizuki    293  2月 15 12:51 hue.0614c27197fc3ce572e1.png
-rw-rw-r--. 1 mizuki mizuki    829  2月 15 12:51 index.html
-rw-rw-r--. 1 mizuki mizuki  13112  2月 15 12:51 line.567f57385ea3dde2c9ae.gif
-rw-rw-r--. 1 mizuki mizuki   9427  2月 15 12:51 loading.8732a6660b528fadfaeb.gif
-rw-rw-r--. 1 mizuki mizuki 844209  2月 15 12:51 main-es2015.7cf269935261dc661ba6.js
-rw-rw-r--. 1 mizuki mizuki 957120  2月 15 12:51 main-es5.7cf269935261dc661ba6.js
-rw-rw-r--. 1 mizuki mizuki  27604  2月 15 12:51 open-sans-v15-latin-300.177cc92d2e8027712a8c.ttf
-rw-rw-r--. 1 mizuki mizuki  55181  2月 15 12:51 open-sans-v15-latin-300.27ef0b062b2e221df16f.svg
-rw-rw-r--. 1 mizuki mizuki  18280  2月 15 12:51 open-sans-v15-latin-300.521d17bc9f3526c690e8.woff
-rw-rw-r--. 1 mizuki mizuki  14564  2月 15 12:51 open-sans-v15-latin-300.60c866748ff15f5b347f.woff2
-rw-rw-r--. 1 mizuki mizuki  15545  2月 15 12:51 open-sans-v15-latin-300.76b56857ebbae3a5a689.eot
-rw-rw-r--. 1 mizuki mizuki  15667  2月 15 12:51 open-sans-v15-latin-700.148a6749baa5f658a451.eot
-rw-rw-r--. 1 mizuki mizuki  55652  2月 15 12:51 open-sans-v15-latin-700.2e00b2635b51ba336b4b.svg
-rw-rw-r--. 1 mizuki mizuki  18476  2月 15 12:51 open-sans-v15-latin-700.623e3205570002af47fc.woff
-rw-rw-r--. 1 mizuki mizuki  28192  2月 15 12:51 open-sans-v15-latin-700.7e08cc656863d52bcb5c.ttf
-rw-rw-r--. 1 mizuki mizuki  14720  2月 15 12:51 open-sans-v15-latin-700.d08c09f2f169f4a6edbc.woff2
-rw-rw-r--. 1 mizuki mizuki  56266  2月 15 12:51 open-sans-v15-latin-regular.7aab4c13671282c90669.svg
-rw-rw-r--. 1 mizuki mizuki  15050  2月 15 12:51 open-sans-v15-latin-regular.9dce7f01715340861bdb.eot
-rw-rw-r--. 1 mizuki mizuki  17704  2月 15 12:51 open-sans-v15-latin-regular.bf2d0783515b7d75c35b.woff
-rw-rw-r--. 1 mizuki mizuki  26488  2月 15 12:51 open-sans-v15-latin-regular.c045b73d86803686f4cd.ttf
-rw-rw-r--. 1 mizuki mizuki  14048  2月 15 12:51 open-sans-v15-latin-regular.cffb686d7d2f4682df83.woff2
-rw-rw-r--. 1 mizuki mizuki    118  2月 15 12:51 password-meter.d59e6dc2616c53ce8e77.png
-rw-rw-r--. 1 mizuki mizuki  37293  2月 15 12:51 polyfills-es2015.2987770fde9daa1d8a2e.js
-rw-rw-r--. 1 mizuki mizuki 126094  2月 15 12:51 polyfills-es5.6696c533341b95a3d617.js
-rw-rw-r--. 1 mizuki mizuki  39748  2月 15 12:51 primeicons.2d2afb2719a1ee903e57.eot
-rw-rw-r--. 1 mizuki mizuki  39648  2月 15 12:51 primeicons.66ee0deb739ca71f0ecd.woff
-rw-rw-r--. 1 mizuki mizuki  39572  2月 15 12:51 primeicons.df0140f8e79ecfeffaf8.ttf
-rw-rw-r--. 1 mizuki mizuki 163568  2月 15 12:51 primeicons.e5e0e94474d5fd92e7e8.svg
-rw-rw-r--. 1 mizuki mizuki   1485  2月 15 12:51 runtime-es2015.edb2fcf2778e7bf1d426.js
-rw-rw-r--. 1 mizuki mizuki   1485  2月 15 12:51 runtime-es5.edb2fcf2778e7bf1d426.js
-rw-rw-r--. 1 mizuki mizuki 171599  2月 15 12:51 styles.9fd124168cb08a0155b6.css
        

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

<base href="/">
        
<base href="/code/2020/01/dist/1901/">
        

ファイルをサーバーにアップロードする。

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

参考