Angular Tutorial 4章

パイプ

i18nPlural(P2046)

数字を文字列に変換する

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  favs0: string[] = [];
  favs1 = ['yohei','miyamoto'];
  favs2 = ['yohei','miyamoto', 'test'];
  messages = {
    '=0':'「いいね!」されていません',
    '=1':'1人だけ「いいね!」と言ってくれています',
    'other':'#人が「いいね!」と言ってくれています'
  }
}

src/app/app.component.html

<div>
  <ul>
    <li>{{favs0.length | i18nPlural: messages}}</li>
    <li>{{favs1.length | i18nPlural: messages}}</li>
    <li>{{favs2.length | i18nPlural: messages}}</li>
  </ul>
</div>

表示結果

「いいね!」されていません
2人が「いいね!」と言ってくれています
3人が「いいね!」と言ってくれています

i18nSelect(P2075)

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  users: User[] = [
    {name: 'taro', sex: 'male'},
    {name: 'hanako', sex: 'female'},
    {name: 'totoro', sex: '???'},
  ]
  messages = {
    'male': '彼',
    'female': '彼女',
    'other': '彼/彼女',
  }
}

class User {
  name: string;
  sex: string;
}

src/app/app.component.html

<div>
  <ul>
    <li *ngFor="let u of users">
      {{u.sex | i18nSelect:messages}}は{{u.name}}です。
    </li>
  </ul>
</div>

出力結果

彼はtaroです。
彼女はhanakoです。
彼/彼女はtotoroです。

ngIfディレクティブ(P2131)

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  show = false
}

src/app/app.component.html

// falseの場合、htmlのツリーからも破棄される
<div *ngIf="show">
  show!
</div>

HTMLのツリーからは、破棄せずその上で非表示にしたい場合は以下

displayスタイルプロパティを使う場合

<div [style.display]="show ? 'inline':'none'">
  hello
</div>

クラスバインディングを使う場合

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  none = false;
}

src/app/app.component.css

.none{
  display: none;
}

src/app/app.component.html

<div [class.none]="none">
  hello
</div>

ngifディレクティブ[else] (P2180)

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  show = true;
}

src/app/app.component.html

<div *ngIf="show; then trueContent; else falseContent">
  この部分は無視される
</div>

<ng-template #trueContent>
  <p>true</p>
</ng-template>
<ng-template #falseContent>
  <p>false</p>
</ng-template>

ngSwitch(P2202)

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  season = '';
}

src/app/app.component.html

<form>
  <select name="season" [(ngModel)]="season">
    <option value="one">one</option>
    <option value="two">two</option>
  </select>
</form>

<div [ngSwitch]="season">
  <span *ngSwitchCase="''">何も選択してないですね。</span>
  <span *ngSwitchCase="'one'">oneを選択しましたね。</span>
  <span *ngSwitchDefault>one以外を選択しましたね。</span>
</div>

ngForとngContainer(P2295)

quickstart/src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  users: User[] = [
    {name: 'yohei', sex: 'male'},
    {name: "miyamoto", sex: 'female'},
  ]
}

src/app/app.component.html

// divで囲む場合
<div *ngFor="let u of users">
  <p>{{u.name}}</p>
  <p>{{u.sex}}</p>
</div>

// ng-containerを使う場合(プログラムの都合でdivが発生しないようにできる)
<ng-container *ngFor="let u of users">
  <p>{{u.name}}</p>
  <p>{{u.sex}}</p>
</ng-container>

出力結果

// divで囲む場合
<div>
 <p>yohei</p>
 <p>male</p>
</div>
<div>
 <p>male</p>
 <p>female</p>
</div>

// ng-containerを使う場合(divで囲まれない)
<p>yohei</p>
<p>male</p>
<p>male</p>
<p>female</p>

ngFor トラッキング(P2316)

トラッキングしない場合

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  users: User[] = [
    {name: 'yohei', gender: 'male'},
    {name: "miyamoto", gender: 'female'},
  ]
  onClick() {
    this.users = [
      {name: 'yohei', gender: 'male'},
      {name: 'miyamoto', gender: 'female'},
      {name: 'yohei', gender: 'male'},
    ]
  }
}

class User {
  name: string;
  gender: string;
}

src/app/app.component.html

<ng-container *ngFor="let u of users">
  <p>{{u.name}}</p>
  <p>{{u.sex}}</p>
</ng-container>
<input type="button" (click)="onClick()" value="更新">

結果

usersの全レコードが更新されてしまう

トラッキングする場合

更新前との差分のみを追加することができる

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  users: User[] = [
    {id: 1, name: 'yohei', gender: 'male'},
    {id: 2, name: "miyamoto", gender: 'female'},
  ]
  onClick() {
    this.users = [
      {id: 1, name: 'yohei', gender: 'male'},
      {id: 2, name: "miyamoto", gender: 'female'},
      {id: 3, name: "tarou", gender: 'female'},
    ]
  }
  // ユニークになるキーを戻す
  trackFn(index: any, user: any){
    return user.id;
  }
}
class User {
  id: number;
  name: string;
  gender: string;
}

src/app/app.component.html

<ng-container *ngFor="let u of users; trackBy: trackFn">
  <p>{{u.name}}</p>
  <p>{{u.gender}}</p>
</ng-container>
<input type="button" (click)="onClick()" value="更新">

sliceでのページング(P2350)

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  start = 0;
  len = 3;
  users: User[] = [
    {id: 1, name: 'yohei1', gender: 'male'},
    {id: 2, name: "yohei2", gender: 'female'},
    {id: 3, name: "yohei3", gender: 'female'},
    {id: 4, name: "yohei4", gender: 'female'},
    {id: 5, name: "yohei5", gender: 'female'},
  ]
  pager(page: number) {
    this.start = this.len * page;
  }
}
class User {
  id: number;
  name: string;
  gender: string;
}

src/app/app.component.html

<table class="table">
 <tr>
  <th>ISBNコード</th><th>書名</th><th>価格</th><th>出版社</th>
  </tr>
// ngForにsliceを使う
  <tr *ngFor="let u of users | slice: start: start+len">
    <td>{{u.name}}</td>
    <td>{{u.gender}}</td>
  </tr>
</table>
<ul class="pagination">
  <li><a href="#" (click)="pager(0)">1</a></li>
  <li><a href="#" (click)="pager(1)">2</a></li>
  <li><a href="#" (click)="pager(2)">3</a></li>
</ul>

ngStyle(P2355)

1つのスタイルを付与するだけであれば、スタイルバインディグでいい。
https://qiita.com/YoheiMiyamoto/items/29fef991ddcd61dcd285#%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%83%90%E3%82%A4%E3%83%B3%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0p1462

複数設定するのであれば、 ngStyle を使う方が良い

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

@Component({
  selector: 'my-app',
  template: `<div [ngStyle]="style">hello</div>`
})
export class AppComponent  {
  style = {
    backgroundColor: '#f00',
    color: '#fff',
    fontWeight: 'bold',
    margin: '15px',
    padding: '15px'
  };
}

ngClass(P2393)

src/app/app.component.css

.back{
  background-color: red;
}

.fore{
  color: blue;
}

.space{
   padding: 15px;
}

個別に着脱

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
    flag = false;
    styles = {
      back: false,
      fore: false,
      space: false,
    }
}

src/app/app.component.html

<form>
  <input type="button" (click)="styles.back=!styles.back" value="back">
  <input type="button" (click)="styles.fore=!styles.fore" value="fore">
  <input type="button" (click)="styles.space=!styles.space" value="space">
</form>

<div [ngClass]="styles">
  hello
</div>

ngClassをまとめて着脱

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
    flag = false;
}

src/app/app.component.html

<form>
  <input type="button" (click)="flag=!flag" value="ON/OFF">
</form>
// 3つのクラスを同時に着脱
<div [ngClass]="{'back fore space':flag}">
  hello
</div>

ngPlural(P2435)

i18nPluralパイプのディレクティブ版
https://qiita.com/YoheiMiyamoto/items/22f73f5127429a427829#i18npluralp2046
quickstart/src/app/app.component.html

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  favs: string[] = [ '山田理央', '鈴木洋平', '腰掛奈美' ];
}

src/app/app.component.html

// ロジックをパイプと違ってHTMLに書く事になる。メッセージを動的に組み立てたい時はパイプより便利。
<div [ngPlural]="favs.length">
  <ng-template ngPluralCase="=0">[いいね!]されていません。
  </ng-template>
  <ng-template ngPluralCase="=1">1人だけ[いいね!]と言ってくれています。
  </ng-template>
  <ng-template ngPluralCase="other">
    {{favs.length}}人が[いいね!]と言っています。
  </ng-template>
</div>

ngTemplateOutlet(P2506)

テンプレートをインポートする

src/app/app.component.ts

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  users :User[] = [
    {id: 1, name: 'yohei', gender: 'male'},
    {id: 2, name: 'miyamoto', gender: 'male'}
  ];
  usersIndex = 0;
}

class User {
  id: number;
  name: string;
  gender: string;
}
// 事前に用意したテンプレート(myTemp)
<ng-template #myTemp let-name="name" let-gender="gender">
  <div>{{name}}</div>
  <div>{{gender}}</div>
</ng-template>

// usersIndexをプルダウンからセット
<select name="usersIndex" [(ngModel)]="usersIndex">
  <option *ngFor="let u of users; let i = index" [value]="i">{{u.name}}</option>
</select>

// テンプレートの呼び出し
<ng-container *ngTemplateOutlet="myTemp; context: users[usersIndex]">

ngComponentOutlet

コンポーネントを動的にインポートする

src/app/app.module.ts

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule} from '@angular/forms'
import { AppComponent }  from './app.component';

// コンポーネントを読み込む
import { BookComponent }  from './book.component';
import { UserComponent }  from './user.component';

@NgModule({
  imports:      [ BrowserModule, FormsModule ],
  declarations: [ AppComponent, BookComponent, UserComponent ],
  entryComponents: [ BookComponent, UserComponent ], // entryComponentsの設定も必要
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

src/app/user.component.ts

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

@Component({
  selector: 'my-event',
  template: `
    <div class="user">UserComponent!</div>
  `,
  styleUrls: ['app/app.component.css']
})
export class UserComponent {
}

src/app/book.component.ts

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

@Component({
  selector: 'my-event',
  template: `
    <div class="book">BookComponent!</div>
  `,
  styleUrls: ['app/app.component.css']
})
export class BookComponent {
}

src/app/app.component.ts

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

// コンポーネントを読み込む
import { BookComponent } from './book.component';
import { UserComponent } from './user.component';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent  {
  interval: any;
  comps = [ BookComponent, UserComponent ]; // 配列でコンポーネントを読み込む
  current = 0;
  banner: any = BookComponent;
  ngOnInit() {
    this.interval = setInterval(() => {
      this.current = (this.current + 1) % this.comps.length; // 配列をループさせる
      this.banner = this.comps[this.current]; // bannerオブジェクトにコンポーネントを入れる
    }, 3000);
  }
}

src/app/app.component.html

<ng-container *ngComponentOutlet="banner"></ng-container>