---
title: "JSS Angularアプリでのプレースホルダーの扱い方"
slug: "working-with-placeholders-in-a-jss-angular-app"
abstract: "JSS Angularアプリケーションにおけるプレースホルダーコンポーネントの動作と利用技術"
---
# JSS Angularアプリでのプレースホルダーの扱い方

[プレースホルダーは](https://doc.sitecore.com/xp/ja/developers/hd/19/sitecore-headless-development/introducing-placeholders-in-jss-apps.html "Introducing placeholders in JSS apps") 、Sitecore JSSで構築されたあらゆるアプリケーションにおいて重要なコンポーネントです。例として、このトピックではSitecore JSS Angularサンプルアプリケーションを使用します。

JSS Angularサンプルアプリケーションにはルートのプレースホルダーが付属しています。ルートのプレースホルダーはsrc/app/routing/layout/layout.component.htmlで定義されています。

sc-placeholderコンポーネントはAngular用にSitecore JSSからインポートされます@sitecorelabs/sitecore-jss-angular。

<sc-placeholder name="jss-main" rendering="route" (loaded)="onPlaceholderLoaded($event)"></sc-placeholder><sc-placeholder name="jss-main" rendering="route"></sc-placeholder>

sc-placeholder Angularコンポーネントはname属性を持ち、値jss-mainであり、jss-mainというSitecoreプレースホルダーを表していることを示しています。プレースホルダーは、プレースホルダーコンポーネントのrendering入力にバインドされたデータによって示されるルートデータに基づいて、1つ以上のコンポーネントをレンダリングします。

<sc-placeholder name="jss-main" rendering="route" (loaded)="onPlaceholderLoaded($event)"></sc-placeholder>

> !注既存のDOM要素に属性としてsc-placeholderを適用できます。

## ルートデータの起源を理解する

プレースホルダーコンポーネントがどのようにしてrouteデータを受け取るかを理解するには、さまざまなコンポーネント、アプリケーションモジュール、サービスがどのように連携しているかを理解する必要があります。

本番環境では、[ユニバーサルレンダリング](https://angular.io/guide/universal)をサポートするために、JSS Angularアプリケーションを構築するスクリプトはサーバーバンドルとクライアントバンドルをそれぞれ構築します。スクリプトjss buildを実行すると、以下の場所でバンドルを見つけることができます。

-   クライアントバンドル - dist/browser/に所在しています。
-   サーバーバンドル - dist/server.bundle.jsに所在しています。

2つのバンドルは単独で動作できず、必ずサーバーが必要です。JSS Node向けのJavaScriptビューエンジンが付属しており、サーバーバンドルを実行できます。JSS JavaScriptビューエンジンはrenderView関数をエクスポートする*サーバーバンドル*を期待しています。

2つのバンドルを構築し提供するために、JSS Angularアプリはアプリケーションの2つのエントリポイントと、それに関連するアプリモジュールおよびデータサービスを定義します。

ファイルsrc/main.server.tsでは、サーバーバンドルのエントリーポイントを定義し、サーバー AppServerModuleのアプリケーションモジュールをブートストラップしますsrc/app/app.server.module.tsで定義されています。 AppServerModuleは、サーバー上で動作し、JSSアプリのコンテキストデータを保存するJssContextServerSideService src/app/jss-context.server-side.service.tsで定義されています。コンテキストデータには現在のルートのデータとSitecoreコンテキストデータが含まれています。

ファイルsrc/main.tsでは、クライアントバンドルのエントリーポイントを定義し、クライアントレンダリングAppModuleのアプリケーションモジュールをブートストラップします。src/app/app.module.tsで定義されています。AppModuleはクライアント(ブラウザ内)上で動作し、JSSアプリのコンテキストデータを保存するsrc/app/jss-context.service.tsで定義されたJssContextServiceを使用します。

アプリケーションがブラウザ上でサーバーバンドルから実行されると、サーバーはAppServerModuleをレンダリングするrenderView関数を呼び出し、JSSアプリのコンテキストデータを準備します。実装の詳細については、ファイルserver.bundle.tsおよび関連サービスを参照してください。両方のコンテキストサービスはAngularクラスTransferStateを使用します。これはキーバリューストアであり、データを正規化した後にサーバー側のアプリケーションモジュールからクライアント側のアプリケーションモジュールへ転送されます。

renderModule(AppServerModule, {
 document: template,
 url: path,
 extraProviders: 
 // custom injection with the initial state that SSR should utilize
 { provide: 'JSS\_SERVER\_LAYOUT\_DATA', useValue: transferState },
 { provide: 'JSS\_SERVER\_VIEWBAG', useValue: state.viewBag },
 ,
 })
 .then((html) => callback(null, { html }))
 .catch((err) => callback(err, null));

クライアント側AppModuleルートアプリケーションコンポーネントAppComponent、src/app/app.component.tsで定義されたブートストラップを行い、ファイルsrc/app/routing/routing.module.tsで定義されたRoutingModuleクラスをインポートします。RoutingModuleはAngularルーターをアプリケーションのrootに接続し、JSS AngularアプリがAngularルーターのアウトレットを使用できるようにします。

RoutingModuleはルーターのアウトレットプレースホルダーでどのコンポーネントをレンダリングするかを定義します。このコンポーネントはsrc/app/routing/layout/layout.component.tsファイルで定義されたLayoutComponentです。RoutingModuleはルートを解決する際にアクティブなルートの状態を設定するJssRouteResolverプロバイダーを使用します。

最後に、routeデータとレイアウトデータの両方を注入したLayoutComponentは、アクティブなルートをサブスクライブし、Sitecoreコンテキストからルートデータを抽出できます。

この時点で、プレースホルダーコンポーネントは本番環境でルートのHTMLマークアップをレンダリングできます。

[切断モードで](https://doc.sitecore.com/xp/ja/developers/hd/19/sitecore-headless-development/start-a-jss-app-in-disconnected-mode.html "Start a JSS app in disconnected mode")作業する場合、JSSアプリケーションはローカルに定義されたファイルからルートデータを取得します。サンプルアプリケーションにはフォルダsrc/dataにあらかじめ定義されたローカルデータが含まれています。例えばホームページを読み込む際、アプリケーションはファイルsrc/data/en.ymlからデータを取得します。レスポンスにはルートのJSONデータが含まれています:

{
 "context": {
 "pageEditing": false,
 "site": {
 "name": "JssDisconnectedLayoutService"
 },
 "pageState": "normal",
 "language": "en"
 },
 "route": {
 "databaseName": "available-in-connected-mode",
 "deviceId": "available-in-connected-mode",
 "itemId": "home-page",
 "itemLanguage": "en",
 "itemVersion": 1,
 "layoutId": "available-in-connected-mode",
 "templateId": "available-in-connected-mode",
 "templateName": "available-in-connected-mode",
 "name": "home",
 "fields": {
 "pageTitle": {
 "value": "Welcome to Sitecore JSS"
 }
 },
 "placeholders": {
 "jss-main": 
 {
 "uid": "{2C4A53CC-9DA8-5F51-9D79-6EE2FC671B2D}",
 "componentName": "ContentBlock",
 "dataSource": "available-in-connected-mode",
 "params": {},
 "fields": {
 "heading": {
 "value": "Welcome to Sitecore JSS"
 },
 "content": {
 "value": "<p>Thanks for using JSS. Here are some resources to get you started:</p>"
 }
 }
 }
 
 }
 }}

routeプロパティのJSONデータ構造には、placeholdersのリストが含まれています。各プレースホルダー名はコンポーネントの配列に関連付けられています。コンポーネントのプレースホルダーへのマッピングを*「コンポーネント配置ルール*」と呼びます。

sc-placeholderコンポーネントはrouteプロパティ内のplaceholdersオブジェクトにアクセスできるため、名前(jss-main)で対応するプレースホルダーを見つけ、その中のコンポーネントの配列をレンダリングします。この例では、ContentBlockComponentコンポーネント(通常のAngularコンポーネント)を動的にレンダリングし、依存性注入トークンを通じてheadingおよびcontentのフィールドを提供します。

import { Component, Input } from '@angular/core';
import { ComponentRendering } from '@sitecore-jss/sitecore-jss-angular';
@Component({
 selector: 'app-content-block',
 templateUrl: './content-block.component.html',
})
export class ContentBlockComponent {
 @Input() rendering: ComponentRendering;
}

renderingプロパティは、データから得られるContentBlockComponentコンポーネントに関するすべての情報を含みます。

"componentName": "ContentBlock",
 "dataSource": "available-in-connected-mode",
 "params": {},
 "fields": {
 "heading": {
 "value": "Welcome to Sitecore JSS"
 },
 "content": {
 "value": "<p>Thanks for using JSS. Here are some resources to get you started:</p>"
 }
}
## ネストプレースホルダーとコンポーネント

コンポーネントはそれぞれ独立したsc-placeholderコンポーネントを含めることができるため、コンポーネント配置ルールのplaceholdersでその内容を定義できます。

例えば、WelcomeComponentコンポーネントには以下が含まれることがあります:

@Component({
 selector: 'app-welcome',
 template: \`
 <sc-placeholder name="welcome" rendering="rendering"></sc-placeholder>
 \`
})
export class WelcomeComponent {
 rendering: any;
}

この場合、子コンポーネントをコンポーネント配置ルールで定義します:

{
 componentName: 'Welcome',
 fields: {
 title: {
 value: 'Sitecore Experience Platform + JSS',
 },
 text: {
 value: '<p>...</p>',
 },
 logoImage: {
 value: {
 src: '/assets/img/sc\_logo.png',
 alt: 'Logo'
 },
 },
 },
 placeholders: {
 welcome: 
 {
 componentName: 'AnotherComponent',
 fields: {
 /\* etc \*/
 }
 }
 
 }
}
## 入力および出力のバインディング

inputsを使ってコンポーネントにカスタムプロパティをバインディングし、sc-placeholderoutputsのバインディングプロパティを付けることが可能です。これらのプロパティにより、プレースホルダー内のすべてのコンポーネントのプロパティに対する入力および出力バインディングが可能になります。

これにより、子コンポーネントはsc-placeholderによって動的に作成されていても、オーケストレーションやインタラクションが可能になり、複雑なユーザーフローではSitecore XPで管理・最適化したい場合に特に有用です。

以下はinputsとoutputsの簡略化された例です:

@Component({
 selector: 'my-container-component',
 template: \`<sc-placeholder
 name="placeholder"
 rendering="rendering"
 inputs="inputs"
 outputs="outputs"
 ></sc-placeholder>\`
})
class MyContainerComponent {
 @Input() public rendering: any;

 inputs = {
 hello: 'world',
 something: () => 'can be really complex'
 };
 outputs = {
 onSomething: (type) => alert(type)
 }
}

@Component({selector: 'my-rendering')
class MyRendering {
 // standard on any placeholder-managed component
 @Input() public rendering: any;

 // additional properties for input/output binding
 @Input() hello: string;
 @Input() something: Function;
 @Output() onSomething = new EventEmitter<string>();
}
## 怠惰な読み込みプレースホルダー

Sitecoreコンポーネントをレイジーロードすると、プレースホルダーはデフォルトで空のまま表示されます。コンポーネントの読み込み中に表示される一時的なボディを指定することができます。コンポーネントの読み込みが終わると、その一時的なボディは実際のコンテンツに置き換えられます。以下は簡略化された例です:

<sc-placeholder rendering="rendering">
 <img \*scPlaceholderLoading src="https://doc.sitecore.com/api/markdown-image/xp/ja/developers/hd/19/sitecore-headless-development/loading.gif">
 </sc-placeholder>

## プレースホルダーの強化

Placeholderコンポーネントは開発者体験を向上させる複数のカスタマイズをサポートします。

### 誤差成分

プレースホルダーでレンダリングエラーが発生した場合、プレースホルダーはその内容の代わりにエラーコンポーネントを表示し、エラーの詳細をコンソールに記録します。このコンポーネントはerrorComponentプロップを使って自分のReactコンポーネントを置き換えることでカスタマイズできます。

### 欠損コンポーネント

プレースホルダーにcomponentFactoryが知らないレンダリング名が含まれている場合(例えば、バックエンド開発者がFooレンダリングを作成しページに追加したが、まだfoo.component.tsがない場合)、レンダリングはAngularコンポーネントのmissingComponentComponentプロパティで定義されたMissingComponentコンポーネントに置き換えられます。デフォルトの実装はシンプルなメッセージですが、missingComponentComponentプロパティで自分のAngularコンポーネントを使ってカスタマイズできます。