Angularのif,for,イベント,クラスバインディング
Vue.jsと比較をするために、Angularのチュートリアルに従って、構文を勉強しています。
前回はAngularのコンポーネントの作成から、インターフェース、双方向データバインディングまでを学びました。
今回は*ngFor、*ngIf、イベント設定、クラスバインディング(条件文によるclassの付与)を学びます。
サンプルデータの作成
データが1件だけだと、全体像をつかみづらいのでもう少し多くのサンプルーデータを作ります。サンプルデータはsrc¥app¥フォルダにmock-heroes.tsという名前で作成します。
ちなみにモック(mock)とは偽物という意味でAngularの用語ではありません。日本でも、商品としては機能しない外観だけの商品見本を「モック」と呼んだりします。
HEROESという定数を作って、そこにHeroインターフェースの配列(Hero[])を定義して、そのあとのデータで初期化します。
同じ階層にある、hero.tsのHeroインターフェースを利用するのでインポートしてから定義します。
ここでは定数値であるHEROESは変数と区別するためにすべて大文字で記述しています。
mock-heroes.ts
import { Hero } from './hero';
export const HEROES: Hero[] = [
{ id: 11, name: 'Dr Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
*ngFor
前回の[(ngModel)]もそうでしたが、HTMLファイルに記述するAngularの命令文をディレクティブと呼びます。
*ngForもその一つで、ディレクティブを記述した要素をfor文のように繰り返します。
このディレクティブを使って、前回登場したheroes.component.htmlの中身を書き換えるのですが、そのまえにheroes.component.tsにHEROES定数を定義しなくてはいけません。
heroes.component.ts
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HEROES } from '../mock-heroes';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes: HEROES;
constructor() { }
ngOnInit(): void {
}
}
heroes.component.htmlを次のようにします。liに対して*ngForを利用することで繰り返します。"let h of heroes"とすることで、heroesの中にある要素がなくなるまで繰り返します。取り出された値はhに入ります。
heroes.component.html
<h2>Heroes</h2>
<ul class="heroes">
<li *ngFor="let h of heroes">
<span class="badge">{{h.id}}</span>{{h.name}}
</li>
</ul>
コンポーネントのCSS
ここまでほとんど触れてきませんでしたが、コンポーネントのcss(heroes.component.css)は、対象のコンポーネントだけに有効です。CSSファイルの指定は、.tsファイルの@Component内のstyleUrls:として記述すると以前書きましたが、styleUrlsをstylesとすることで中の配列に直接CSSを書くこともできます。
heroes.component.ts
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HEROES } from '../mock-heroes';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
//styleUrls: ['./heroes.component.css']
styles: ['h1,h2 {color: yellow;}','li {color: red;}']
})
export class HeroesComponent implements OnInit {
heroes: Hero[] = HEROES;
constructor() { }
ngOnInit(): void {
}
}
上記のようにした時「Tour of Heroes」の文字はh1の要素ですが、コンポーネントの外なので元の色のままです。
*ngIfとイベント
リストを表示することができたので、今度はそれぞれにクリックイベントを設定してみます。
heroのそれぞれの行をクリックしたとき、その詳細をリストの下段に表示するようにします。
その際に利用できるのが*ngIfです。*ngIfは設定された値が真の時のみ出力をし、そうでない場合はDOMから除去してくれるものです。
liクリックで詳細表示用の変数に値をセットする仕組にし、その値が存在する時は*ngIfで表示させるようにします。
クリックイベントを拾うには(click)="関数名(パラメタ)"と記述します。この関数はheroes.component.tsに記述します。
さらにheroes.component.tsには詳細表示用の変数として、selectedHeroを定義します。
コード全体は次のように変わります。
heroes.component.html
<h2>Heroes</h2>
<ul class="heroes">
<li *ngFor="let h of heroes" (click)="onSelect(h)" >
<span class="badge">{{h.id}}</span>{{h.name}}
</li>
</ul>
<div *ngIf="selectedHero">
<h2>{{selectedHero.name | uppercase}} Details</h2>
<div><span>id: </span>{{selectedHero.id}}</div>
<div>
<label>name:
<input [(ngModel)]="selectedHero.name" placeholder="name"/>
</label>
</div>
</div>
heroes.component.ts
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { HEROES } from '../mock-heroes';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
heroes: Hero[] = HEROES;
selectedHero: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
constructor() { }
ngOnInit(): void {
}
}
動的なクラス付与
ある条件により、HTML要素に対してクラスを付与したり外したりする機能をクラスバインディングと呼びます。
コンポーネントのHTMLの要素の属性として、[class.付与したいクラス]="条件" と記述することで実現できます。
ここでは、li要素に対して、selectedというクラスを付与したり外すことで選択状況を可視化させます。
heroes.component.html
<h2>Heroes</h2>
<ul class="heroes">
<li *ngFor="let h of heroes" (click)="onSelect(h)" [class.selected]="h===selectedHero" >
<span class="badge">{{h.id}}</span>{{h.name}}
</li>
</ul>
<div *ngIf="selectedHero">
<h2>{{selectedHero.name | uppercase}} Details</h2>
<div><span>id: </span>{{selectedHero.id}}</div>
<div>
<label>name:
<input [(ngModel)]="selectedHero.name" placeholder="name"/>
</label>
</div>
</div>
この時のCSSは次のようになっています。
heroes.component.css
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes li.selected {
background-color: #CFD8DC;
color: white;
}
.heroes li.selected:hover {
background-color: #BBD8DC;
color: white;
}
.heroes .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color:#405061;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}