「Angular」の版間の差分
(→Mac OS) |
|||
(同じ利用者による、間の20版が非表示) | |||
11行目: | 11行目: | ||
npm で angular/cli をインストール. | npm で angular/cli をインストール. | ||
<pre> | <pre> | ||
− | $ npm install --global @angular/core | + | $ npm install --global @angular/cli @angular/core |
</pre> | </pre> | ||
− | == Upgrade to version 7 == | + | == Upgrade to version 7 or later == |
https://www.techiediaries.com/updating-angular-cli-projects/ を参考。 | https://www.techiediaries.com/updating-angular-cli-projects/ を参考。 | ||
− | + | 最新版にあげるなら、以下を実行する。 | |
<pre> | <pre> | ||
− | $ ng ng update @angular/cli @angular/core | + | $ ng update @angular/cli @angular/core |
+ | </pre> | ||
+ | |||
+ | 特定の Version 例えば 7.x.x にあげる場合、 | ||
+ | <pre> | ||
+ | $ ng update @angular/cli@7 @angular/core@7 | ||
</pre> | </pre> | ||
44行目: | 49行目: | ||
=== サービスを作る === | === サービスを作る === | ||
+ | https://angular.jp/cli/generate を参照。 | ||
+ | |||
<pre> | <pre> | ||
$ ng generate service サービス名 | $ ng generate service サービス名 | ||
66行目: | 73行目: | ||
dist ディレクトリの下のファイルをサーバー上にデプロイする. | dist ディレクトリの下のファイルをサーバー上にデプロイする. | ||
− | == | + | == 追加機能 == |
+ | |||
+ | === rxjs === | ||
+ | Angular アプリケーションから、外部の [https://ja.wikipedia.org/wiki/Ajax ajax] サービスを呼び出すライブラリ。 | ||
+ | |||
+ | アプリケーションディレクトリに移動して、以下のようにインストール。 | ||
+ | |||
+ | <pre> | ||
+ | $ npm install rxjs | ||
+ | </pre> | ||
+ | |||
+ | src/app/app.module.ts に以下を追加。 | ||
+ | <pre> | ||
+ | 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 { } | ||
+ | </pre> | ||
+ | |||
+ | === Material === | ||
+ | テーブルとかポップアップとかアニメーションとかを使いたい時は、https://material.angular.io/ の get started にしたがって angular/material をインストール。 | ||
+ | |||
+ | <pre> | ||
+ | $ npm install --save @angular/material @angular/cdk @angular/animations | ||
+ | </pre> | ||
+ | |||
+ | src/app/app.module.ts に使いたいモジュールを追加。 | ||
+ | <pre> | ||
+ | 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 { } | ||
+ | </pre> | ||
+ | |||
+ | === Google Maps === | ||
+ | 詳しくは、https://angular-maps.com/ を見るべし。 | ||
+ | |||
+ | インストールは、 | ||
+ | <pre> | ||
+ | $ npm i @agm/core --save | ||
+ | </pre> | ||
+ | |||
+ | Mapの高さを指定しなければならない。そこで、テンプレートに | ||
+ | <pre> | ||
+ | <agm-map [style.height]="mapHeight" ...></agm-map> | ||
+ | </pre> | ||
+ | として、xxx.component.ts に、 | ||
+ | <pre> | ||
+ | import { HostListener } from '@angular/core'; | ||
+ | |||
+ | mapHeight = "600px"; | ||
+ | |||
+ | @HostListener('window:resize') | ||
+ | onResize(): void { | ||
+ | const height = window.innerHeight; | ||
+ | this.mapHeight = (ウィンドウの高さから計算される Map のサイズ) + "px"; | ||
+ | } | ||
+ | </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)"> | ||
+ | <!-- NAME column --> | ||
+ | <ng-container matColumnDef="NAME"> | ||
+ | <th mat-header-cell *matHeaderCellDef mat-sort-header mat-sort-header>Name</th> | ||
+ | <td mat-cell *matCellDef="let row">{{ row.name }}</td> | ||
+ | </ng-container> | ||
+ | ... 以下同様 | ||
+ | </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> | ||
+ | === LifeCycle Hooks === | ||
+ | https://angular.jp/guide/lifecycle-hooks 参照。 | ||
+ | * <code>ngOnChanges()</code>: data bound input property に値がセットあるいはリセットされた時に呼ばれる。非常に頻繁に呼び出されるので、パフォーマンスへの影響が大きい。 | ||
+ | * <code>ngOnInit()</code>: 最初に表示される際に data bound property を表示し、コンポーネントの input property をセットした後に、呼び出される。 | ||
+ | * <code>ngDoCheck()</code> | ||
+ | * <code>ngAfterContentInit()</code> | ||
+ | * <code>ngAfterContentChecked()</code> | ||
+ | * <code>ngAfterViewInit()</code> | ||
+ | * <code>ngAfterViewChecked()</code> | ||
+ | * <code>ngOnDestroy()</code> | ||
− | [[Category:Programming]] | + | [[Category:Programming]][[Category:How-To]] |
2021年8月21日 (土) 00:18時点における最新版
Angularを使ったweb application作成の備忘録
目次
Install
Mac OS
brew で node.js, npm をインストール.
$ brew install npm
npm で angular/cli をインストール.
$ npm install --global @angular/cli @angular/core
Upgrade to version 7 or later
https://www.techiediaries.com/updating-angular-cli-projects/ を参考。
最新版にあげるなら、以下を実行する。
$ ng update @angular/cli @angular/core
特定の Version 例えば 7.x.x にあげる場合、
$ ng update @angular/cli@7 @angular/core@7
コマンドリファレンス
プロジェクトを新規作成
$ 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
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 { }
Google Maps
詳しくは、https://angular-maps.com/ を見るべし。
インストールは、
$ npm i @agm/core --save
Mapの高さを指定しなければならない。そこで、テンプレートに
<agm-map [style.height]="mapHeight" ...></agm-map>
として、xxx.component.ts に、
import { HostListener } from '@angular/core'; mapHeight = "600px"; @HostListener('window:resize') onResize(): void { const height = window.innerHeight; this.mapHeight = (ウィンドウの高さから計算される Map のサイズ) + "px"; }
とすると良い。
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)"> <!-- NAME column --> <ng-container matColumnDef="NAME"> <th mat-header-cell *matHeaderCellDef mat-sort-header mat-sort-header>Name</th> <td mat-cell *matCellDef="let row">{{ row.name }}</td> </ng-container> ... 以下同様
コンポーネント (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; } ... }
LifeCycle Hooks
https://angular.jp/guide/lifecycle-hooks 参照。
ngOnChanges()
: data bound input property に値がセットあるいはリセットされた時に呼ばれる。非常に頻繁に呼び出されるので、パフォーマンスへの影響が大きい。ngOnInit()
: 最初に表示される際に data bound property を表示し、コンポーネントの input property をセットした後に、呼び出される。ngDoCheck()
ngAfterContentInit()
ngAfterContentChecked()
ngAfterViewInit()
ngAfterViewChecked()
ngOnDestroy()