コンテンツ・レイアウトのデータ構造の標準化

コンテンツ・レイアウト開発者は、コンテンツ・レイアウトが受信するデータの構造を標準化する必要があります。

すべてのデータが存在する場合は、コンテンツ・レイアウトでコンポーネントを単にレンダリングできます。データの一部が存在しない場合、コンテンツ・レイアウトで問合せを追加する必要がある場合があります。いずれの場合も、コンテンツ・レイアウトでは特定のデータ・フォーマットが想定されることはなく、レンダリングされるフォーマットにデータが強制的に変換されます。

データがすべて期待どおりであることを確認する必要があります。データが存在しない場合は、追加の問合せを行う必要があります。データから次のフィールドが欠落している可能性があります:

  • 参照フィールドのfieldsエントリ

  • 大きいテキスト・フィールド

コンテンツ・レイアウトは特定のコンテンツ・タイプ用に設計されているため、コンテンツ・レイアウトの開発者は必要なフィールドのリストを把握します。これらの各フィールドについて、コンテンツ・レイアウトがレンダリングできるようにデータをフェッチする必要があります。欠落データをフェッチしてから完全なデータでレンダリングする、または、すぐにレンダリングしてから欠落データをフェッチして空白を埋める、という2つのオプションがあります。

オプション1: 欠落データのフェッチおよび完全データでのレンダリング

約束を作成して必要なデータを取得し、すべての約束が戻ったときにレンダリングを続行します。

たとえば、対応するフィールドを持つ次のコンテンツ・タイプがあります:

  • starter-blog-author

    • フィールド

      • starter-blog-author_name - テキスト・フィールド

      • starter-blog-author_bio - テキスト・フィールド

  • starter-blog-post

    • フィールド

      • starter-blog-post_title - テキスト・フィールド

      • starter-blog-post_content - 大きいテキスト・フィールド

      • starter-blog-post_author - starter-blog-authorアイテムへの参照

コンテンツ・レイアウトには次のテンプレートがあり、想定されるフィールド値をレンダリングします:

{{#fields}}
<div class="blog_container">
    <div class="blog-post-title">{{starter-blog-post_title}}</div>
    {{#starter-blog-post_author.fields}}
    <div class="blog-author-container">
        <div class="blog-author-details">
            <div class="blog-author-name">{{starter-blog-author_name}}</div>
            <div class="blog-author-bio">{{{starter-blog-author_bio}}}</div>
            <span class="more-from-author">More articles from this author</span>
        </div>
    </div>
    {{/starter-blog-post_author.fields}}
    <div class="blog-post-content">{{{starter-blog-post_content}}}</div>
</div>
{{/fields}}

コンテンツ・レイアウトは、次の問合せからデータを使用してコールできます:

  • expandを使用したアイテム問合せ - 指定されたすべてのデータ

    • /content/published/api/v1.1/items/{id}?expand=fields.starter-blog-post_author&channelToken=8dd714be0096ffaf0f7eb08f4ce5630f

    • これは、テンプレート内のすべての値を正常に移入するために必要なデータのフォーマットです。他のいずれかの問合せを使用した場合は、データをフェッチしてこのフォーマットに変換する追加作業が必要です。

    • "fields": {    
          "starter-blog-post_title": "...",
          "starter-blog-post_summary": "...",
          "starter-blog-post_content": "...",
          "starter-blog-post_author": {
              "id": "CORE386C8733274240D0AB477C62271C2A02",
              "type": "Starter-Blog-Author"
              "fields": {
                  "starter-blog-author_bio": "...",
                  "starter-blog-author_name": "..."
              }
          }
      }
  • アイテム問合せ(expandなし) - 参照アイテム・フィールドが"starter-blog-post_author.fields "にありません:

    • /content/published/api/v1.1/items/{id}?channelToken=8dd714be0096ffaf0f7eb08f4ce5630f
    • "fields": {    
          "starter-blog-post_title": "...",
          "starter-blog-post_summary": "...",
          "starter-blog-post_content": "...",
          "starter-blog-post_author": {
              "id": "CORE386C8733274240D0AB477C62271C2A02",
              "type": "Starter-Blog-Author"
          }
      }
  • SCIM問合せ - 大きいテキスト・フィールド"starter-blog-post_content"がありません。参照アイテム・フィールド"starter-blog-post_author.fields"がありません:

    • /content/published/api/v1.1/items?q=(type eq "Starter-Blog-Post")&fields=ALL&channelToken=8dd714be0096ffaf0f7eb08f4ce5630f

    • "fields": {
          "starter-blog-post_title": "...",
          "starter-blog-post_summary": "...",
          "starter-blog-post_author": {
              "id": "CORE386C8733274240D0AB477C62271C2A02",
              "type": "Starter-Blog-Author"
          }
      }

これらのいずれかの問合せで一貫してレンダリングできるようにするには、コンテンツ・レイアウトからのrender.jsを使用して、参照されるすべてのフィールドが展開され、大きいテキスト・フィールドが存在することを確認する必要があります。

そうでない場合は、これらのバックアップを問い合せてデータを修正し、完全なデータを使用してレンダリングする必要があります。

サンプルのrender()関数:

render: function (parentObj) {
    var self = this,
        template,
        contentClient = self.contentClient,
        content = self.contentItemData;

     var getRefItems = function (contentClient, ids) {
        // Calling getItems() with no "ids" returns all items.
        // If no items are requested, just return a resolved Promise.
        if (ids.length === 0) {
            return Promise.resolve({});
        } else {
            return contentClient.getItems({
                "ids": ids
            }); 
        }
     };
   
     var fetchIDs = [], // list of items to fetch
         referedFields = ['starter-blog-post_author'], // names of reference fields
         largeTextFields = ['starter-blog-post_content'], // large text fields in this asset
         fieldsData = content.fields;
     // See if we need to fetch any referenced fields
     referedFields.forEach(function (fieldName) {
         if(fieldsData[fieldName] && fieldsData[fieldName].fields) {
            // got data already, nothing else to do
         } else { 
             // fetch this item
             fetchIDs.push(fieldsData[fieldName].id);
         }
     });

     // See if we need to fetch any large text fields
     for(var i = 0; i < largeTextFields.length; i++) {
        if(!fieldsData[largeTextFields[i]]) {
           // need to fetch this content item directly to get all the large text fields
            fetchIDs.push(content.id);
            break;
        }
     }
    // now we have the IDs of all the content items we need to fetch, get them all before continuing
    getRefItems(contentClient, fetchIDs).then(function (referenceData) {
        var items = referenceData && referenceData.items || [];

        // add the data back in
        items.forEach(function (referencedItem){
            // check if it's the current item
            if(referencedItem.id === content.id) {
               // copy across the large text fields 
               largeTextFields.forEach(function (fieldName) {
                   fieldsData[fieldName] = referencedItem.fields[fieldName];
                });
            } else{
                // check for any referenced fields
                for (var i = 0; i < referedFields.length; i++) {
                    if(referencedItem.id === fieldsData[referedFields[i]].id){
                       // copy across the fields values
                       fieldsData[referedFields[i]].fields = referencedItem.fields;
                       break;
                    }
                }
            }
        });

        // now data is fixed up, we can continue as before
        try{
           // Mustache
           template = Mustache.render(templateHtml, content);

             if(template) {
                $(parentObj).append(template);
             }

        } catch (e) {            
            console.error(e.stack);
        }    
    });
}

オプション2: 即座にレンダリングした後、欠落しているデータをフェッチして空白に入力

パフォーマンスを向上するには、存在しない可能性があるアイテムを分離し、それらを2番目のパスでレンダリングします。これには2つのMustacheテンプレートが必要です。最初のテンプレートが初回のレンダリングを実行し、「穴」をそのままにしますが、この「穴」は、データが完成したときに2回目のレンダリングで埋められます。

この場合、「穴」に個別のテンプレートを使用するか、モデルに実際の値ではなくテンプレート・マクロを戻させることにより、複数のパスをサポートするようMustacheテンプレートを設定する必要があります。どちらの場合も、データが取得されるまでこれらの「穴」を非表示にしてから移入し、適切なUIアニメーションを使用してページが「動き回り」すぎないようにする必要があります。