前回、TOPページをいい感じにしました。
今回は他の画面を作って、画面遷移できるようにしてみます。
ブログ一覧/詳細ページの追加
ブログの画面は以下のようなURLになるように作りたいと思います。
一覧:/blogs
詳細:/blogs/:id
サービスの作成
画面を作る前に表示用のデータを取得用サービスを作ります。
ただ、今回はまだFirebaseからデータを取得しないので、ダミーデータを返すようにします。
src/app/blogs/blogs.service.ts
import { Injectable } from '@angular/core';
import { Blog } from 'src/types/blog.interface';
@Injectable()
export class BlogsService {
  readonly testData: Blog[] = [
    { id: 1, title: 'ブログ1', contents: 'ブログ1のコンテンツ', tags: ['タグ1'], created_at: '2020/01/01 00:00:00' },
    { id: 2, title: 'ブログ2', contents: 'ブログ2のコンテンツ', tags: ['タグ2', 'タグ3'], created_at: '2020/01/02 00:00:00' },
  ];
  constructor() { }
  async getBlogs() {
    return this.testData;
  }
  async getBlog(id: number) {
    return this.testData.find(data => data.id === id);
  }
}
- getBlogs:ブログ一覧を返却
 - getBlog:指定したIDを持つブログを返却
 
※後々ちゃんとFirebaseから取得するようにします(それを想定してasyncつけてます)
ちなみにBlog型は以下のように定義してます
src/types/blog.interface.ts
export interface Blog {
    id: number;
    title: string;
    contents: string;
    tags: string[];
    created_at: string;
}
ブログ一覧画面
- 一覧表示用のコンポーネントを作成
- コンストラクタでサービスをDI
 - getBlogsで取得した一覧情報をblogsに設定
 
 
src/app/blogs/list/list.component.ts
import { Component, OnInit } from '@angular/core';
import { BlogsService } from '../blogs.service';
import { Blog } from 'src/types/blog.interface';
@Component({
  selector: 'app-blogs-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.scss']
})
export class BlogsListComponent implements OnInit {
  blogs: Blog[];
  constructor(private blogsService: BlogsService) { }
  async ngOnInit() {
    this.blogs = await this.blogsService.getBlogs();
  }
}
- HTMLを作成
- コンポーネント側でblogsに一覧を設定するため、項目数分だけngForでループを回す
 - 作成日はパイプを使って
yyyy/MM/dd形式で出力 - ブログタイトルはリンクを表示(表示しているブログIDをリンク先に指定)
 
 
src/app/blogs/list/list.component.html
<div class="blog-list">
    <h1 class="title">ブログ一覧</h1>
    <ul *ngIf="blogs">
        <li *ngFor="let blog of blogs">
            <span class="create-date">{{blog.created_at | date: 'yyyy/MM/dd'}}</span>
            <a class="blog-title" routerLink="/blogs/{{blog.id}}">{{blog.title}}</a>
        </li>
    </ul>
</div>
詳細画面の追加
- コンポーネントの作成
- コンストラクタでサービスをDI
 - URLのパスからブログIDを取得するためActivatedRouteをDI
 - 
this.activatedRoute.snapshot.params.idでパスからIDを取得 - getBlogでIDで指定された内容を取得
- params.idはstringのため、Number(id)で数値型に変換
 
 
 
src/app/blogs/detail/detail.component.ts
import { Component, OnInit } from '@angular/core';
import { BlogsService } from '../blogs.service';
import { ActivatedRoute } from '@angular/router';
import { Blog } from 'src/types/blog.interface';
@Component({
  selector: 'app-blogs-detail',
  templateUrl: './detail.component.html',
  styleUrls: ['./detail.component.scss']
})
export class BlogsDetailComponent implements OnInit {
  detail: Blog;
  constructor(private blogsService: BlogsService, private activatedRoute: ActivatedRoute) { }
  async ngOnInit() {
    const id = this.activatedRoute.snapshot.params.id;
    if (id) {
      this.detail = await this.blogsService.getBlog(Number(id));
    }
  }
}
src/app/blogs/detail/detail.component.html
<div *ngIf="detail" class="blog-detail">
    <h1>{{detail.title}}</h1>
    <span>{{detail.contents}}</span>
</div>
/blogsルーティング設定
遅延ロードによるモジュール読み込みを行えるようなモジュール構成にしていきます
- 
/blogsのサブパスでモジュールを切るため、BlogsRoutingModuleを作成- 詳細ページは
:idで動的にブログIDを指定できるようにします 
 - 詳細ページは
 
src/app/blogs/blogs-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { BlogsListComponent } from './list/list.component';
import { BlogsDetailComponent } from './detail/detail.component';
const routes: Routes = [
  { path: '', component: BlogsListComponent },
  { path: ':id', component: BlogsDetailComponent },
];
@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class BlogsRoutingModule { }
- BlogsModuleを作成し、一覧/詳細コンポーネントの宣言、BlogsRoutingModuleのインポートを行います
 
src/app/blogs/blogs.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BlogsRoutingModule } from './blogs-routing.module';
import { BlogsService } from './blogs.service';
import { BlogsListComponent } from './list/list.component';
import { BlogsDetailComponent } from './detail/detail.component';
@NgModule({
  declarations: [
    BlogsListComponent,
    BlogsDetailComponent,
  ],
  imports: [
    CommonModule,
    BlogsRoutingModule,
  ],
  providers: [
    BlogsService,
  ]
})
export class BlogsModule { }
- 
app-routing.module.tsでloadChildrenを用いてBlogsModuleを遅延ロードするようにします- こうすることで、/blogsにアクセスしたときにはじめてBlogsModuleが読み込まれるようになるため、その分初期表示時の負荷が軽減できます。
 - また、Moduleのインポートが不要のため、blogsディレクトリ内でコードが完結できるという点もGoodだと思います。
 
 
src/app/app-routing.module.ts
・・・
const routes: Routes = [
  ・・・
  { path: 'blogs', loadChildren: () => import('./blogs/blogs.module').then(m => m.BlogsModule) },
  ・・・
];
・・・
こんな感じになりました
ブログ一覧
/blogs
ブログ詳細
/blogs/1
自己紹介ページ
ちなみに、自己紹介ページも雑に作りました。
まとめ
だいぶブログの体をなしてきた気がしてきました!
次回はFirestoreからデータをとってきて、画面に出力するところをやってみようと思います。
(追記)
Angular9とFirebaseでブログを作ってみる5(Firestoreからデータを取得して表示してみる)


