标准化内容布局的数据结构

内容布局开发人员需要对内容布局接收的数据结构进行标准化。

如果所有数据都存在,则内容布局可以简单地呈现组件。如果数据不是全部存在,则内容布局可能需要进行额外查询。在所有情况下,内容布局都不应假定某种数据格式,而是将数据强制转换为将呈现的格式。

您需要确保具有预期的所有数据。如果数据不存在,则需要进行额外查询。数据中可能会缺少以下字段:

  • 用于引用字段的 "fields" 条目

  • 大文本字段

因为内容布局是为特定内容类型设计的,所以内容布局的开发人员知道所需字段的列表。对于这些字段中的每个字段,都需要提取数据,以便内容布局可以呈现。您有两个选项:提取缺少的数据,然后使用完整数据进行呈现;或者立即呈现,然后提取缺少的数据来填充空白。

选项 1:提取缺少的数据,然后使用完整数据进行呈现

创建 Promise 来检索所需的数据,然后在所有 Promise 返回时继续呈现。

例如,我们具有以下内容类型和相应字段:

  • 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:立即呈现,然后提取缺少的数据来填充空白

通过分离出可能不存在的项并在第二轮中呈现它们,可以提高性能。这将需要两个 Mustache 模板,第一个进行初始呈现,留下“洞”,然后在数据完整时通过第二次呈现来填充这些洞。

这需要设置 Mustache 模板以支持多轮次:针对“洞”具有单独的模板或者具有模型返回模板宏,而不是实际值。在任一情况下,您都需要“隐藏”这些洞,直到检索数据,然后填充这些洞并使用适当的 UI 动画显示它们,以避免页“跳跃”过多。