LoginSignup
3
0

More than 5 years have passed since last update.

IonicでContentfulのコンテンツを読み込んで表示してみるぞ!

Last updated at Posted at 2018-12-22

はじめに

この記事は 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を編集していきましょう。

src/app/contentful.service.ts
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.tsContentfulServiceを追加していきます。

/src/app/app.module.ts
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のデータを取得してきます。

src/app/home/home.page.ts
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>の中身を入れ替える感じです。

src/app/home/home.page.html
<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>

画像がでかいのでスタイルもちょっと設定します。

/src/app/home/home.page.scss
ion-card {
  margin-bottom: 20px;
}

ion-img {
  width: 200px;
  margin: auto;
}

投稿の詳細ページも作ってみるよ

これでとりあえずContentfulが持っているデータを一覧表示することができました。嬉しいです。
せっかくなので商品の個別ページも作ってみましょう。これもIonic CLIで作りますよ。

$ ionic g page details

はいできました。
そしたら一覧の各商品(記事)のIDを使ってルーティングを設定していきます。
このあとserviceで投稿のidを使って記事単品を取得するのでidを使ってリンクするようにします。

src/app/app-routing.module.ts
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 { }

んでは、一覧の表示にも詳細へのリンクを追加しましょう。

src/app/home/home.page.ts
<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件だけ取得する処理をサービスに追加していきます。

/src/app/contentful.service.ts
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);
+    }
}

今追加した関数を使って詳細ページの処理を追加します。

/src/app/details/details.page.ts
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テンプレートも修正しますよ

src/app/details/details.page.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>

こちらもスタイルを設定します。

src/app/details/details.page.scss
ion-img {
  width: 200px;
  margin: auto;
}

まとめ

こんな感じで簡単ではありますがContentfulのデータを読み込んで表示することができました。
デモはこちらに用意してありますので、試すのめんどい人は見てみてください。

参考

Use Contentful in an Angular project

3
0
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
3
0