Programista tworzący układ zawartości powinien ustandaryzować strukturę danych otrzymywanych przez układ zawartości.
Jeśli wszystkie dane są obecne, układ zawartości może bezproblemowo wyrenderować składnik. Jeśli nie wszystkie dane są obecne, może wystąpić wykonania dodatkowych zapytań. We wszystkich przypadkach układ zawartości nie po9wnien nigdy zakładać określonego formatu danych, a zamiast tego wymuszać dane w formacie umożliwiającym renderowanie.
Trzeba zapewnić obecność wszystkich oczekiwanych danych. Jeśli pewne dane nie istnieją, trzeba wykonać dodatkowe zapytania. W danych może potencjalnie brakować:
Wpisu "fields
" dla pól, do których występuje odwołanie
Dużych pól tekstowych
Ponieważ układy zawartości są projektowane dla określonych typów zawartości, programista układu wie, które pola są wymagane. Aby układ zawartości mógł zostać wyrenderowany, dla każdego z tych pól muszą zostać pobrane dane. Są dostępne dwa rozwiązania: pobrać brakujące dane i renderować układ z użyciem kompletnych danych albo renderować od razu, a następnie pobrać brakujące dane w celu wypełnienia pustych miejsc.
Rozwiązanie 1: Pobieranie brakujących danych i renderowanie z użyciem kompletnych danych
Utworzyć obiekt "Promise" do pobrania wymaganych danych, a następnie kontynuować renderowanie, gdy zostaną zwrócone wszystkie dane "Promise".
Na przykład mamy następujące typy zawartości z odpowiadającymi im polami:
starter-blog-author
pola
starter-blog-author_name
— pole tekstowe
starter-blog-author_bio
— pole tekstowe
starter-blog-post
pola
starter-blog-post_title
— pole tekstowe
starter-blog-post_content
— duże pole tekstowe
starter-blog-post_author
— odwołanie do elementu starter-blog-author
Układ zawartości ma następujący szablon do renderowania wartości tych oczekiwanych pól:
{{#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}}
Układ zawartości można wywołać z danymi z następujących zapytań:
Zapytanie (o element) z właściwością "expand
" — dostarczone wszystkie dane
/content/published/api/v1.1/items/{id}?expand=fields.starter-blog-post_author&channelToken=8dd714be0096ffaf0f7eb08f4ce5630f
Jest to format danych wymaganych do pomyślnego wypełnienia wszystkich wartości w szablonie. Jeśli którekolwiek z zapytań zostanie użyte, wymagane jest pobranie danych i przekształcenie ich do tego formatu.
"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": "..." } } }
Zapytanie (o element) bez właściwości "expand
" — brak pól elementu, do których występuje odwołanie "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" } }
Zapytanie SCIM — brak dużego pola tekstowego "starter-blog-post_content" oraz brak pól "starter-blog-post_author.fields" elementu, do których występuje odwołanie:
/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" } }
Aby można było spójnie renderować z użyciem dowolnego z tych zapytań, render.js
z układu zawartości musi się upewnić, że pola, do których występują odwołanie, zostały rozwinięte oraz że są obecne duże pola tekstowe.
Jeśli tak nie jest, musi ponownie uruchomić zapytanie, poprawić dane, po czym renderować z użyciem kompletnych danych.
Przykładowa funkcja 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); } }); }
Rozwiązanie 1: Natychmiastowe renderowanie i późniejsze pobranie brakujących danych w celu wypełnienia pustych miejsc
Wydajność operacji można poprawić, oddzielając elementy, których może nie być, i renderując je w drugim przebiegu. W tym celu są potrzebne dwa szablony Mustache: pierwszy do wykonania pierwszego renderowania z pozostawieniem pustych miejsc, które następnie zostaną wypełnione podczas drugiego renderowania, kiedy będą obecne wszystkie dane.
Wymaga to skonfigurowania szablonu Mustache do obsługi więcej niż jednego przebiegu przez używanie albo osobnych szablonów dla pustych miejsc, albo modelu z makrami zwracającymi szablon zamiast faktycznych wartości. W obu przypadkach trzeba ukryć puste miejsca do chwili pobrania danych, wstawienia ich i pokazania za pomocą odpowiedniej animacji UI (aby uniknąć zbytniego „przeskakiwania”).