Headless Oracle Content Managementを使用したOracle JETでのブログの作成

イントロダクション

Oracle JavaScript Extension Toolkit (Oracle JET)は、開発者が魅力的なユーザー・インタフェースを構築できるように設計された、モジュラ式でオープンソースのJavaScript開発ツールキットです。業界標準と広く普及しているオープンソース・フレームワークであるPreactに基づいており、類似したAPIを使用してReactにするかわりに軽量です。Oracle JETは、開発者がより優れたアプリケーションをより迅速に構築できるように、高度な機能とサービスを追加します。

このチュートリアルでは、仮想DOM (VDOM)アーキテクチャでOracle JETにOracle Content ManagementをヘッドレスCMSとして、およびコンテンツ配信用のソフトウェア開発キット(SDK)を利用して単純なブログを構築します。このOracle JETサンプルは、GitHubにあります。

このチュートリアルは、次の3つのステップで構成されています。

  1. Oracle Content Managementの準備
  2. Oracle JETでブログを構築
  3. アプリケーションのデプロイメントの準備

前提条件

このチュートリアルに進む前に、最初に次の情報を読むことをお薦めします。

このチュートリアルに従うには、次のものが必要です。

構築内容

私たちのブログは、訪問者がトピックにまとめられたブログ記事を探索できる2ページのサイトで構成されています。最初のページであるホーム・ページは、ブランディング(会社名とロゴ)、一部のリンクおよびトピックでソートされた記事のリストで構成されます。2ページ目の記事ページは、ブログの著者の情報を含むブログ記事を表示します。

これは、ホーム・ページがこのチュートリアルの最後に表示される内容です:

この図は、使用可能なトピックおよび記事のリストが表示されたCafe Supremoデモ・サイトのホーム・ページを示しています。

記事ページはこのチュートリアルの最後に表示されます。

この図は、「Create Beautiful Latte Art!」という記事ページを示しています。

続行するには、Oracle Content Managementへのアクティブなサブスクリプションがあり、コンテンツ管理者ロールでログインする必要があります。

タスク1: Oracle Content Managementの準備

Oracle Content Managementインスタンスがまだない場合は、クイック・スタートを参照して、Oracle Cloudに登録する方法、Oracle Content Managementインスタンスのプロビジョニング方法、およびOracle Content ManagementをヘッドレスCMSとして構成する方法を学習します。

このチュートリアルでは、2つの方法のいずれかでコンテンツ・モデルを作成する必要があります。使用可能なダウンロード可能なアセット・パックがあり、コンテンツ・タイプおよび関連コンテンツで空のリポジトリを埋めるか、独自のコンテンツ・モデルおよびコンテンツを作成できます。

Oracle Content Managementを準備するには:

  1. チャネルおよびアセット・リポジトリを作成します
  2. 次の2つの方法のいずれかを使用して、コンテンツ・モデルを作成します

チャネルおよびアセット・リポジトリの作成

コンテンツを公開するには、まずOracle Content Managementでチャネルおよびアセット・リポジトリを作成する必要があります。

Oracle Content Managementでチャネルおよびアセット・リポジトリを作成するには:

  1. 管理者としてOracle Content Management Webインタフェースにログインします。

  2. 左側のナビゲーション・メニューで「コンテンツ」を選択し、ページ・ヘッダーの選択リストから「チャネルの公開」を選択します。

    この図は、「コンテンツ」ページ・ヘッダーのドロップダウン・メニューで選択された「公開チャネル」オプションを示しています。

  3. 右上隅で、「作成」をクリックして新しいチャネルを作成します。このチュートリアルの目的でチャネルにOCEGettingStartedChannelという名前を付け、アクセスをパブリックにしておきます。「保存」をクリックしてチャネルを作成します。

    この図は、チャネル名フィールドに'OCEGettingStartedChannel'がある公開チャネル定義パネルを示しています。

  4. 左側のナビゲーション・メニューで「コンテンツ」を選択し、ページ・ヘッダーの選択リストから「リポジトリ」を選択します。

    この図は、「コンテンツ」ページ・ヘッダーのドロップダウン・メニューで選択された「リポジトリ」オプションを示しています。

  5. 右上隅で、「作成」をクリックして新しいアセット・リポジトリを作成します。このチュートリアルでは、アセット・リポジトリにOCEGettingStartedRepositoryという名前を付けます。

    この図は、リポジトリ名フィールドにOCEGettingStartedRepositoryが付いたリポジトリ定義パネルを示しています。

  6. 「公開チャネル」フィールドで、OCEGettingStartedChannelチャネルを選択して、OCEGettingStartedRepositoryリポジトリ内のコンテンツをOCEGettingStartedChannelチャネルに公開できることをOracle Content Managementに示します。終了したら、「保存」をクリックします。

    この図は、「公開チャネル」フィールドにOCEGettingStartedChannelが付いたリポジトリ定義パネルを示しています。

コンテンツ・モデルの作成

次のステップでは、コンテンツ・モデルを作成します。次の2つの方法のいずれかを使用できます。

Oracle Content Managementサンプル資産パックのインポート

このチュートリアルに必要なすべてのコンテンツ・タイプおよびアセットを含む、事前構成されたOracle Content Managementサンプル・アセット・パックをダウンロードできます。必要に応じて、サンプル・アセット・パックをダウンロードするのではなく、独自のコンテンツ・モデルを作成することもできます。

このチュートリアルで使用するコンテンツのコピーは、Oracle Content Management Samples Asset Packからアップロードできます。これにより、コンテンツ・タイプを試して内容を変更できます。Oracle Content Management Samples Asset Packをインポートする場合は、アセット・パック・アーカイブOCESamplesAssetPack.zipをダウンロードし、選択したディレクトリに抽出できます。

  1. Oracle Content Managementのダウンロード・ページからOracle Content Management Samples Asset Pack (OCESamplesAssetPack.zip)をダウンロードします。ダウンロードしたzipファイルをコンピュータ上の場所に抽出します。抽出後、この場所にはOCEGettingStarted_data.zipというファイルが含まれます。

  2. 管理者としてOracle Content Management Webインタフェースにログインします。

  3. 左側のナビゲーション・メニューで「コンテンツ」を選択し、ページ・ヘッダーの選択リストから「リポジトリ」を選択します。次に、OCEGettingStartedRepositoryを選択し、上部アクション・バーの「コンテンツのインポート」ボタンをクリックします。

    この図は、OCEGettingStartedRepositoryアイテムが選択された「リポジトリ」ページを示しています。

  4. ローカル・コンピュータから「ドキュメント」フォルダにOCEGettingStarted_data.zipをアップロードします。

    この図は、OCEGettingStarted_data.zipファイルのアップロード確認画面を示しています。

  5. アップロードしたら、OCEGettingStarted_data.zipを選択し、「OK」をクリックしてコンテンツをアセット・リポジトリにインポートします。

    このイメージは、「OK」ボタンが有効になっている選択したOCEGettingStarted_data.zipファイルを示しています。

  6. コンテンツが正常にインポートされたら、「アセット」ページに移動し、OCEGettingStartedRepositoryリポジトリを開きます。すべての関連イメージおよびコンテンツ項目がアセット・リポジトリに追加されたことがわかります。

    このイメージは、OCEGettingStartedRepositoryリポジトリと、インポートされたばかりのすべてのアセットを示しています。

  7. 左上の「すべて選択」をクリックし、「公開」をクリックして、以前に作成した公開チャネル(OCEGettingStartedChannel)にインポートされたすべてのアセットを追加します。

    この図は、OCEGettingStartedRepositoryリポジトリを示しています。すべてのアセットが選択され、アクション・バーの「公開」オプションが表示されています。

  8. 公開する前に、すべての資産を検証する必要があります。最初に、選択したチャネルとしてOCEGettingStartedChannelを追加し、「検証」ボタンをクリックします。

    この図は、「検証結果」ページを示しています。このページには、「チャネル」フィールドにOCEGettingStartedChannelチャネルが追加され、検証されるすべてのアセット、および「検証」ボタンが有効になっています。

  9. アセットの検証後、右上隅にある「公開」ボタンをクリックして、選択したチャネルにすべてのアセットを公開できます。

    この図は、「検証結果」ページを示しています。「チャネル」フィールドにOCEGettingStartedChannelチャネルが追加され、すべてのアセットが検証され、「公開」ボタンが有効になっています。

完了すると、すべてのアセットが公開された「アセット」ページが表示されます。(アセット名の上にあるアイコンで確認できます。)

この図は、すべての資産が公開された「資産」ページを示しています。

Oracle Content Management Samples Asset Packをインポートした後、Oracle JETでブログの構築を開始できます。

独自のコンテンツ・モデルの作成

Oracle Content Management Samples Asset Packをインポートするかわりに、独自のコンテンツ・モデルを作成することもできます。

このチュートリアルでは、「OCEGettingStartedHomePage」というコンテンツ・タイプを使用して、ブログのホーム・ページを作成します。このホーム・ページは、ブランディング(会社名とロゴ)、リンク用のURLおよびページに含まれるブログ・トピックのリストで構成されます。

コンテンツ・モデルのコンテンツ・タイプを作成するには:

  1. 管理者としてOracle Content Management Webインタフェースにログインします。
  2. 左側のナビゲーション・メニューで「コンテンツ」を選択し、ページ・ヘッダーの選択リストから「アセット・タイプ」を選択します。
  3. 右上隅にある「作成」をクリックします。
  4. コンテンツ・タイプ(デジタル資産タイプではない)の作成を選択します。すべての必須コンテンツ・タイプに対してこの手順を繰り返します。

この図は、Oracle Content Management Webインタフェースの「アセット・タイプの作成」ダイアログを示しています。

4つのコンテンツ・タイプを作成し、それぞれに独自のフィールド・セットを作成します。

最初のコンテンツ・タイプOCEGettingStartedHomePageには、次のフィールドが必要です。

表示名 フィールド・タイプ 必須 マシン名
会社名 単一値テキスト・フィールド X company_name
会社ロゴ 単一値テキスト・フィールド X company_logo
トピック 複数値参照フィールド X トピック
担当者URL 単一値テキスト・フィールド X contact_url
URLについて 単一値テキスト・フィールド X about_url

OCEGettingStartedHomePageコンテンツ・タイプ定義は次のようになります。

この図は、コンテンツ・タイプOCEGettingStartedHomePageの定義を示しています。これには、「会社名」、「会社ロゴ」、「トピック」、「連絡先URL」および「URLについて」のデータ・フィールドが含まれます。

2番目のコンテンツタイプ OCEGettingStartedTopicには、次のフィールドが必要です。

表示名 フィールド・タイプ 必須 マシン名
サムネイル 単一値イメージ・フィールド X サムネイル

OCEGettingStartedTopicコンテンツ・タイプは次のようになります。

この図は、コンテンツ・タイプOCEGettingStartedTopicの定義を示しています。このデータ・フィールド「サムネイル」が含まれます。

3番目のコンテンツ・タイプOCEGettingStartedAuthorには、次のフィールドが必要です。

表示名 フィールド・タイプ 必須 マシン名
アバター 単一値イメージ・フィールド X アバター

OCEGettingStartedAuthorコンテンツ・タイプは次のようになります。

この図は、コンテンツ・タイプOCEGettingStartedAuthorの定義を示しています。データ・フィールドAvatarが含まれます。

4番目および最後のコンテンツ・タイプOCEGettingStartedArticleには、次のフィールドが必要です。

表示名 フィールド・タイプ 必須 マシン名
公開日 単一値日付フィールド X published_name
作成者 単一値参照フィールド X 文書作成
イメージ 単一値イメージ・フィールド X イメージ
イメージ・キャプション 単一値テキスト・フィールド X image_caption
記事の内容 単一値ラージ・テキスト・フィールド X article_content
トピック 単一値参照フィールド X topic

OCEGettingStartedArticleコンテンツ・タイプは次のようになります。

この図は、コンテンツ・タイプOCEGettingStartedArticlePageの定義を示しています。これには、「公開日」、「作成者」、「イメージ」、「イメージキャプション」、「記事コンテンツ」および「トピック」のデータ・フィールドが含まれます。

コンテンツ・タイプを作成した後は、以前に作成したリポジトリOCEGettingStartedRepositoryに次のコンテンツ・タイプを追加できます。

  1. 管理者としてOracle Content Management Webインタフェースにログインします。
  2. OCEGettingStartedRepositoryに移動します。
  3. リポジトリを編集し、「アセット・タイプ」で、新しく作成された4つのコンテンツ・タイプをすべて指定します。
  4. 「保存」ボタンをクリックして変更を保存します。

この図は、Oracle Content Managementの「リポジトリの編集」ページを示しており、OCEGettingStartedRepositoryリポジトリに関連付けられた4つの新規作成されたコンテンツ・タイプを示しています。

コンテンツ・タイプをリポジトリに追加した後、「アセット」ページでOCEGettingStartedRepositoryリポジトリを開き、すべてのコンテンツ・タイプのコンテンツ項目の作成を開始できます。

このイメージは、Oracle Content Management Webインタフェースの「資産」ページのコンテンツ項目を示しています。左側に、コレクション、チャネル、言語、タイプ、コンテンツ項目の選択およびステータスのオプションがあります。

タスク2: Oracle JETでのブログの作成

Oracle JETアプリケーションでOracle Content Managementコンテンツを消費するには、GitHubでオープンソース・リポジトリとして使用できるOracle JETブログ・サンプルを使用できます。

ノート: Oracle JETサンプルの使用はオプションであり、このチュートリアルで使用してすぐに開始することに注意してください。Oracle JETチームによるスタータ・テンプレートの1つなど、独自のOracle JETアプリケーションを作成することもできます。

Oracle JETでブログを構築するには:

  1. サンプル・リポジトリのクローニングと依存関係のインストール
  2. Oracle JET CLIをインストールし、Oracle Spectra UIコンポーネントを追加します
  3. Oracle JETアプリケーションの構成
  4. Oracle Content Management Content SDKの操作
  5. Content SDKを使用したコンテンツのフェッチ

サンプルリポジトリのクローニングと依存関係のインストール

Oracle JETブログ・サンプルは、GitHubでオープン・ソース・リポジトリとして使用できます。

まず、GitHubからローカル・コンピュータにサンプルをクローニングし、ディレクトリをリポジトリ・ルートに変更する必要があります。

git clone https://github.com/oracle-samples/oce-jet-blog-sample.git
cd oce-jet-blog-sample

コード・ベースができたので、アプリケーションの依存関係をダウンロードする必要があります。rootディレクトリから次のコマンドを実行します。

npm install

Oracle JET CLIのインストールおよびOracle Spectra UIコンポーネントの追加

次に、Oracle JETコマンドを実行するには、Oracle JET CLIをインストールする必要があります。package.jsonファイルに依存性がインストールされているため、必要なアクションはありません。Oracle JET CLIに問題がある場合は、Oracle JET CLIドキュメントの手順に従います。Oracle JET CLIはGitHubにあります。カスタム・コンポーネントをインストールし、アプリケーションを提供するために、Oracle JET CLIが必要になります。

この後、このOracle JETサンプルで使用するカスタム・コンポーネントをインストールする必要があります。これらのコンポーネントには、使用する独自のCSSが関連付けられています。Spectra UIコンポーネントは次のとおりです。

これらのコンポーネントを追加するには、参照としてExchange- urlを構成し、カスタムOracle JETコンポーネント(USインスタンス用)をインストールする必要があります:

ojet configure --exchange-url=https://devinstance4wd8us2-wd4devcs8us2.uscom-central-1.oraclecloud.com/profile/devinstance4wd8us2-wd4devcs8us2/s/devinstance4wd8us2-wd4devcs8us2_compcatalog_3461/compcatalog/0.2.0
ojet add component oj-sp-welcome-page oj-sp-image-card oj-sp-item-overview-page oj-sp-item-overview --username=comp.catalog --password=bXwphh6RMFjn#g

コンポーネント交換URLへの接続の詳細は、Visual Builderのドキュメントを参照してください。

Oracle JETアプリケーションの構成

このOracle JETブログ・サンプルでは、Oracle Content Management Content SDK (および他のリクエスト)が正しいインスタンスURLおよびAPIバージョンを正しいチャネル・トークンでターゲットにできるように、いくつかの情報を構成する必要があります。これらの値は、新しい配信クライアントをインスタンス化するために、src/scripts/server- config- utils.jsで使用されます。

テキスト・エディタでsrc/config/content.jsonを開きます。次の情報が表示されます。

{
  "serverUrl": "https://samples.mycontentdemo.com",
  "apiVersion": "v1.1",
  "channelToken": "47c9fb78774d4485bc7090bf7b955632"
}

各キーと値のペアを、インスタンスURL、ターゲットにするAPIバージョンおよび公開チャネルに関連付けられたチャネル・トークンを反映するように変更します。このチュートリアルのチャネルはOCEGettingStartedChannelです。チャネル・トークンはsrc/config/content.jsonファイルにあります。ローカルホストのポート番号に'expressServerPort'の値を追加することもできます。

ノート: これらの値は、ホスティング・プロバイダまたはローカルの.envファイルに環境変数として格納することをお薦めします。インスタンスURLまたはチャネル・トークンを公開すると、サービス拒否(DDoS)の分散攻撃が発生する可能性があります。このリスクは、インスタンスのCross- Origin Resource Sharing (CORS)が正しく構成されていることを確認することで緩和されます。

Oracle Content Management Content SDKの操作

Oracle Content Managementは、アプリケーションでコンテンツを検出して使用するためのSDKを提供します。SDKはNPMモジュールとして公開され、プロジェクトはGitHubでホストされます。

SDKの詳細は、ここを参照してください。

SDKは、このプロジェクトのランタイム依存関係としてpackage.jsonファイルに登録されました。

Content SDKを使用したコンテンツのフェッチ

Content SDKを活用してコンテンツをフェッチできるようになり、Oracle JETアプリケーションでコンテンツをレンダリングできるようになりました。

src/scriptsフォルダには、Content SDKを使用してOracle Content Managementからデータを取得するためのコードが含まれています。

src/scripts/server- config- utils.jsファイルはContent SDKをインポートし、src/config/content.jsonで指定された構成を使用してデリバリ・クライアントを作成します。

次のコマンドは、SDKをインポートします。

import { createDeliveryClient, createPreviewClient } from '@oracle/content-management-sdk';

次のコマンドは、デリバリ・クライアントを作成します。

return createDeliveryClient(serverconfig);

src/scripts/services.jsファイルには、アプリケーションのデータを取得するためのすべてのコードが含まれます。アプリケーションの各ページ・コンポーネントには、そのページのすべてのデータを取得するためのメイン機能が1つあります。

イメージをレンダリングする場合、services.jsには、アセットのレンディションから構築されたアセットのソースセットを取得するヘルパー・メソッドが用意されています。

function getSourceSet(asset) {
  const urls = {};
  urls.srcset = '';
  urls.jpgSrcset = '';
  if (asset.fields && asset.fields.renditions) {
    asset.fields.renditions.forEach((rendition) => {
      addRendition(urls, rendition, 'jpg');
      addRendition(urls, rendition, 'png');
      addRendition(urls, rendition, 'webp');
    });
  }
  // add the native rendition to the srcset as well
  urls.srcset += `${asset.fields.native.links[0].href} ${asset.fields.metadata.width}w`;
  urls.native = asset.fields.native.links[0].href;
  urls.width = asset.fields.metadata.width;
  urls.height = asset.fields.metadata.height;
  return urls;
}

ホーム・ページ・データ

ホーム・ページには、すべてのデータを取得するために複数のデータ・コールが必要です。

  1. まず、src/config/content.jsonで指定されたチャネルの項目を問い合せます。
  2. 次に、各トピック項目について、その詳細をフェッチします。
  3. 最後に、各トピックのすべての記事データをフェッチします。

src/scripts/services.jsを開き、getTopicsListPageData()関数を見つけ、ホーム・ページのすべてのトピック・データを取得します。

export function getTopicsListPageData() {
  const client = getClient();

  return fetchHomePage(client)
    .then((data) => (
      getRenditionURLs(client, data.logoID)
        .then((renditionUrls) => {
          data.companyThumbnailRenditionUrls = renditionUrls;
          return data;
        })
    ));
}

getTopicsListPageData()関数によってコールされるfetchHomePage()関数は、チャネル内のすべての項目を取得します。ロゴID、会社名、情報および連絡先URL、およびトピックのリストを取得します。このサンプル・ブログでは、HomePage_JETというホーム・ページを問い合せています。

function fetchHomePage(client) {
  return client.queryItems({
    q: '(type eq "OCEGettingStartedHomePage" AND name eq "HomePage_JET")',
  }).then((data) => {
    const logoID = data.items[0].fields.company_logo.id;
    const title = data.items[0].fields.company_name;
    const aboutUrl = data.items[0].fields.about_url;
    const contactUrl = data.items[0].fields.contact_url;

    const { topics } = data.items[0].fields;
    const promises = [];

    topics.forEach((origTopic) => {
      // add a promise to the total list of promises to get the full topic details
      promises.push(
        fetchTopic(client, origTopic.id)
          .then((topic) => topic),
      );
    });

    // execute all the promises returning a single dimension array of all
    // of the topics and the other home page data
    return Promise.all(promises)
      .then((allTopics) => (
        {
          logoID,
          companyTitle: title,
          aboutUrl,
          contactUrl,
          topics: flattenArray(allTopics),
        }
      )).catch((error) => logError('Fetching topics failed', error));
  }).catch((error) => logError('Fetching home page data failed', error));
}

fetchTopic()関数は、トピックの詳細をすべて取得するために各トピックIDに対してコールされます。

function fetchTopic(client, topicId) {
  return client.getItem({
    id: topicId,
    expand: 'fields.thumbnail',
  }).then((topic) => {
    topic.renditionUrls = getSourceSet(topic.fields.thumbnail);
    return topic;
  }).catch((error) => logError('Fetching topic failed', error));
}

getTopicsListPageData()は、レンダリングするイメージのURLを取得するためにgetRenditionURLs()もコールします。

function getRenditionURLs(client, identifier) {
  return client.getItem({
    id: identifier,
    expand: 'fields.renditions',
  }).then((asset) => getSourceSet(asset))
    .catch((error) => logError('Fetching Rendition URLs failed', error));
}

次に、ホーム・ページで、各トピックのfetchTopicArticles(topicId)関数もコールします。これは、指定したトピックおよびレンディションURLのすべての記事を取得するために使用されます。

export function fetchTopicArticles(topicId) {
  const client = getClient();
  return client.queryItems({
    q: `(type eq "OCEGettingStartedArticle" AND fields.topic eq "${topicId}")`,
    orderBy: 'fields.published_date:desc',
  }).then((data) => {
    const promises = [];
    const articles = data.items;

    articles.forEach((article) => {
      // add a promise to the total list of promises to get the article url
      promises.push(
        getRenditionURLs(client, article.fields.image_16x9.id)
          .then((renditionUrls) => {
            article.renditionUrls = renditionUrls;
            return {
              ...article,
            };
          }),
      );
    });

    // execute all the promises and return all the data
    return Promise.all(promises)
      .then((allArticles) => ({
        topicId,
        articles: flattenArray(allArticles),
      }));
  }).catch((error) => logError('Fetching topic articles failed', error));
}

fetchTopicArticles()メソッドは、前述のようにgetRenditionURLs()を使用して記事のイメージを取得します。

記事ページ・データ

記事ページは記事IDを受け取り、すべてのデータを取得するために複数のデータ・コールを必要とします。

  1. 指定した記事の記事の詳細を取得します。各記事について、そのレンディションURLを取得します。
  2. 作成者アバターのレンディションURLを取得します。

src/scripts/services.jsを開き、記事ページのデータを取得するfetchArticleDetails(articleId)関数を見つけます。

export function fetchArticleDetails(articleId) {
  const client = getClient();
  return client.getItem({
    id: articleId,
    expand: 'fields.author,fields.image',
  }).then((article) => {
    const title = article.fields.author.name;
    const date = article.fields.published_date;
    const content = article.fields.article_content;
    const imageCaption = article.fields.image_caption;
    const { name } = article;
    const renditionUrls = getSourceSet(article.fields.image);
    const avatarID = article.fields.author.fields.avatar.id;
    // Get the author's avatar image
    return getRenditionURLs(client, avatarID)
      .then((authorRenditionUrls) => (
        // return an object with just the data needed
        {
          id: articleId,
          name,
          title,
          date,
          content,
          imageCaption,
          renditionUrls,
          authorRenditionUrls,
        }
      ));
  }).catch((error) => logError('Fetching article details failed', error));
}

fetchArticleDetails()メソッドは、前述のようにgetRenditionURLs()を使用してアバター・イメージを取得します。

Oracle JETの構造とページ

次の各項では、Oracle JETによるデータのレンダリング方法の概要を説明します。

Oracle JETブログのアプリケーション・フロー

このOracle JETアプリケーションの主要なエントリ・ポイントは、src/app.tsxファイルです。このファイルは、プリクト・ルーターとアプリケーションのすべてのページをインポートします。

当サイトでは、次の3つのルートを提供します。

次のルーター・コードに示すように、デフォルトのホーム・ページ・パス'/'はTopicsListPage用で、各記事はArticleDetailsPageのサーバー+ 'article/: articleId'でホストされます。プリアクション ルータのroute()メソッドを使用して、ここでルーティングされます。無効なルートがNotFoundPageに送信されます。

ノート: ベース・ルーター・パスの追加サポートは、目的のパスの前にbase_urlを追加することで、Preactルーターでここで変更できます。サンプル・コードには、base_urlの場合にPreact Router route()メソッドを置き換える特別なappRoute()メソッドが含まれています。

<Router>
  <TopicsListPage path="/" />
  <ArticleDetailsPage path="/article/:articleId" />
  <NotFoundPage default />
</Router>

TopicsListページ

TopicsListページ(ホーム)は、個々のトピックおよび選択したトピックの記事リストで構成されるトピック・リストで構成されます。

TopicsListPageクラスは、ランタイムのcomponentDidMountフェーズでコールされるfetchData()関数を使用してデータをフェッチします。

fetchData()関数は次のとおりです。

fetchData() {
  this.setState(() => ({
    loading: true,
  }));

  getTopicsListPageData()
    .then((data) => {
      const promiseArr = [];
      for (let i = 0; i < data.topics.length; i += 1) {
        promiseArr.push(
          fetchTopicArticles(data.topics[i].id).then(
            (topicArticlesData) => { data.topics[i].articleData = topicArticlesData; },
          ),
        );
      }
      Promise.all(promiseArr).then(() => {
        this.setState({
          data,
          loading: false,
        });
      });
    });
}

初期ロード時に進捗円を表示するように状態がロードに設定され、ロードが完了するとfalseに設定されます。関数getTopicsListPageData()は一度呼び出され、トピックごとにfetchTopicArticles()がコールされます。結果は、このコード・セクションで解決される約束です。

すべてのデータを取得したら、ボディウェルカムページを作成します。

ボディは次のように作成されます。ここでは、oj- sp- image- cardコンポーネントを使用します。

const bodyElems = [];
let articleCards = [];
topics.forEach(
  (topic) => {
    bodyElems.push(
      <div className='topicType'>
        <div className='topicHeading'>{topic.name}</div>
        <div>{topic.description}</div>
      </div>,
    );
    topic.articleData.articles.forEach((article) => {
      articleCards.push(
        <oj-sp-image-card
          primaryText={article.name}
          secondaryText={article.description}
          imageSrc={article.renditionUrls.native}
          footerType='none'
          onClick={ function openArticleLink() { route(`/article/${article.id}`); } }
        />,
      );
    });
    bodyElems.push(
      <div className='articleItem'>
        {articleCards}
      </div>,
    );
    articleCards = [];
  },
);

ここで、bodyElemsは、oj- sp- welcome- pageコンポーネントにスロットインするHTML要素の配列です。bodyElemsは、トピック・ヘッダーと、そのトピックの各記事の実際のイメージ・カードを交互に記述します。

各イメージカードには、16: 9のアスペクト比のイメージがあります。これは、oj- sp- image- cardコンポーネントに16: 9イメージ・サイズを使用する制限があり、このOracle JETサンプル・ブログ・コードのojspcardの名前とともに16: 9レンディションがrenditionUrlsにリストされています。また、イメージ・カードのonClickアクションは、記事ページにルーティングされる関数です。

TopicsListPageによって返されます。これには、bodyElemsが内側にスロットされた状態でoj- sp- welcome- pageコンポーネントが含まれます。

<oj-sp-welcome-page
  pageTitle={companyTitle}
  overlineText= 'Home'
  displayOptions={{
    imageStretch: 'full',
  }}
  illustrationForeground={companyThumbnailRenditionUrls.native}
  primaryAction={{
    label: 'About Us',
    icon: 'oj-ux-ico-information',
    display: 'on',
  }}
  onspPrimaryAction={ function openAboutLink() { window.open(aboutUrl, '_blank'); } }
  secondaryAction={{
    label: 'Contact Us',
    icon: 'oj-ux-ico-contact-edit',
    display: 'on',
  }}
  onspSecondaryAction={ function openContactLink() { window.open(contactUrl, '_blank'); } }
>
{bodyElems}
</oj-sp-welcome-page>

oj- sp- welcome- pageコンポーネントは、ページのヘッダーを作成し、bodyElemsを格納する領域を提供します。見たとおり、ページ・タイトル、「ホーム」オーバーライン・テキスト、ボタンおよび表示イメージがあります。表示イメージはillustrationForegroundフィールドにソースがあり、イメージはdisplayOptionsパラメータを使用してストレッチされます。oj- uxライブラリにアイコンがある「About Us」と「Contact Us」の2つのボタンがあり、どちらのボタンも「About Us」と「Contact Us」という新しいタブでリンクを開き、私たちに問い合わせてください。

ArticleDetailsページ

ArticleDetailsページは、作成者、イメージ、テキストなど、記事自体に関連するすべてのデータで構成されます。

ArticleDetailsクラスは、ランタイムのcomponentDidMountフェーズでコールされるfetchData()関数を使用してデータをフェッチします。

fetchData()関数は次のとおりです。

fetchData(articleId) {
  this.setState(() => ({
    loading: true,
  }));

  fetchArticleDetails(articleId)
    .then((data) => this.setState(() => ({
      data,
      loading: false,
    })));
  }
}

初期ロード時に進捗円を表示するように状態がロードに設定され、ロードが完了するとfalseに設定されます。ファンクションfetchArticleDetails()は、オンになっているarticleIdに対して1回コールされます。結果は、このコード・セクションで解決される約束です。

すべてのデータを取得したら、articleDetailsページを作成します。

ページは次のように作成されます。ここでは、oj- sp- item- overview- pageコンポーネントと、「概要」セクションにスロットされたoj- sp- item- overviewコンポーネントと、「メイン」セクションにスロットされたメイン・コンテンツを使用します。

<oj-sp-item-overview-page
  overviewExpanded={false}
  translations={{
    goToParent: 'Back to Home',
  }}
  onspGoToParent={ function goToParent() { route('/'); } }
>
  <div slot='overview'>
    <oj-sp-item-overview
      itemTitle={title}
      itemSubtitle={formattedDate}
      photo={{
        src: data.authorRenditionUrls.small,
      }}
    />
  </div>
  <div slot='main'>
    <h1 style='text-align:center'>{name}</h1>
    <figure>
      {data.renditionUrls && (
        <picture>
          <source type='image/webp' srcSet={data.renditionUrls.srcset} />
          <source srcSet={data.renditionUrls.jpgSrcset} />
          <img
            src={data.renditionUrls.small}
            alt='Article'
            width={parseInt(data.renditionUrls.width, 10) * 0.66}
            height={parseInt(data.renditionUrls.height, 10) * 0.66}
          />
        </picture>
      )}
      <figcaption style='text-align:center'>{imageCaption}</figcaption>
    </figure>
    {/* eslint-disable-next-line @typescript-eslint/naming-convention */}
    <div dangerouslySetInnerHTML={{ __html: cleancontent }} />
  </div>
</oj-sp-item-overview-page>

oj- sp- item- overview- pageコンポーネントは、articleDetailsページのレイアウトを提供します。特に、overviewExpandedはページ全体を置き換えるためにfalseで、goToParentの翻訳は「ホームに戻る」であり、onspGoToParentはtopicsListページにルーティングするロジックを完了する関数です。

oj- sp- item- overview- pageが取り込むことができるスロットが2つあり、これらは「概要」スロットと「メイン」スロットで、それぞれ記事の作成者に関する詳細が含まれ、記事の内容を提供します。

oj- sp- item- overviewコンポーネントは「概要」セクションにスロットされ、作成者の詳細の列レイアウトを提供します。itemTitleは作成者の名前、サブタイトルは書式設定された日付、写真は作成者写真のソースです。

「main」スロットされたセクションには、記事のタイトル、キャプションを含むイメージ、記事のブログ・テキストを含む記事のすべての詳細が含まれます。

タスク3: アプリケーションのデプロイメントの準備

Oracle JETブログ・サイトをビルドしたので、問題のデバッグやアプリケーションのプレビューを稼働前に行えるように、ローカル開発サーバーで確認する必要があります。

プロジェクトのルートにあるpackage.jsonファイルには、バンドルの作成およびアプリケーションの実行を容易にするためのスクリプトが含まれています。

開発

開発中にdevスクリプトを使用できます。

npm run dev

このスクリプトは、クライアントとサーバーのバンドルを構築し、ローカルサーバーでアプリケーションを起動します。Webpackはコードの変更を監視し、必要に応じてクライアント・バンドルとサーバー・バンドルを再作成します。

生産

本番では、ビルド・スクリプトを使用してクライアントおよびサーバー・バンドルを構築します。

npm run build

スクリプトが完了すると、次のコマンドを使用してアプリケーションを実行できます。

npm run start

まとめ

このチュートリアルでは、GithubにあるOracle JETにブログ・サイトを作成しました。このサイトでは、Oracle Content ManagementをヘッドレスCMSとして使用しています。ブログ・サイト・チュートリアルの公開済コンテンツのチャネルを使用してOracle Content Managementを設定および構成した後、Oracle JETサイトをインストールして実行し、必要なコンテンツをフェッチしてサイトを構築しました。

Oracle JETの詳細は、Oracle JET Webサイトにアクセスしてください。

Oracle Content Managementの重要な概念については、ドキュメンテーションを参照してください。

このようなサンプルは、Oracle Help CenterOracle Content Managementサンプル・ページで確認できます。