---
title: "JSS Vue.jsサンプルアプリでプレースホルダーを扱う方法"
slug: "working-with-placeholders-in-a-jss-vue-js-sample-app"
abstract: "JSS Vueアプリケーションにおけるプレースホルダー使用技術の概要"
---
# JSS Vue.jsサンプルアプリでプレースホルダーを扱う方法

[プレースホルダーは](https://doc.sitecore.com/xp/ja/developers/hd/19/sitecore-headless-development/introducing-placeholders-in-jss-apps.html "Introducing placeholders in JSS apps") 、JSSで構築されたあらゆるアプリケーションにおいて重要な要素です。

> !注Sitecoreレイアウト、プレースホルダー、レンダリングの仕組みやそれらを活用する方法については、「 [Sitecoreデータを使うJSSアプリのページ構成](https://doc.sitecore.com/xp/ja/developers/hd/19/sitecore-headless-development/page-composition-in-jss-apps-using-sitecore-data.html "Page composition in JSS apps using Sitecore data") 」を参照してください。また、それらを活用する方法JSS。

このトピックでは、JSSでVue.jsアプリケーションを開発する際に使えるプレースホルダーの手法について説明します。

## 基本的なプレースホルダー技術

プレースホルダーを追加する最も基本的かつ一般的な方法は、Placeholderコンポーネントを使うことです:

<template>
 <div>
 <h1>My App</h1>
 <sc-placeholder name="jss-main" :rendering="route" />
 </div>
</template>
<script>
import { Placeholder } from '@sitecore-jss/sitecore-jss-vue';

export default{
 name: 'MyComponent',
 props: {
 route: {
 type: Object
 }
 },
 components: {
 ScPlaceholder: Placeholder,
 }
}
</script>

nameは公開するプレースホルダーのキーであり、renderingは現在提供されているSitecoreルート/レイアウトデータ、または他のコンポーネント内でプレースホルダーを公開する場合は親コンポーネントデータです。

Placeholderコンポーネントは子コンポーネントの周りにラッパーをレンダリングしません。これはVueの[フラグメント技術](https://v3.vuejs.org/guide/migration/fragments.html#overview)によるものです。

## SitecoreJssPlaceholderPlugin technique

この技術により、*Vueコンポーネントの配列をプレースホルダー* で、計算されたプロパティとしてコンポーネントインスタンスに付与してプレースホルダーを公開できます。

この技術により、従来のSitecore開発ではほぼ不可能だった非常に強力な操作技術が可能になります。

この技術を使うには、sitecore-jss-vueパッケージに付属しているSitecoreJssPlaceholderPluginVueプラグインをインストールする必要があります。以下のように。

import { createApp, createSSRApp } from 'vue';
import { SitecoreJssPlaceholderPlugin } from '@sitecore-jss/sitecore-jss-vue';
import componentFactory from './path/to/your/componentFactory.js';

const app = isSSR ? createSSRApp(vueOptions) : createApp(vueOptions);

// The plugin uses a "global" componentFactory, but you can override
// the \`componentFactory\` at the component level if needed.
app.use(SitecoreJssPlaceholderPlugin, { componentFactory });

コンポーネントでプラグインを使うには、コンポーネントにwithPlaceholderプロパティを追加してください:

<template>
 <div>
 <component v-for="(tab, index) in tabs" :is="tab" :key="\`tab${index}\`" />
 </div>
</template>

<script>
export default {
 name: 'MyComponent',
 props: {
 rendering: {
 type: Object,
 },
 },
 withPlaceholder: {
 placeholders: 'tabs',
 },
};
</script>

前述の例では、SitecoreJssPlaceholderPluginプラグインはbeforeCreateフックに取り付けるmixinを使ってコンポーネント定義上のwithPlaceholderプロパティを探します。プラグインはwithPlaceholderプロパティが提供する値を使い、renderingプロパティデータ内のtabsなど指定されたプレースホルダーデータを見つけます。プラグインはそのコンポーネントに対して計算されたプロパティを作成し、プレースホルダーの名前をデフォルトでプロパティ名として使い、そのプレースホルダーのすべてのVueコンポーネントの配列を計算されたプロパティに割り当てます。これにより、Vue.jsの[動的コンポーネント](https://v3.vuejs.org/guide/component-basics.html#dynamic-components)機能を使ってテンプレート内のプレースホルダーコンポーネントをレンダリングできます。

テンプレート内のコンポーネント配列を反復すると、Vueは出力した場所でコンポーネントをレンダリングします。またはPlaceholderコンポーネントを使ってこれを実現できます。この方法でプレースホルダーの内容を発行すると、そのプレースホルダーの内容にはラップコンポーネントがなく、インラインでレンダリングされます。これは特定のコンポーネント階層に基づくVueライブラリ(例えばvue-carousel)を使う場合に非常に役立ちます。例えば、以下のコードサンプルを考えてみてください:

<carousel>
 <slide>Slide 1 Content</div>
 <slide>Slide 2 Content</div
 <slide>Slide 3 Content</div
</carousel>

前のサンプルでは、コンポーネント階層がcarousel -> slideであることが期待されます。 slideコンポーネントをプレースホルダーで追加したい場合は、Sitecoreが定義できるようにPlaceholderコンポーネントsc-placeholderを使って同じ階層を達成できます。

<carousel>
 <sc-placeholder name="jss-slides" :rendering="rendering" />
</carousel>

### コンポーネント変換

前述のCarousel->Slideのように完全にフラットなコンポーネント階層を望む場合は、計算された配列特性を利用して、インラインテンプレートを使って子コンポーネントをラッパーで変換できます。この手法を使うと、子Sitecoreコンポーネントはラッピングを完全に認識せず、関心事の分離のために自分のコンテンツのみをレンダリングできます。

<template>
 <carousel>
 <template v-for="(slide, index) in $options.computed.slidesPlaceholder">
 <!-- this \`v-if\` is important, as it helps prevents breakage of the carousel markup when using Sitecore Experience Editor -->
 <component v-if="slide.isxEditorComponent" :is="slide" />

 <!-- wraps \_all\_ child components in a <slide> component -->
 <slide v-else :key="\`slide${index}\`">
 <component :is="slide" />
 </slide>
 </template>
 </carousel>
</template>

<script>
export default {
 name: 'ContainerComponent',
 props: {
 rendering: {
 type: Object,
 },
 },
 withPlaceholder: {
 // you can alias the computed prop name for the placeholder or pass an array of placeholders
 placeholders: {
 placeholder: 'slides',
 computedPropName: 'slidesPlaceholder',
 },
 },
};
</script>
## スロットAPI

SitecoreJssPlaceholderPluginプラグインが提供する動的計算プロパティではなく[スロットパターン](https://v3.vuejs.org/guide/component-slots.html#slots)を好みたい場合は、JSSプレースホルダーにはデフォルトのスロットスコープがあります。<Placeholder>コンポーネントのdefaultスコープ付きスロットを使えば、動的に計算されたプロパティと同様にプレースホルダーの内容のレンダリングを引き継ぐことができます。

以下の例は、デフォルトのスコープスロットを使ってコンポーネント配列をレンダリングする方法を示しています。

<template>
 <sc-placeholder :rendering="rendering" name="jss-main">
 <div v-slot="{components, isEmpty}">
 <!--
 The placeholder is considered "empty" if it contains no assigned presentation components.
 However, if in Experience Editor (EE) mode, the placeholder may still contain EE-generated components.
 -->
 <template v-if="!isEmpty">
 <template v-for="(component, index) in components">
 <!--
 If the component is a presentation component, i.e. not an EE-generated component,
 we may want to wrap it in another component or markup.
 -->
 <div v-if="!component.isxEditorComponent" :key="index">
 <component :is="component" />
 </div>
 <!-- Else the component is an EE-generated component, render it as-is -->
 <component v-else :is="component" :key="index" />
 </template>
 </template>
 <template v-else>
 <!--
 In EE-mode, we still want to render "empty" placeholders so that content authors can
 assign components to them.
 -->
 <component v-for="(component, index) in components" :is="component" :key="index" />
 </template>
 </div>
 </sc-placeholder>
</template>

<script>
import { Placeholder } from '@sitecore-jss/sitecore-jss-vue';

export default {
 name: 'MyComponent',
 props: {
 rendering: {
 type: Object,
 }
 },
 components: {
 ScPlaceholder: Placeholder,
 },
};
</script>
## 直接的なプレースホルダー内省

プレースホルダーのレンダリングを完全にコントロールしたいなら、そのプレースホルダーの構成要素ではなくその *データ*にアクセスし、$props.renderingを使って子コンポーネントのデータやフィールドを巡査できます。なぜなら、プレースホルダーの階層はJavaScriptオブジェクトだからです。このデータは好きなようにレンダリングできます。

良いユースケースとしては、タブのパーツを複数の場所(タブタイトルやタブの内容)にレンダリングしたいコンポーネントなどがあります。例えば:

<template>
 <div class="tabs-container">
 <div class="tabs-heading">
 <ul>
 <li v-for="(tab, index) in rendering.placeholders'jss-tabs'" :key="\`tab${index}\`">
 <button :class="\`${index === activeTabIndex ? 'active' : null}\`" v-on:click="activeTabIndex = index">
 <sc-text :field="tab.fields.title" />
 </button>
 </li>
 </ul>
 </div>
 <div class="tabs-content">
 <component :is="activeTab" />
 </div>
 </div>
</template>
<script>
export default {
 name: 'Tabs',
 props: {
 rendering: {
 type: Object,
 }
 },
 data() {
 return {
 activeTabIndex: 0,
 };
 },
 withPlaceholder() {
 return {
 placeholders: 
 {
 placeholder: 'jss-tabs',
 computedPropName: 'tabsPlaceholder',
 },
 ,
 };
 },
 computed: {
 activeTab() {
 return this.$options.computed.tabsPlaceholder && this.$options.computed.tabsPlaceholderthis.activeTabIndex;
 },
 },
}
</script>
## 複数のプレースホルダー

前述の技術を使えば、コンポーネント内に複数のプレースホルダーをレンダリングできます。例えば:

-   基本的なテクニックを使う:
    
    <template>
     <div>
     <h1>My App</h1>
     <sc-placeholder name="jss-column-1" :rendering="rendering" />
     <sc-placeholder name="jss-column-2" :rendering="rendering" />
     </div>
    </template>
    <script>
    import { Placeholder } from '@sitecore-jss/sitecore-jss-vue';
    export default{
     name: 'MyComponent',
     props: {
     rendering: {
     type: Object
     }
     },
     components: {
     ScPlaceholder: Placeholder,
     }
    }
    </script>
    
-   SitecoreJssPlaceholderPluginの使い方:
    
    <template>
     <div>
     <div class="col-1">
     <component v-for="(comp, index) in this.$options.computed.phColumn1" :is="comp" :key="\`comp${index}\`" />
     </div>
     <div class="col-2">
     <component v-for="(comp, index) in this.$options.computed.phColumn2" :is="comp" :key="\`comp${index}\`" />
     </div>
     </div>
    </template>
    
    <script>
    export default {
     name: 'MyComponent',
     props: {
     rendering: {
     type: Object,
     },
     },
     withPlaceholder: {
     placeholders: 
     {
     placeholder: 'jss-column-1',
     computedPropName: 'phColumn1',
     },
     {
     placeholder: 'jss-column-2',
     computedPropName: 'phColumn2',
     },
     ,
     },
    };
    </script>

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

プレースホルダーコンポーネントは、体験を向上させる多くのカスタマイズをサポートします。

### 誤差成分

プレースホルダーでレンダリングエラー(例外)が発生した場合、プレースホルダーの内容ではなくエラーコンポーネントが表示され、エラーの詳細をコンソールにログします。このコンポーネントは、errorComponentプロパティを使ってVueコンポーネントを置き換えることでカスタマイズできます。

### 欠損した部品の対処

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