はじめに
この記事は Ionic Advent Calendar 2018 の第21日目の記事です!
今回は、Ionic4のプロジェクトでContentfulの記事を読み込んで、ブログっぽいものを表示してみたいと思います。
大筋はContentfulのサイトにあるUse Contentful in an Angular projectというチュートリアルをなぞって進めていきます。
Ionicのインストール
とりあえずここはNode.jsがインストールされていることが前提です。
もしインストールされていなかったら、公式サイトからインストールしたり、nodenvなどのバージョン管理ツールを使ってインストールしてください。
node
とかnpm
コマンドが使えるようになったら
$ npm install -g ionic
でIonic CLIをインストールしましょう。
できましたね。
$ ionic
してみてコマンドが動けばインストールできてます。おめでとうございます。
Ionicのプロジェクトを作るよ
さっそく作ります
$ ionic start ionic-contentful blank --type=angular
これでionic-contentful
という名前のIonicプロジェクトが作成できます。ちなみに--type=angular
と書くことで今のところIonic4でプロジェクトが作成できます。そしてblank
はテンプレートの名前です。1ページのシンプルなテンプレートになってます。
? Install the free Ionic Appflow SDK and connect your app? (Y/n)
はn
と入力してください。
プロジェクトの作成が完了したらionic-contentful
ディレクトリに移動してください。
とりあえず動かす
んではさっそく動かします。
$ ionic serve
で、開発用のサーバーが立ち上がってブラウザが開きます。ちなみにionic s
でも同じ効果があります。割と最近省略できるようになりました。
このコマンドプロジェクトないのファイルを監視していますので、tsやらscssが変更されるとブラウザ側もリロードされます。
んでブラウザで開いたのが、blank
テンプレートそのままのIonicアプリです。こっから色々いじっていきます。
Contentful用のサービスをつくる
Ionic CLIでContentful用のサービスをつくりますよ。
$ ionic g service contentful
これでsrc/app/contentful.service.ts
が生成されました。
これを編集する前にcontentfulのSDKをインストールします。
$ npm i --save contentful
んではserviceを編集していきましょう。
import { Injectable } from '@angular/core';
+import { createClient, Entry } from 'contentful';
+const CONFIG = {
// 本当はここには自分のアカウントの情報を記載しますが、今回は公開されているテストアカウントを使用します
+ space: 'wl1z0pal05vy',
+ accessToken: '0e3ec801b5af550c8a1257e8623b1c77ac9b3d8fcfc1b2b7494e3cb77878f92a',
+ contentTypeIds: {
+ product: '2PqfXUJwE8qSYKuM0U6w8M'
+ }
+};
@Injectable({
providedIn: 'root'
})
export class ContentfulService {
+ private cdaClient = createClient({
+ space: CONFIG.space,
+ accessToken: CONFIG.accessToken
+ });
constructor() { }
+ getProducts(query?: object): Promise<Entry<any>[]> {
+ return this.cdaClient.getEntries(Object.assign({
+ content_type: CONFIG.contentTypeIds.product
+ }, query))
+ .then(res => res.items);
+ }
}
続いてapp.module.ts
にContentfulService
を追加していきます。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
+ import { ContentfulService } from './contentful.service';
@NgModule({
declarations: [AppComponent],
entryComponents: [],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
providers: [
StatusBar,
SplashScreen,
+ ContentfulService,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
##投稿の一覧ページを作るよ
ほいだら次は、HomePageコンポーネントでContentfulのコンテンツを読み込めるようにhome.page.ts
を修正していきます。
HomePageコンポーネントが初期化されるときに、Contentfulからproductsのデータを取得してきます。
import { Component, OnInit } from '@angular/core'; // OnInitを追加
+ import { ContentfulService } from '../contentful.service';
+ import { Entry } from 'contentful';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
// OnInitをimplementsしましょ
export class HomePage implements OnInit {
+ private products: Entry<any>[] = [];
+ constructor(private contentfulService: ContentfulService) {}
+ ngOnInit() {
+ this.contentfulService.getProducts()
+ .then(products => this.products = products);
+ }
}
htmlテンプレート側でも表示してみましょ。<ion-content>
の中身を入れ替える感じです。
<ion-header>
<ion-toolbar>
<ion-title>
Contentful Data
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-card *ngFor="let product of products">
<ion-img [src]=product.fields.image[0].fields.file.url"></ion-img>
<ion-card-header>
<ion-card-title>{{ product.fields.productName }}</ion-card-title>
</ion-card-header>
</ion-card>
</ion-content>
画像がでかいのでスタイルもちょっと設定します。
ion-card {
margin-bottom: 20px;
}
ion-img {
width: 200px;
margin: auto;
}
##投稿の詳細ページも作ってみるよ
これでとりあえずContentfulが持っているデータを一覧表示することができました。嬉しいです。
せっかくなので商品の個別ページも作ってみましょう。これもIonic CLIで作りますよ。
$ ionic g page details
はいできました。
そしたら一覧の各商品(記事)のIDを使ってルーティングを設定していきます。
このあとserviceで投稿のid
を使って記事単品を取得するのでid
を使ってリンクするようにします。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', loadChildren: './home/home.module#HomePageModule' },
+ { path: 'details/:id', loadChildren: './details/details.module#DetailsPageModule' },
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
んでは、一覧の表示にも詳細へのリンクを追加しましょう。
<ion-header>
<ion-toolbar>
<ion-title>
Contentfulのデータをよみこむぞ
</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-card *ngFor="let product of products">
<ion-img [src]=product.fields.image[0].fields.file.url"></ion-img>
<ion-card-header>
<ion-card-title>{{ product.fields.productName }}</ion-card-title>
</ion-card-header>
+ <ion-card-content>
+ <ion-button routerLink="/details/{{ product.sys.id }}">Details</ion-button>
+ </ion-card-content>
</ion-card>
</ion-content>
次は詳細ページで使うための、投稿データを1件だけ取得する処理をサービスに追加していきます。
import { Injectable } from '@angular/core';
import { createClient, Entry } from 'contentful';
const CONFIG = {
space: 'wl1z0pal05vy',
accessToken: '0e3ec801b5af550c8a1257e8623b1c77ac9b3d8fcfc1b2b7494e3cb77878f92a',
contentTypeIds: {
product: '2PqfXUJwE8qSYKuM0U6w8M'
}
};
@Injectable({
providedIn: 'root'
})
export class ContentfulService {
private cdaClient = createClient({
space: CONFIG.space,
accessToken: CONFIG.accessToken
});
constructor() { }
getProducts(query?: object): Promise<Entry<any>[]> {
return this.cdaClient.getEntries(Object.assign({
content_type: CONFIG.contentTypeIds.product
}, query))
.then(res => res.items);
}
+ getProduct(id: any): Promise<Entry<any>> {
+ return this.cdaClient.getEntry(id)
+ .then(res => res);
+ }
}
今追加した関数を使って詳細ページの処理を追加します。
import { Component, OnInit } from '@angular/core';
+ import { Router, ActivatedRoute, ParamMap } from '@angular/router';
+ import { ContentfulService } from '../contentful.service';
+ import { Entry } from 'contentful';
@Component({
selector: 'app-details',
templateUrl: './details.page.html',
styleUrls: ['./details.page.scss'],
})
export class DetailsPage implements OnInit {
+ private product: Entry<any> = null;
+ private id: string = null;
constructor(
+ private contentfulService: ContentfulService,
+ private route: ActivatedRoute,
+ private router: Router
) {}
ngOnInit() {
+ this.route.paramMap.subscribe((params: ParamMap) => {
+ this.id = params.get('id');
+ this.getProduct();
+ });
}
+ getProduct() {
+ this.contentfulService.getProduct(this.id)
+ .then(product => {
+ this.product = product;
+ });
+ }
+ goBack() {
+ this.router.navigate(['/']);
+ }
}
htmlテンプレートも修正しますよ
<ion-header>
<ion-toolbar>
<ion-title>details</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
+ <ion-card *ngIf="product">
+ <ion-img [src]=product.fields.image[0].fields.file.url"></ion-img>
+ <ion-card-header>
+ <ion-card-title>{{ product.fields.productName }}</ion-card-title>
+ </ion-card-header>
+ <ion-card-content>
+ <p>Price: ${{ product.fields.price }}</p>
+ <p>Size: {{ product.fields.sizetypecolor }}</p>
+ <p>{{ product.fields.productDescription }}</p>
+ </ion-card-content>
+ </ion-card>
+ <ion-button target="_blank" (click)="goBack()">Back</ion-button>
</ion-content>
こちらもスタイルを設定します。
ion-img {
width: 200px;
margin: auto;
}
##まとめ
こんな感じで簡単ではありますがContentfulのデータを読み込んで表示することができました。
デモはこちらに用意してありますので、試すのめんどい人は見てみてください。
###参考