LoginSignup
12
11

More than 5 years have passed since last update.

Angluar2のクイックスタートとチュートリアルを実施 - その6

Last updated at Posted at 2016-09-21

前回の投稿Angluar2のクイックスタートとチュートリアルを実施 - その5の続きです。
前回作成したソースを使用します。

本章ではルーティング(=画面遷移)を学びます。ナビゲーションとも呼びます。ビューからビューへの移動を定義します。

本章は結構長いです...

本投稿の参照元(英語)

Routing - ts

本章で学ぶこと

前述のほか、以下を学びます。

  • ダッシュボード1ビューを追加する
  • ルーターによりヒーロー一覧とダッシュボードビューを切り替える
  • HeroServiceを複数コンポーネントで共有する
  • htmlとcssを外部ファイルに移動する
  • パイプuppercaseの使い方を学ぶ

ナビゲーションのイメージは以下です。(公式より拝借)

イメージ

実行結果

本章を完了すると以下リンク先のようなものが出来上がります。
尚、右上の青いボタンを押すことで全画面表示を行うことができます。
上図の実際の動きがわかります。
live-examples

事前準備

以下のコマンドによりサーバの起動を行います。プログラムの変更が即座にブラウザに反映されます。(サーバを落としていなければ不要です。)

cd angular2-tour-of-heroes
npm start

参考

公式のRouting and Navigation(英語)では、本章についてのより詳細な情報が記載されています。

計画

以下の計画に沿って実施します

  • AppComponentの役割をナビゲーションのハンドルだけにする
  • ヒーロー一覧をAppComponentからHeroesComponentに移す
  • ルーティングを作成する
  • DashboardComponentを作成する
  • ダッシュボードにナビゲーション構造を持たせる

AppComponentを分割

現在はAppComponentをロードするとヒーロー一覧を即座に表示しますが、画面の遷移を行うシェルにし、その他のことは他のコンポーネントにまかせます。
AppComponentクラスからヒーロー一覧をHeroesComponentに移動させましょう。

HeroesComponent

AppComponentは既にヒーロー一覧用のプログラムなので、そのままHeroesComponentに変更します。以下のリネームを実施します。

種別 変更前 変更後
ファイル名 app/app.component.ts app/heroes.component.ts
クラス名 AppComponent HeroesComponent
@Componentデコレータのselector my-app my-heroes

新しいAppComponent

新しいapp/app.component.tsはアプリケーションシェルにします。いくつかのコンポーネントへのリンクを画面上部に表示し、ページへナビゲートします。まず以下を行います。

  • app/app.component.tsファイルを作成する

  • app/app.component.tsでやること・・・

    • AppComponentクラスの定義
    • @Componentデコレータとmy-appセレクタの追加
    • HeroesComponentからAppComponentへ以下を移動させる
      • title クラスプロパティ
      • @Componentのテンプレートの <h1> 要素。これは、titleのバインドを含む
    • @Componentのテンプレートに<my-heroes> 要素を追加し、ヒーロー一覧を表示 させる
    • Componentのインポート
  • app/app.module.tsでやること・・・

    • @AppModuledeclarations配列にHeroesComponentを追加し<my-heroes>タグを使用可能にする
    • @AppModuleprovidersHeroServiceを追加
  • app/heroes.component.tsでやること・・・

    • @Componentprovidersを削除

結果app/app.component.ts, app/app.module.tsは以下の様になります。

app/app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <my-heroes></my-heroes>
  `
})
export class AppComponent {
  title = 'Tour of Heroes';
}
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 { HeroDetailComponent } from './hero-detail.component';
import { HeroesComponent }     from './heroes.component';
import { HeroService }         from './hero.service';
@NgModule({
  imports: [
    BrowserModule,
    FormsModule
  ],
  declarations: [
    AppComponent,
    HeroDetailComponent,
    HeroesComponent
  ],
  providers: [
    HeroService
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule {
}

この段階でもまだアプリケーションは動作します。リファクタリングによる問題はおきません。

ベースタグの追加

index.html<head>セクションの下に<base href="/">タグを追加します。

index.html
<head>
  <base href="/">

base hrefは必要です。理由はRouter#base-href(英語)に記載があります。

ルーティングの追加

今まで、自動的にヒーロー一覧を表示していた代わりに、ボタンをクリックしたら表示するように変更します。これにはコンポーネントルーターを使用します。
Angularのルーターは外側におり、オプションモジュールで、RouterModuleと呼ばれます。
ルーターは複数の提供サービス(RouterModule)と、複数のディレクティブ(RouterOutlet, RouterLink, RouterLinkActive)と、設定ファイル(Routes)の組み合わせからなります。最初に行うのは設定です。

route2の設定

まだこのアプリは何のrouteも設定していません。コンフィグファイルを作りアプリケーションrouteを設定します。
Routesはユーザがボタンをクリックしたり、URLを貼り付けたときに、どのビューを表示するかをルーターに伝えます。
app/app.routing.tsファイルを作成し、HeroesComponentへのrouteを定義します。

app/app.routing.ts
import { ModuleWithProviders }  from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HeroesComponent }      from './heroes.component';

const appRoutes: Routes = [
  {
    path: 'heroes',
    component: HeroesComponent
  }
];

Routesはroute定義の配列です。現在routeはHeroesComponetへの一つだけしかないですが後で追加します。routeの定義は以下からなります。

  • path: routerがこのパスとブラウザのURLの一致を確認する(この場合heroes)
  • component: このrouteにナビゲートされたときルーターが作成するコンポーネント (この場合HeroesComponent)

Routesの詳細は、公式のRouting(英語)にあります。

routing定数を宣言し、他で使用できるようにエクスポートします。この値はRouterModule.forRootメソッドの戻り値です。このメソッドはroutesを引数に、configured router moduleを返します。AppModule@NgModuleにそれを追加します。

app/app.routing.ts
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);

forRootと呼ばれる理由は、アプリケーションroot(AppModule)用のルーターを提供するためです。forRootメソッドはルーターサービスのプロバイダーとディレクティブを返します。これらはルーティングに使用します。

利用可能なルーターの作成

app/app.routing.tsに初期設定をしました。rootモジュールのNgModuleにそれを追加しましょう。routing定数をapp.routing.tsからインポートし、AppModuleimports配列に追加します。

app/app.module.ts
import { routing } from './app.routing';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    routing
  ],
})
export class AppModule {
}

Router Outlet(ルーターの出口)

ブラウザのアドレスバーに/heroesを入れるとHeroesComponentを表示しますが、ページのどこに表示するかは、テンプレートの<router-outlet>要素によって定めます。
RouterOutletRouterModuleにより提供されるディレクティブの一つです。
ルーターは各コンポーネントを<router-outlet>の場所に表示します。

Router Links(ルーターリンク)

アプリ利用者により、route URLをアドレスバーに貼り付けられることを想定していません。テンプレートにアンカータグを追加することで、クリックされた際にHeroesComponentへナビゲートします。

app/app.component.ts
template: `
   <h1>{{title}}</h1>
   <a routerLink="/heroes">Heroes</a>
   <router-outlet></router-outlet>
 `

routerLinkは、アンカータグにバインドされています。RouterLinkディレクティブはリンクをクリックしたとき、指定した文字列(ここでは/heroes)がどこへナビゲートするかをルーターへ伝えます。
直接のリンクをやめたため、ルートパスへルーティング指示をワンタイムバインディングとして定義します。route設定をもう一度確認してください。'/heroes'をHeroesComponentへ通しています。

公式のRouting#link-parameters-array(英語)で、直接のルーターリンクや、リンクパラメータ配列について学べます。

ブラウザを更新すると、タイトルとheroesリンクだけになり、ヒーロー一覧が見えなくなります。
現在、ブラウザーのアドレスバーは/を指定しています。HeroesComponentを表示するのは/heroesなので何も見えません。後で対応します。アドレスを/heroesに変えれば、ヒーロー一覧が表示されます。

本項により、AppComponentは以下の様になりました。

app/app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `
     <h1>{{title}}</h1>
     <a routerLink="/heroes">Heroes</a>
     <router-outlet></router-outlet>
   `
})
export class AppComponent {
  title = 'Tour of Heroes';
}

AppComponentは、ルーターとrouteされたビューが結びつけられました。このようなコンポーネントをルーターコンポーネントと呼びます。

ダッシュボード1の追加

ルーティングは複数のビューがあってこそ意味をなします。新しいビューを作ります。
app/dashboard.component.tsを以下の様に作成します。

app/dashboard.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'my-dashboard',
  template: '<h3>My Dashboard</h3>'
})
export class DashboardComponent { }

後で役に立つように変更します。

ダッシュボードのroute設定

ダッシュボードのビューが表示されるように、app/app.routing.tsRoutesに定義を追加します。

app/app.routing.ts
{
  path: 'dashboard',
  component: DashboardComponent
},

rootモジュールであるAppModuleクラスNgModuledeclarations配列に、DashboardComponentを追加します。

app/app.module.ts
declarations: [
  AppComponent,
  DashboardComponent,
  HeroDetailComponent,
  HeroesComponent
],

リダイレクトの設定

アプリケーションの開始時にアドレスバー/dashboardを表示し、ダッシュボードを表示します。これにはリダイレクトを使用します。
以下の様にapp/app.routing.tsを変更します。

app/app.routing.ts
{
  path: '',
  redirectTo: '/dashboard',
  pathMatch: 'full'
},

リダイレクトについての詳細は、公式のRouter(英語)にあります。

テンプレートにナビゲーションを追加

最後に、ヒーロー一覧より前に、ダッシュボードのリンクを追加します。

app/app.component.ts
template: `
   <h1>{{title}}</h1>
   <nav>
     <a routerLink="/dashboard">Dashboard</a>
     <a routerLink="/heroes">Heroes</a>
   </nav>
   <router-outlet></router-outlet>

リンクを<nav>タグ内に記載していますが、今は何も機能しません。後でスタイルを追加するときに便利なのでタグを記載します。

ブラウザで、アプリケーションroot(/)を表示するとダッシュボードが表示され、ダッシュボードとヒーロー一覧へのリンクが表示されます。

ダッシュボードのトップヒーロー一覧

dashboard.component.tsのhtmlテンプレートを外部テンプレートファイルに変更します。以下の様にtemplateの部分をtemplateUrlに変更してください。
ファイル名はapp/dashboard.component.htmlとします。

app/dashboard.component.ts
templateUrl: 'app/dashboard.component.html',

なお、Angularでは相対パスはサポートしていませんので、アプリケーションルートからの絶対パスでURLを指定する必要があります。(今回の場合app/dashboard.component.html)
中身を以下の様に定義します。

app/dashboard.component.html
<h3>Top Heroes</h3>
<div class="grid grid-pad">
  <div *ngFor="let hero of heroes" (click)="gotoDetail(hero)" class="col-1-4">
    <div class="module hero">
      <h4>{{hero.name}}</h4>
    </div>
  </div>
</div>

もう一度、ヒーロー一覧のイテレートのために*ngForを使用しヒーロー名を表示します。本章の後でスタイルを追加するために、<div>を入れています。
また、(clock)にバインドしているgotoDetail()メソッドも後で追加します。

ヒーローサービスの共有

ダッシューボードがヒーロー情報を取得するためにHeroServiceを再利用します。
本章の序盤でHeroServiceHeroesComponentのプロバイダからAppModuleのプロバイダに移動しました。これにより、すべてのコンポーネントでシングルトンインスタンスのHeroServiceを使うことができます。

ヒーローの取得

app/dashboard.component.tsのインポート記載部分を以下の様にします。

app/dashboard.component.ts
import { Component, OnInit } from '@angular/core';

import { Hero } from './hero';
import { HeroService } from './hero.service';

また、DashboardComponentを以下の様に実装します。

app/dashboard.component.ts
export class DashboardComponent implements OnInit {

  heroes: Hero[] = [];

  constructor(private heroService: HeroService) { }

  ngOnInit(): void {
    this.heroService.getHeroes()
      .then(heroes => this.heroes = heroes.slice(1, 5));
  }

  gotoDetail(hero: Hero): void { /* まだ実装しない */}
}

HeroesComponentと同じ様なものですが、ヒーロー一覧から4人(2番目〜5番目)を取得しているところと、スタブgotoDetailを用意しているところが異なります。

ブラウザ上で4人のヒーローが表示されていることを確認できます。

ヒーロー詳細への画面遷移

HeroesComponentの下に表示していたヒーロー詳細ですが、現在3つの方法で詳細を表示する必要があります。

  • ダッシュボードから選択したヒーロー
  • ヒーロー一覧から選択したヒーロー
  • URLから直接入力されたもの

ヒーロー詳細へのルートを個別の場所に表示する様に追加します。

ヒーロー詳細へのroute

app/app.routing.tsHeroDetailComponentへのrouteを追加します。
新しいrouteはちょっと普通と違う方法をとります。HeroDetailComponentを使用しますが、呼び出し元のHeroesComponentにもDashboardComponentにも呼び出す方法を持っていません。現在HeroesComponentheroプロパティをセットする場合以下のようにバインドしています。

<my-hero-detail [hero]="selectedHero"></my-hero-detail>

これは明白にほかのシナリオでは動きません。

routeの調整

URLで、ヒーローのidを以下のように指定することで実現します。

/detail/11

detailは定数です。数字部分は変数で、このパラメータをヒーローのidとするルートが必要です。

Routeの設定

以下の様にrouteを定義します。

app/app.routing.ts
{
  path: 'detail/:id',
  component: HeroDetailComponent
},

:idのコロンはプレースホルダーでHeroDetailComponentへ遷移させるときヒーローのidとして使用します。

これで、アプリケーションのrouteの作成は完了です。
'Hero Detail'からのテンプレートへのリンクは作成しません。なぜなら、利用者は特定のヒーローへのナビゲーションはクリックしないからです。利用者は、ダッシュボードか、ヒーロー一覧をクリックします。
後の項で、ヒーローをクリックしたときの動きを取得します。

HeroDetailComponentの修正

HeroDetailComponentを書き換えます。

app/hero-detail.component.ts

import { Component, Input } from '@angular/core';
import { Hero } from './hero';
@Component({
  selector: 'my-hero-detail',
  template: `
    <div *ngIf="hero">
      <h2>{{hero.name}} details!</h2>
      <div>
        <label>id: </label>{{hero.id}}
      </div>
      <div>
        <label>name: </label>
        <input [(ngModel)]="hero.name" placeholder="name"/>
      </div>
    </div>
  `
})
export class HeroDetailComponent {
  @Input() hero: Hero;
}

テンプレート部分は変更しません。大きな変更点はどうやってヒーローを取得するかの部分です。もう、親のコンポーネントからヒーロー情報を取得することをやめ、新しいHeroDetailComponentActivatedRouteサービスのparamsobservable3からのidパラメータとHeroServiceidから取得したheroを使います。

まず、必要なものをインポートします

app/hero-detail.component.ts
// まだ、Inputは残しますが, 後で消します。
import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';

import { HeroService } from './hero.service';

コンストラクタに、privateなActivatedRouteサービスとHeroServiceサービスを定義します。

app/hero-detail.component.ts
constructor(
  private heroService: HeroService,
  private route: ActivatedRoute) {
}

HeroDetailComponentクラスにOnInitインタフェースを実装します。

export class HeroDetailComponent implements OnInit {

ngOnInitライフサイクルフック内で、ActivatedRouteサービスのparamsobservableからidパラメータを取り出しHeroServiceidからヒーローを取得します。

app/hero-detail.component.ts
ngOnInit(): void {
  this.route.params.forEach((params: Params) => {
    let id = +params['id'];
    this.heroService.getHero(id)
      .then(hero => this.hero = hero);
  });
}

forEachメソッドによりidを取得しています。forEachはrouteパラメータ配列を提供します。idは数値型(number)です。routeパラメータは通常、文字列型(string)です。Javascriptの(+)演算子により、routeパラメータを数値型に型変換しています。

ヒーローサービスクラスへgetHero()メソッドの追加

HeroServicegetHero()メソッドを追加します。getHero()メソッドでは、getHeroes()メソッドで取得したヒーロー一覧をidでフィルタします。

app/hero.service.ts
getHero(id: number): Promise<Hero> {
  return this.getHeroes()
             .then(heroes => heroes.find(hero => hero.id === id));
}

画面の戻りかた

複数の方法でHeroDetailComponentへ画面遷移できますが他への遷移はどうやるのでしょうか。
AppComponentから二つのリンクをクリックできます。また戻るボタンでも移動できます。第三の方法としてgoBackメソッドにより、ブラウザのヒストリースタックを使用して戻る方法4があります。

app/hero-detail.component.ts
goBack(): void {
  window.history.back();
}

アプリケーション外まで戻ることは、このチュートリアルでは許容していますが、実際のアプリでは抑止したいものです。CanDeactivate guard(英語)で方法が学べるはずです。
goBack()メソッドをコンポーネントのテンプレートに以下の様に追加し、バインドします。

<button (click)="goBack()">Back</button>

この追加したボタンと一緒に、ヒーロー詳細を表示するテンプレートを別ファイルapp/hero-detail.component.htmlに移します。

app/hero-detail.component.html
<div *ngIf="hero">
  <h2>{{hero.name}} details!</h2>
  <div>
    <label>id: </label>{{hero.id}}</div>
  <div>
    <label>name: </label>
    <input [(ngModel)]="hero.name" placeholder="name" />
  </div>
  <button (click)="goBack()">Back</button>
</div>

app/hero-detail.component.ts側はテンプレートからテンプレートURLに変更し、htmlファイルを読み込みます。
app/hero-detail.component.ts
templateUrl: 'app/hero-detail.component.html',

ブラウザを更新し、結果を確認してください。

ダッシュボードからヒーローを選択する

ダッシュボードからヒーローが選択されたとき、ヒーロー詳細を表示/編集可能にするべきです。ダッシュボードのテンプレートでは各ヒーローをクリックするとgotoDetail()メソッドを呼び出します。

app/dashboard.component.html
<div *ngFor="let hero of heroes" (click)="gotoDetail(hero)" class="col-1-4">

スタブ化していたgotoDetail()を今、実装します。

app/dashboard.component.ts
gotoDetail(hero: Hero): void {
  let link = ['/detail', hero.id];
  this.router.navigate(link);
}

gotoDetailメソッドは以下の2ステップにより画面遷移します。

  1. link配列にrouteパラメータを格納
  2. routernavigateメソッドにそれを渡す

画面遷移を行うために以前AppComponentのテンプレートにルーターリンクを実装しました。これらのリンクは一つの要素のみ持ちますが、今回のパラメータ配列は2つの要素を持ちます。目的のrouteのpathとヒーローのidをセットするroute parameterです。
この二つのアイテムpath:idは以前、app.routing.tsにパラメータ化しています。

app/app.routing.ts
{
  path: 'detail/:id',
  component: HeroDetailComponent
},

DashboardComponentはまだルーターを持っていません。インポートしてコンストラクタに登録します。

import { Router } from '@angular/router';
constructor(
  private router: Router,
  private heroService: HeroService) {
}

ブラウザを更新しダッシュボードからヒーローを選択すると、ヒーロー詳細に画面遷移します

HeroesComponentからのヒーローの選択

HeroesComponentにも同じ様に実装します。
このコンポーネントは現在、"マスター/ディティール"の形式(一覧と詳細)をとっています。

app/heroes.component.ts
template: `
  <h1>{{title}}</h1>
  <h2>My Heroes</h2>
  <ul class="heroes">
    <li *ngFor="let hero of heroes"
      [class.selected]="hero === selectedHero"
      (click)="onSelect(hero)">
      <span class="badge">{{hero.id}}</span> {{hero.name}}
    </li>
  </ul>
  <my-hero-detail [hero]="selectedHero"></my-hero-detail>
`,

<h1>タグを削除します。AppComponentからHeroesComponentに変更したときのごみです。次に<my-hero-detail>タグも削除します。もうここにはヒーロー詳細を表示しません。ヒーロー詳細は専用のページを用意し、ダッシュボードと同様、そのページへのrouteを作ります。
ここでちょっとひねった方法をとります。ヒーロー一覧からヒーローを選択しても、すぐにはヒーロー詳細を表示せずに、まずはミニ詳細を代わりに表示し、ミニ詳細に表示されたボタンをクリックすることで、フルの詳細ページに移動するようにします。

ミニ詳細の追加

<my-hero-detail>の場所に以下のhtmlの一部を追加します。

<div *ngIf="selectedHero">
  <h2>
    {{selectedHero.name | uppercase}} is my hero
  </h2>
  <button (click)="gotoDetail()">View Details</button>
</div>

ヒーロークリックにより、以下の様なものがヒーロー一覧の下に表示されます。

イメージ

パイプによる大文字変換

パイプ演算子(|)とuppercaseパイプにより表示文字を大文字に変更できます。

{{selectedHero.name | uppercase}} is my hero

パイプは「文字列のフォーマット」「通貨単位」「日付」などを表示するには、いい方法です。Angularにはいくつかのパイプと書き方が用意されています。
詳細は公式のPipes(英語)に記載されています。

コンポーネント外に内容を移す

まだミニ詳細の[View Details]ボタンを押したときにHeroDetailComponentへ画面遷移する機能に対応していませんが、ヒーロー一覧コンポーネントは非常に大きく、ほとんどはcss部分です。htmlとcssを他のファイルに移動します。
以下のマイグレーションを行います。

  1. htmlテンプレート部分をapp/heroes.component.htmlにカット&ペースト
  2. スタイル部分をheroes.component.cssにカット&ペースト
  3. @ComponentデコレータにtemplateUrlstyleUrlsプロパティを追加し、それぞれのファイルパスを記載

styleUrlsプロパティは、スタイルシートのファイルパスの配列です。必要に応じ複数のスタイルを適用できます。

app/heroes.component.ts
@Component({
  selector: 'my-heroes',
  templateUrl: 'app/heroes.component.html',
  styleUrls:  ['app/heroes.component.css']
})

ダッシュボードと同じ様にコンポーネントクラスを更新します

  1. routerをインポート
  2. コンストラクタでrouterをインジェクト
  3. router.navigateメソッドに呼ばれるgotoDetailメソッドの実装

app/heroes.component.tsは以下の様に修正されます。

app/heroes.component.ts
export class HeroesComponent implements OnInit {
  heroes: Hero[];
  selectedHero: Hero;

  constructor(
    private router: Router,
    private heroService: HeroService) { }

  getHeroes(): void {
    this.heroService.getHeroes().then(heroes => this.heroes = heroes);
  }

  ngOnInit(): void {
    this.getHeroes();
  }

  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }

  gotoDetail(): void {
    this.router.navigate(['/detail', this.selectedHero.id]);
  }
}

ブラウザを更新しクリックするとナビゲートできるようになります。ダッシュボードから、詳細へいったり、ダッシュボードに戻ったり、ヒーロー一覧から、ミニ詳細を通して、ヒーロー詳細を表示したり、ヒーロー一覧へ戻ったりができます。
本章の機能の実現は以上です。

アプリケーションのスタイル

機能は実現できましたが、見た目がイマイチです。cssを変更します。

ダッシュボードのスタイル

app/dashboard.component.ts@Componentデコレータに以下を追加します。

app/dashboard.component.ts
styleUrls: ['app/dashboard.component.css']

洒落たヒーロー詳細

DashboardComponentからheroプロパティ、@Inputデコレータとそれらをインポートしているインポート句を削除し、以下のcssファイルを追加します。

app/hero-detail.component.css
label {
  display: inline-block;
  width: 3em;
  margin: .5em 0;
  color: #607D8B;
  font-weight: bold;
}
input {
  height: 2em;
  font-size: 1em;
  padding-left: .4em;
}
button {
  margin-top: 20px;
  font-family: Arial;
  background-color: #eee;
  border: none;
  padding: 5px 10px;
  border-radius: 4px;
  cursor: pointer; cursor: hand;
}
button:hover {
  background-color: #cfd8dc;
}
button:disabled {
  background-color: #eee;
  color: #ccc; 
  cursor: auto;
}
app/dashboard.component.css
[class*='col-'] {
  float: left;
}
*, *:after, *:before {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
h3 {
  text-align: center; margin-bottom: 0;
}
[class*='col-'] {
  padding-right: 20px;
  padding-bottom: 20px;
}
[class*='col-']:last-of-type {
  padding-right: 0;
}
.grid {
  margin: 0;
}
.col-1-4 {
  width: 25%;
}
.module {
    padding: 20px;
    text-align: center;
    color: #eee;
    max-height: 120px;
    min-width: 120px;
    background-color: #607D8B;
    border-radius: 2px;
}
h4 {
  position: relative;
}
.module:hover {
  background-color: #EEE;
  cursor: pointer;
  color: #607d8b;
}
.grid-pad {
  padding: 10px 0;
}
.grid-pad > [class*='col-']:last-of-type {
  padding-right: 20px;
}
@media (max-width: 600px) {
    .module {
      font-size: 10px;
      max-height: 75px; }
}
@media (max-width: 1024px) {
    .grid {
      margin: 0;
    }
    .module {
      min-width: 60px;
    }
}

ナビゲーションリンクのスタイル

AppComponent<nav>タグにもスタイルを適用します。
app/app.component.cssを追加します。

app/app.component.css
h1 {
  font-size: 1.2em;
  color: #999;
  margin-bottom: 0;
}
h2 {
  font-size: 2em;
  margin-top: 0;
  padding-top: 0;
}
nav a {
  padding: 5px 10px;
  text-decoration: none;
  margin-top: 10px;
  display: inline-block;
  background-color: #eee;
  border-radius: 4px;
}
nav a:visited, a:link {
  color: #607D8B;
}
nav a:hover {
  color: #039be5;
  background-color: #CFD8DC;
}
nav a.active {
  color: #039be5;
}

routerLinkActiveディレクティブ

Angularでは、routerLinkActiveディレクティブが提供されています。htmlナビゲーション要素に追加することでrouteがアクティブなrouteになったときのcssが定義できます。

app/app.component.ts
template: `
  <h1>{{title}}</h1>
  <nav>
    <a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
    <a routerLink="/heroes" routerLinkActive="active">Heroes</a>
  </nav>
  <router-outlet></router-outlet>
`,

AppComponentstylesプロパティからstyleUrlsプロパティに変更します。

app/app.component.ts
styleUrls: ['app/app.component.css'],

グローバルアプリケーションスタイル

アプリケーションの外へスタイルを作成できます。
ベーシックなスタイルをアプリに適用する場合、クイックスタートスタイルシートの追加で紹介した方法を用います。

styles.css_抜粋
/* Master Styles */
h1 {
  color: #369;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 250%;
}
h2, h3 {
  color: #444;
  font-family: Arial, Helvetica, sans-serif;
  font-weight: lighter;
}
body {
  margin: 2em;
}
body, input[text], button {
  color: #888;
  font-family: Cambria, Georgia;
}
/* . . . */
/* everywhere else */
* {
  font-family: Arial, Helvetica, sans-serif;
}

スタイルシートの全文は公式のこちらです。
必要に応じてindex.htmlをスタイルシートを参照する様に変更します。

index.html
<link rel="stylesheet" href="styles.css">

現在の見た目は以下の様になります。

イメージ


本章は以上です。まだこのアプリでは、リモートデータアクセスを実装していません。次章ではモックデータを置換し、httpを使ってデータを取得する方法の説明を行います。



  1. ダッシュボードとは「複数の情報源からデータを集め、概要をまとめて一覧表示する機能や画面、ソフトウェアのこと。」らしいです。IT用語辞典 @e-words 

  2. ルートと書くと、rootなのかrouteなのか分かりづらいのでアルファベットで記載します。root=根、route=道です。一応。 

  3. observableについては次章で説明をします。 

  4. Javascriptのリファレンスを確認してください。window.history.back()メソッド@アルファシス 

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11