Contentful CMS数据提取问题求助:嵌套对象处理及资源推荐
规整Contentful嵌套数据的解决方案
我从Contentful CMS获取数据时,返回的是庞大的嵌套对象,必须通过{{post.section4Facts.content[1].content[1].content[0].value}}这类多层索引才能拿到需要的值,希望能规整这种数据结构,同时寻求相关技术资源(Contentful官网没找到对应解答)。以下是我用的Angular组件代码和模板代码:
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ServiceService } from '../service.service'; import * as contentful from 'contentful'; @Component({ selector: 'app-post-details', templateUrl: './post-details.component.html', styleUrls: ['./post-details.component.scss'] }) export class PostDetailsComponent implements OnInit { postId: any; post: any; tags: any[] = []; constructor(private route: ActivatedRoute, private apiService: ServiceService) { } ngOnInit(): void { this.route.params.subscribe(params => { this.postId = params['id']; console.log(this.postId); const client = contentful.createClient({ space: '****', accessToken: '****', }); client.getEntry(this.postId).then(entry => { this.post = entry.fields; this.tags = entry.metadata.tags console.log(this.tags[0].sys.id); }).catch(error => { console.log(error); }); }); } }
模板代码:
<div class="container" *ngIf="post"> <div class="row"> <div class="col-md-8 col-lg-8"> <article class="article-post"> <div class="article-header"> <h1 class="article-title" *ngIf="post.title">{{post.title}}</h1> <img class="article-img" src={{post.image?.fields.file.url}} alt=""> <div class="article-meta"> <span class="meta-date"><i class="far fa-clock"></i> May 20, 2023</span> <span class="meta-category " *ngFor="let tag of tags"> <i class="far fa-folder"></i> {{tag? tag.sys.id : 'No tags'}}</span> </div> </div> <div class="article-content"> <h2>{{post.section1Title}}</h2> <p>{{post.description.content[0].content[0].value}}</p> <!-- section 1 explained desc --> <p>{{post.section1Info.content[0].content[0].value}}</p> <!-- section 2 my thoughts --> <h2>{{post.section2Title}}</h2> <p>{{post.section2Experience.content[0].content[0].value}}</p> <!-- section 3 info --> <h2>{{post.section3Title}}</h2> <p>{{post.section3UseCase.content[0].content[0].value}}</p> <blockquote> <p>{{post.blockquote1.content[0].content[0].value}}</p> </blockquote> <p><sub>{{post.section4Title}}</sub></p> <p>{{post.section4Facts.content[0].content[0].value}}</p> <a href="{{post.section4Facts.content[1].content[1].content[0].value}}">MDN library</a> <!-- <ul *ngIf="post.table !== undefined"> <li *ngFor="let li of post.table.content[0].content[1].content"> {{li.content[0].content[0].data.value}}</li> <li></li> <li></li> <li></li> <li></li> </ul> --> </div> </article> </div> <div class="col-md-4 col-lg-4"> <aside class="sidebar"> <div class="widget"> <h5 class="widget-title">About Me</h5> <div class="widget-content"> <a href="/about">Hello there! My name is Boyan, and I'm delighted to welcome you to my online development blog.</a> </div> </div> <div class="widget"> <h5 class="widget-title">Categories</h5> <div class="widget-content"> <ul class="list-unstyled"> <li><a href="#">Technology</a></li> <li><a href="#">Web Development</a></li> <li><a href="#">Design</a></li> <li><a href="#">Photography</a></li> <li><a href="#">Miscellaneous</a></li> </ul> </div> </div> <div class="widget"> <h5 class="widget-title">Tags</h5> <div class="widget-content"> <ul class="list-unstyled tag-cloud"> <li><a href="#">Bootstrap</a></li> <li><a href="#">HTML</a></li> <li><a href="#">CSS</a></li> <li><a href="#">JavaScript</a></li> <li><a href="#">Web Design</a></li> <li><a href="#">Development</a></li> </ul> </div> </div> </aside> </div> </div> </div>
一、数据规整实现方案
1. 组件内编写解析函数预处理数据
在组件中添加通用解析函数,提前把嵌套的Rich Text字段转换成易读的格式,避免模板里写多层索引:
// 提取Rich Text中的纯文本内容 getRichTextText(richText: any): string { if (!richText?.content?.length) return ''; return richText.content .map((block: any) => block.content .map((inline: any) => inline.content .map((node: any) => node.value).join('') ).join('') ).join('\n'); } // 提取Rich Text中的链接值 getRichTextLink(richText: any, blockIndex: number = 0, inlineIndex: number = 0): string { if (!richText?.content?.length) return ''; const targetNode = richText.content[blockIndex]?.content[inlineIndex]?.content[0]; return targetNode?.value || ''; }
然后在获取数据后直接规整:
client.getEntry(this.postId).then(entry => { const rawFields = entry.fields; // 生成规整后的post对象 this.post = { title: rawFields.title, imageUrl: rawFields.image?.fields.file.url, section1Title: rawFields.section1Title, description: this.getRichTextText(rawFields.description), section1Info: this.getRichTextText(rawFields.section1Info), section2Title: rawFields.section2Title, section2Experience: this.getRichTextText(rawFields.section2Experience), section3Title: rawFields.section3Title, section3UseCase: this.getRichTextText(rawFields.section3UseCase), blockquote1: this.getRichTextText(rawFields.blockquote1), section4Title: rawFields.section4Title, section4FactsText: this.getRichTextText(rawFields.section4Facts), section4FactsLink: this.getRichTextLink(rawFields.section4Facts, 1, 1) }; this.tags = entry.metadata.tags; }).catch(error => { console.log(error); });
模板里就可以直接用规整后的字段:
<p>{{post.description}}</p> <a href="{{post.section4FactsLink}}">MDN library</a>
2. 用Angular管道实现复用解析逻辑
如果多个组件需要处理Contentful数据,写一个自定义管道更高效:
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'contentfulRichText' }) export class ContentfulRichTextPipe implements PipeTransform { transform(richText: any, type: 'text' | 'link' = 'text', indices?: [number, number]): string { if (!richText?.content?.length) return ''; if (type === 'text') { return richText.content .map((block: any) => block.content .map((inline: any) => inline.content .map((node: any) => node.value).join('') ).join('') ).join('\n'); } if (type === 'link' && indices) { const [blockIdx, inlineIdx] = indices; const targetNode = richText.content[blockIdx]?.content[inlineIdx]?.content[0]; return targetNode?.value || ''; } return ''; } }
模板中直接调用管道:
<p>{{post.description | contentfulRichText}}</p> <a href="{{post.section4Facts | contentfulRichText:'link':[1,1]}}">MDN library</a>
3. 定义TypeScript类型增强代码可读性
给规整后的Post定义接口,避免any类型带来的不确定性:
interface FormattedPost { title: string; imageUrl: string; section1Title: string; description: string; section1Info: string; section2Title: string; section2Experience: string; section3Title: string; section3UseCase: string; blockquote1: string; section4Title: string; section4FactsText: string; section4FactsLink: string; } // 组件内声明post类型 post: FormattedPost | undefined;
二、实用技术资源
- Contentful Rich Text结构文档:虽然官网没有直接的规整方案,但可以参考Rich Text的节点层级说明(block、inline、text节点的关系),理解嵌套逻辑后更容易写解析函数。
- 官方渲染工具:使用
@contentful/rich-text-html-renderer包,直接将Rich Text转为HTML字符串,无需手动解析嵌套结构,安装后调用:
模板中用import { documentToHtmlString } from '@contentful/rich-text-html-renderer'; // 直接转换为HTML const descriptionHtml = documentToHtmlString(rawFields.description);[innerHTML]="descriptionHtml"渲染即可。
内容的提问来源于stack exchange,提问作者Boyan Iliev
相关产品推荐
相关产品推荐

