「Angular」の版間の差分

提供: fukudat.net
移動先: 案内検索
(Upgrade to version 7)
(sortable table)
(同じ利用者による、間の9版が非表示)
73行目: 73行目:
 
dist ディレクトリの下のファイルをサーバー上にデプロイする.
 
dist ディレクトリの下のファイルをサーバー上にデプロイする.
  
== rxjs ==
+
== 追加機能 ==
以下のようにインストール。
+
 
 +
=== rxjs ===
 +
Angular アプリケーションから、外部の [https://ja.wikipedia.org/wiki/Ajax ajax] サービスを呼び出すライブラリ。
 +
 
 +
アプリケーションディレクトリに移動して、以下のようにインストール。
  
 
<pre>
 
<pre>
107行目: 111行目:
 
</pre>
 
</pre>
  
== Material ==
+
=== Material ===
 
テーブルとかポップアップとかアニメーションとかを使いたい時は、https://material.angular.io/ の get started にしたがって angular/material をインストール。
 
テーブルとかポップアップとかアニメーションとかを使いたい時は、https://material.angular.io/ の get started にしたがって angular/material をインストール。
  
143行目: 147行目:
 
})
 
})
 
export class AppModule { }
 
export class AppModule { }
 +
</pre>
 +
 +
== Tips ==
 +
 +
=== 条件付き要素 ===
 +
ある条件が成り立っている場合にのみ表示される HTML要素が作りたいときは、<code>*ngIf</code>を使う。
 +
 +
コンポーネントのスニペット:
 +
<pre>
 +
...
 +
export class XxxComponent implements OnInit {
 +
  flag: boolean;  // この値が True のとき、要素が表示される。
 +
  ...
 +
}
 +
</pre>
 +
 +
テンプレートのスニペット:
 +
<pre>
 +
...
 +
<div *ngIf="flag">
 +
  this.flag が true のときのみ表示
 +
</div>
 +
<div *ngIf="!flag">
 +
  this.flag が false のときのみ表示
 +
</div>
 +
</pre>
 +
 +
true/false やswitch文のように条件が相互に排他的な場合は、上記のように容易に多分岐をさせることも可能。
 +
しかし、if-else-if-else文のような、条件がカスケードするような多分岐を記述するのは簡単でない。
 +
 +
そのために、<code>*ngIf</code>には<code>else</code>を記述する方法が用意されている。
 +
 +
コンポーネントのスニペット:
 +
<pre>
 +
...
 +
export class XxxComponent implements OnInit {
 +
  data: string;  // この値によって、表示するHTMLを切り替える。
 +
  ...
 +
}
 +
</pre>
 +
 +
テンプレートのスニペット:
 +
<pre>
 +
...
 +
<div *ngIf="data=='A'; else ELSE_1">
 +
  this.data が A のときのみ表示
 +
</div>
 +
<ng-template #ELSE_1>
 +
  <div *ngIf="data=='B'; else ELSE_2">
 +
    this.data が B の時のみ表示
 +
  </div>
 +
</ng-template>
 +
<ng-template #ELSE_2>
 +
  <div *ngIf="data=='C'; else ELSE_3">
 +
    this.data が C の時のみ表示
 +
  </div>
 +
</ng-template>
 +
<ng-template #ELSE_3>
 +
  <div>
 +
    this.data が A, B, C 以外の時のみ表示
 +
  </div>
 +
</ng-template>
 +
</pre>
 +
 +
=== 繰り返し要素 ===
 +
コンポーネントの属性が配列(ここでは array とする)があった時、その要素ごとに HTML要素が作りたいときは <code>*ngFor</code>を使う。
 +
例えば、<nowiki><list> (<ul>, <ol>)</nowiki>の中の<nowiki><li></nowiki>とか、<nowiki><table></nowiki>の中の<nowiki><tr></nowiki>など。
 +
 +
コンポーネント (xxx.component.ts) のスニペット
 +
<pre>
 +
export class XxxComponent implements OnInit {
 +
  array: SomeClass[];  // 繰り返しを作るデータ。型はなんでもよい。
 +
}
 +
</pre>
 +
 +
テンプレート (xxx.component.html) のスニペット
 +
<pre>
 +
<ul>
 +
<li *ngFor="let element of array">{{ element.field }}</li>
 +
</ul>
 +
</pre>
 +
 +
綺麗なテーブルは[[#table|table]]参照。
 +
 +
=== table ===
 +
Material の table を使うと、ヘッダを固定したり、フッタに集計を表示して、テーブルの中身をスクロールさせたり、カラムをダイナミックに選んだり、行をソートしたりできる。
 +
 +
インストールは [[#Material|Material]] を参照。
 +
 +
コンポーネント (xxx.component.ts) のスニペット:
 +
<pre>
 +
class MyRow {
 +
  name: string;      // カラム1
 +
  age: number;      // カラム2
 +
  height: number;  // カラム3
 +
  hobby: string;    // カラム4
 +
}
 +
 +
export class XxxComponent implements OnInit {
 +
  tableData: MyRow[];  // テーブルに表示するデータ
 +
  displayedColumns = [ "NAME", "AGE", "HOBBY" ];  // 表示するカラム
 +
 +
  ngOnInit() {
 +
    // ここである必要はないけど、どこかでデータをセット。外部サービスからデータを入手するの実用的な使い方。
 +
    this.tableData = [ { "name": "Andy", "age": 9, "height": 132.5, "hobby": "Video Game" },
 +
                                    { ... }, ];
 +
  }
 +
}
 +
</pre>
 +
 +
テンプレートのスニペット:
 +
<pre>
 +
<table mat-table [dataSource]="tableData">
 +
  <!-- NAME column -->
 +
  <ng-container matColumnDef="NAME">
 +
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
 +
    <td mat-cell *matCellDef="let row">{{ row.name }}</td>
 +
  </ng-container>
 +
 +
  <!-- AGE column -->
 +
  <ng-container matColumnDef="AGE">
 +
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Age</th>
 +
    <td mat-cell *matCellDef="let row">{{ row.age }}</td>
 +
  </ng-container>
 +
 +
  <!-- HEIGHT column -->
 +
  <ng-container matColumnDef="HEIGHT">
 +
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Height</th>
 +
    <td mat-cell *matCellDef="let row">{{ row.height | number: '.1' }}</td> <!-- フォーマットが | で指定できる -->
 +
  </ng-container>
 +
 +
  <!-- HOBBY column -->
 +
  <ng-container matColumnDef="HOBBY">
 +
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Hobby</th>
 +
    <td mat-cell *matCellDef="let row">{{ row.height }}</td>
 +
  </ng-container>
 +
 +
  <!-- ここで行の繰り返しを指定 -->
 +
  <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
 +
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
 +
  <tr mat-footer-row *matFooterRowDef="displayedColumns; sticky: true"></tr> 
 +
</pre>
 +
 +
=== sortable table ===
 +
上記の[[#table|table]]をソート可能なテーブルにする。
 +
 +
テンプレートのスニペット:
 +
<pre>
 +
<table mat-table [dataSource]="tableData" matSort (matSortChange)="sortData($event)">
 +
... 以下同様
 +
</pre>
 +
 +
コンポーネント (xxx.component.ts) のスニペット:
 +
<pre>
 +
import { MatSort, Sort } import from '@angular/material/sort';
 +
...
 +
export class XxxComponent implements OnInit {
 +
  sortData(sort: Sort) {
 +
    const sort_dir = sort.diretion === 'asc' ? 1 : -1;
 +
    this.tableData = this.tableData.slice();  // データをコピーする。
 +
    const compare = (a, b) => { return (a < b ? -1 : 1) * sort_dir; };
 +
    switch (sort.active) {
 +
    case 'NAME':
 +
      this.tableData.sort((a, b) => { return compare(a.NAME, b.NAME); }); break;
 +
    case 'AGE':
 +
      this.tableData.sort((a, b) => { return compare(a.AGE, b.AGE); }); break;
 +
    case 'HEIGHT:
 +
      this.tableData.sort((a, b) => { return compare(a.HEIGHT, b.HEIGHT); }); break;
 +
    default:
 +
      break;
 +
    }
 +
    ...
 +
  }
 
</pre>
 
</pre>
  
 
[[Category:Programming]]
 
[[Category:Programming]]

2019年8月15日 (木) 11:48時点における版

Angularを使ったweb application作成の備忘録

Install

Mac OS

brew で node.js, npm をインストール.

$ brew install npm

npm で angular/cli をインストール.

$ npm install --global @angular/core

Upgrade to version 7 or later

https://www.techiediaries.com/updating-angular-cli-projects/ を参考。

Version 7.x.x にあげる場合、以下を実行する。

$ ng update @angular/cli@7 @angular/core@7

最新版にあげるなら、

$ ng update @angular/cli @angular/core

コマンドリファレンス

プロジェクトを新規作成

$ ng new "アプリケーション名"
$ cd "アプリケーション名"

カレントディレクトリの下に "アプリケーション名"というディレクトリができる. src/app/ ディレクトリの下にソースコードのテンプレートが作られる.

コンポーネントを作る

https://angular.jp/cli/generate を参照。

$ ng generate component コンポーネント名

src/app/コンポーネント名 ディレクトリの下にソースコードのテンプレートが作られる.

例えば、HelloWorld という名前のコンポーネントを作ると、src/app/hello-world というディレクトリが作成され、その下に hello-world.component.{ts|html|css|spec.ts} というファイルが作られる。 これらのファイルを src/appの直下に作りたい場合は,--flat オプションを付ける.

サービスを作る

https://angular.jp/cli/generate を参照。

$ ng generate service サービス名

src/app ディレクトリの下にソースコードのテンプレートが作られる.

例えば、FileSystem というサービスを作ると、src/appディレクトリの下に file-system.service.{ts|spec.ts} というファイルが作られる。

開発用サーバーを起動する

https://angular.jp/cli/serve を参照。

$ ng serve

デプロイ用にビルドする

https://angular.jp/cli/build を参照。

$ ng build --base-href='/デプロイするサーバー上のpath/' --prod --aot

dist ディレクトリの下のファイルをサーバー上にデプロイする.

追加機能

rxjs

Angular アプリケーションから、外部の ajax サービスを呼び出すライブラリ。

アプリケーションディレクトリに移動して、以下のようにインストール。

$ npm install rxjs rxjs-compat

src/app/app.module.ts に以下を追加。

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

// ↓追加
import { HttpClientModule } from '@angular/common/http';
// ↑追加

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    // ↓追加
    HttpClientModule,
    // ↑追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Material

テーブルとかポップアップとかアニメーションとかを使いたい時は、https://material.angular.io/ の get started にしたがって angular/material をインストール。

$ npm install --save @angular/material @angular/cdk @angular/animations

src/app/app.module.ts に使いたいモジュールを追加。

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

// ↓追加
import { MatTableModule } from '@angular/material/table';
import { MatProgressSpinnerModule } from '@angular/material';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
// ↑追加

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    // ↓追加
    MatTableModule,
    MatProgressSpinnerModule,
    BrowserAnimationsModule,
    // ↑追加
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Tips

条件付き要素

ある条件が成り立っている場合にのみ表示される HTML要素が作りたいときは、*ngIfを使う。

コンポーネントのスニペット:

...
export class XxxComponent implements OnInit {
  flag: boolean;  // この値が True のとき、要素が表示される。
  ...
}

テンプレートのスニペット:

...
<div *ngIf="flag">
  this.flag が true のときのみ表示 
</div>
<div *ngIf="!flag">
  this.flag が false のときのみ表示
</div>

true/false やswitch文のように条件が相互に排他的な場合は、上記のように容易に多分岐をさせることも可能。 しかし、if-else-if-else文のような、条件がカスケードするような多分岐を記述するのは簡単でない。

そのために、*ngIfにはelseを記述する方法が用意されている。

コンポーネントのスニペット:

...
export class XxxComponent implements OnInit {
  data: string;  // この値によって、表示するHTMLを切り替える。
  ...
}

テンプレートのスニペット:

...
<div *ngIf="data=='A'; else ELSE_1">
  this.data が A のときのみ表示 
</div>
<ng-template #ELSE_1>
  <div *ngIf="data=='B'; else ELSE_2">
    this.data が B の時のみ表示
  </div>
</ng-template>
<ng-template #ELSE_2>
  <div *ngIf="data=='C'; else ELSE_3">
    this.data が C の時のみ表示
  </div>
</ng-template>
<ng-template #ELSE_3>
  <div>
    this.data が A, B, C 以外の時のみ表示
  </div>
</ng-template>

繰り返し要素

コンポーネントの属性が配列(ここでは array とする)があった時、その要素ごとに HTML要素が作りたいときは *ngForを使う。 例えば、<list> (<ul>, <ol>)の中の<li>とか、<table>の中の<tr>など。

コンポーネント (xxx.component.ts) のスニペット

export class XxxComponent implements OnInit {
  array: SomeClass[];   // 繰り返しを作るデータ。型はなんでもよい。
}

テンプレート (xxx.component.html) のスニペット

<ul>
<li *ngFor="let element of array">{{ element.field }}</li>
</ul>

綺麗なテーブルはtable参照。

table

Material の table を使うと、ヘッダを固定したり、フッタに集計を表示して、テーブルの中身をスクロールさせたり、カラムをダイナミックに選んだり、行をソートしたりできる。

インストールは Material を参照。

コンポーネント (xxx.component.ts) のスニペット:

class MyRow {
  name: string;      // カラム1
  age: number;      // カラム2
  height: number;  // カラム3
  hobby: string;     // カラム4
}

export class XxxComponent implements OnInit {
  tableData: MyRow[];  // テーブルに表示するデータ
  displayedColumns = [ "NAME", "AGE", "HOBBY" ];  // 表示するカラム

  ngOnInit() {
    // ここである必要はないけど、どこかでデータをセット。外部サービスからデータを入手するの実用的な使い方。
    this.tableData = [ { "name": "Andy", "age": 9, "height": 132.5, "hobby": "Video Game" },
                                    { ... }, ];
  }
}

テンプレートのスニペット:

<table mat-table [dataSource]="tableData">
  <!-- NAME column -->
  <ng-container matColumnDef="NAME">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
    <td mat-cell *matCellDef="let row">{{ row.name }}</td>
  </ng-container>

  <!-- AGE column -->
  <ng-container matColumnDef="AGE">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Age</th>
    <td mat-cell *matCellDef="let row">{{ row.age }}</td>
  </ng-container>

  <!-- HEIGHT column -->
  <ng-container matColumnDef="HEIGHT">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Height</th>
    <td mat-cell *matCellDef="let row">{{ row.height | number: '.1' }}</td> <!-- フォーマットが | で指定できる -->
  </ng-container>

  <!-- HOBBY column -->
  <ng-container matColumnDef="HOBBY">
    <th mat-header-cell *matHeaderCellDef mat-sort-header>Hobby</th>
    <td mat-cell *matCellDef="let row">{{ row.height }}</td>
  </ng-container>

  <!-- ここで行の繰り返しを指定 -->
  <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
  <tr mat-footer-row *matFooterRowDef="displayedColumns; sticky: true"></tr>  

sortable table

上記のtableをソート可能なテーブルにする。

テンプレートのスニペット:

<table mat-table [dataSource]="tableData" matSort (matSortChange)="sortData($event)">
... 以下同様

コンポーネント (xxx.component.ts) のスニペット:

import { MatSort, Sort } import from '@angular/material/sort';
...
export class XxxComponent implements OnInit {
  sortData(sort: Sort) {
    const sort_dir = sort.diretion === 'asc' ? 1 : -1;
    this.tableData = this.tableData.slice();  // データをコピーする。
    const compare = (a, b) => { return (a < b ? -1 : 1) * sort_dir; };
    switch (sort.active) {
    case 'NAME':
      this.tableData.sort((a, b) => { return compare(a.NAME, b.NAME); }); break;
    case 'AGE':
      this.tableData.sort((a, b) => { return compare(a.AGE, b.AGE); }); break;
    case 'HEIGHT:
      this.tableData.sort((a, b) => { return compare(a.HEIGHT, b.HEIGHT); }); break;
    default:
      break;
    }
    ...
  }