如何访问嵌套在json数据对象的属性和值?



模型如下:一本书由几个片段组成,每个片段由章节组成,章节又由诗句组成。

对于片段,感兴趣的属性是标题。对诗来说,趣味属性是诗数和诗文。(用户对章节数据不感兴趣)

以下是相关模型的代码:

Fragments.ts:

import {Deserializable} from './deserializable';
import { Chapter } from './chapter';
import { Verse } from './verse';
export class Fragment implements Deserializable {
public id?: number;
public url?: string;
public surtitle?: string;
public title_string?: string;
public title_format?: number;
public verses?: Verse;
public chapter_id?: Chapter;
deserialize(input: any): this {
Object.assign(this, input);
return this;
}

chapters.ts:

import {Deserializable} from './deserializable';
import { Livre } from './livre';
export class Chapter implements Deserializable {
public id?: number;
public url?: string;
public number?: number;
public book_id?: Livre;
deserialize(input: any): this {
Object.assign(this, input);
return this;
}
}

verse.ts:

import {Deserializable} from './deserializable';
import { Fragment } from './fragment';
export class Verse implements Deserializable {
public id?: number;
public url?: string;
public number?: number;
public text?: string;
public optional_indication?: number;
public fragment_id?: Fragment;
deserialize(input: any): this {
Object.assign(this, input);
return this;
}
}

目标是在网页中向用户显示一本书的内容:首先是一个片段的标题,然后是它的诗句,然后是下一个片段的标题,然后是它的诗句等等。

目前,名为"livre-detail.component.ts"的相关组件中的代码获取了一本书的全部内容,包括片段和嵌套数据,直到每句诗的文本都是"这个片段"。并且JSON数据正确地记录在控制台中,或者当模板简单地执行

时,在浏览器中记录:
<div *ngFor= 'let fragment of fragments'>
{{ fragment | json}}
</div>

在模板中,当代码使用*ngFor指令循环遍历片段时,每个片段的标题就会被正确显示出来("fragment.title_string")。

但是我不能想出一个嵌套循环,导致在每个片段中显示每个节的文本。

我试过很多方法:

  • 使用Angular2中建议的Angular属性keyvalue - *ngFor/循环通过json对象与数组

  • 为组件文件中的元素创建一个变量,使用嵌套映射,就像Angular2中嵌套*ngFor中建议的那样在Angular2的代码中嵌套*ngFor(参见下面选项2中的代码)对我来说,片段就像色彩,诗句就像灯光。

这是我当前的代码:

livre-detail-component.ts:

import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable, from} from 'rxjs';
import {map} from 'rxjs/operators';
import { Fragment } from '../models/fragment';
import { Verse } from '../models/verse';
import {ResponseApi} from '../models/api';
import { FragmentService } from '../services/fragment.service';

@Component({
selector: 'app-livre-detail',
templateUrl: './livre-detail.component.html',
styleUrls: ['./livre-detail.component.scss']
})
export class LivreDetailComponent implements OnInit {
fragments$!: Observable<Fragment[]>;
fragment: Fragment | undefined;
fragments: Fragment[] | undefined;

verse: Verse | undefined;
//verses: Verse[] | undefined;
text: String | undefined;
// verseId: number | undefined;
constructor(
private route: ActivatedRoute,
private fragmentService: FragmentService,  
) { }
ngOnInit() {
// First get the book diminutive from the current route.
const routeParams = this.route.snapshot.paramMap;
const bookDiminutiveFromRoute = String(routeParams.get('bookDiminutive'));

// Find the fragments that belong to the book with the diminutive provided in route.
// Note: a fragment belongs to a chapter which in turn belongs to a route.
this.fragments$ = this.fragmentService.filterList(
'chapter_id__book_id__diminutive', bookDiminutiveFromRoute).pipe(
map((responseApi: ResponseApi<Fragment>) => {
console.log(responseApi.results)
return responseApi.results;
})
);
this.fragments$.subscribe((fragments: Fragment[]) => {
this.fragments = fragments;
console.log(this.fragments)
});
}

livre-detail-component.html:

<div *ngFor= 'let fragment of fragments'>
<h3>{{ fragment.title_string }}</h3>
{{fragment.verses}}
</div>

返回上述结果[object object],[object object],[object object],[object object],[object object],[object object],[object object],[object object],[object object],[object object];在每个片段标题下:

备选方案1:使用嵌套循环的模板,如下所示:

<div *ngFor= 'let fragment of fragments'>
<h3>{{ fragment.title_string }}</h3>
<div>
<div *ngFor= 'let verse of fragment.verses'>
{{ verse.text }}
</div>
</div>

返回以下错误消息:

"类型'Verse | undefined'不能赋值给类型'any[] | Iterable | (Iterable &Any []) | (Any [] &Iterable) | null | undefined

备选方案2:尝试在组件文件中嵌套映射:

import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable, from} from 'rxjs';
import {map} from 'rxjs/operators';
import { Fragment } from '../models/fragment';
import { Verse } from '../models/verse';
import {ResponseApi} from '../models/api';
import { FragmentService } from '../services/fragment.service';

@Component({
selector: 'app-livre-detail',
templateUrl: './livre-detail.component.html',
styleUrls: ['./livre-detail.component.scss']
})
export class LivreDetailComponent implements OnInit {
fragments$!: Observable<Fragment[]>;
fragment: Fragment | undefined;
fragments: Fragment[] | undefined;

verse: Verse | undefined;
verses: Verse[] | undefined;
text: String | undefined;
verseId: number | undefined;
constructor(
private route: ActivatedRoute,
private fragmentService: FragmentService,  
) { }
ngOnInit() {
// First get the book diminutive from the current route.
const routeParams = this.route.snapshot.paramMap;
const bookDiminutiveFromRoute = String(routeParams.get('bookDiminutive'));

// Find the fragments that belong to the book with the diminutive provided in route.
// Note: a fragment belongs to a chapter which in turn belongs to a route.
this.fragments$ = this.fragmentService.filterList(
'chapter_id__book_id__diminutive', bookDiminutiveFromRoute).pipe(
map((responseApi: ResponseApi<Fragment>) => {
console.log(responseApi.results)
return responseApi.results;
})
);
this.fragments = fragments.map((fragment: Fragment)=>{
let verseObjects = this.verses.map((verseId: number) =>{
return this.verses?.find((verse, index) => {return index === verseId})
});
fragment.verses = verseObjects;
return fragment.verses          
}); 
}

这会触发以下错误消息:

错误:src/app/livre-detail/livre-detail.component.ts:54:11 -错误TS2741:属性'deserialize'在类型'(Verse | undefined)[]'中缺失,但在类型'Verse'中需要。

54 片段。诗句=诗句对象;~~~~~~~~~~~~~~~

src/app/模型/verse.ts:十三3deserialize(input: any): this {~~~~~~~~~~~这里声明了'deserialize'。">

参考,诗句。它包括Verse的模型,如下所示:

import {Deserializable} from './deserializable';
import { Fragment } from './fragment';
export class Verse implements Deserializable {
public id?: number;
public url?: string;
public number?: number;
public text?: string;
public optional_indication?: number;
public fragment_id?: Fragment;
deserialize(input: any): this {
Object.assign(this, input);
return this;
}
}

同样供参考,可反序列化。

export interface Deserializable {
deserialize(input: any): this;
}

任何帮助都将是非常感激的。

最后,经过反复试验,我解决了如下问题:

  1. 我在Fragment中编辑了Fragment模型。是通过使Verse成为一个数组来实现的。行
public verses?: Verse;

已被

取代
public verses?: Verse[];
  1. 我还编辑了后端代码库(使用Django Rest框架):我添加了"fragments"到Chapter序列化器,以及对Fragment序列化器的显式引用。因此,我还必须在serializer.py文件中将Fragment序列化器移动到Chapter序列化器之上,因为现在Chapter序列化器引用的是FragmentSerializer类。这样,请求与ChapterList视图关联的url(继承了generics.ListAPIView)返回一个嵌套的json文件,不仅包括每个章节下的每个片段,还包括每个片段下的每个节,所以双重嵌套,这是以前没有的情况。
class FragmentSerializer(serializers.ModelSerializer):
"""
Fragment in a chapter
Returns chapter and book objects upward
as well as all verses downward (reverse relationship) 
"""
class Meta:
model = Fragment
depth = 2
fields = [
'id',
'chapter_id',
'surtitle',
'title_string',
'title_format',
'verses',
]

class ChapterSerializer(serializers.ModelSerializer):
"""
Chapters
"""
# https://www.django-rest-framework.org/api-guide/serializers/#dealing-with-nested-objects
fragments = FragmentSerializer(many=True)
class Meta:
model = Chapter
depth = 1 # allows access to the whole book object
fields = ['id', 'number', 'book_id', 'fragments',]
  1. 现在,在livre-detail.component.html文件中,每个verse属性都可以使用点符号来访问:
<div *ngFor= 'let chapter of chapters' >
<span class="chapter_number">
Chapitre {{ chapter.number }}
</span>
<p></p>
<div *ngFor= 'let fragment of chapter.fragments'>
<p>{{ fragment.title_string }}</p>
<div *ngFor= 'let verse of fragment.verses'>
{{ verse.number }}
{{ verse.text }}     
</div>
<p></p>          
</div>
</div>
  1. 最终干净的typescript文件livre-detail.component.ts为:
import { Component, OnInit, Input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable, from} from 'rxjs';
import {map} from 'rxjs/operators';
import { Livre } from '../models/livre';
import { Chapter } from '../models/chapter';
import { Fragment } from '../models/fragment';
import {ResponseApi} from '../models/api';
import { LivreService } from '../services/livre.service';
import { ChapterService} from '../services/chapter.service';

@Component({
selector: 'app-livre-detail',
templateUrl: './livre-detail.component.html',
styleUrls: ['./livre-detail.component.scss']
})
export class LivreDetailComponent implements OnInit {
livres$!: Observable<Livre[]>;
livres: Livre[] | undefined;
livre: Livre | undefined;
livre_name_ordinal: number | undefined;
livre_name_text: string | undefined;

chapters$!: Observable<Chapter[]>;
chapters: Chapter[] | undefined;
chapter: Chapter | undefined;
chapter_number_as_string: string | undefined;

fragments$!: Observable<Fragment[]>;
fragment: Fragment | undefined;
fragments: Fragment[] | undefined;
fragment_id: number | undefined;
fragment_id_as_string: string | undefined;
title_string: string | undefined;
constructor(
private route: ActivatedRoute,
private livreService: LivreService,
private chapterService: ChapterService,
) { }
ngOnInit(): void {
// First get the book diminutive from the current route.
const routeParams = this.route.snapshot.paramMap;
const bookDiminutiveFromRoute = String(routeParams.get('bookDiminutive'));
console.log(bookDiminutiveFromRoute)
// Get the name of the book whose detail is displayed on this page
this.livres$ = this.livreService.filterList(
'diminutive', bookDiminutiveFromRoute).pipe(
map((responseApi: ResponseApi<Livre>) => {
return responseApi.results;
}
)
);
this.livres$.subscribe((livres: Livre[]) => {
this.livres = livres;
// Actually a single book gets returned, so:
this.livre = this.livres[0]
this.livre_name_ordinal = this.livre.name_ordinal
this.livre_name_text = this.livre.name_text
});
// List chapters that belong to the book being displayed
this.chapters$ = this.chapterService.filterList(
'book_id__diminutive', bookDiminutiveFromRoute).pipe(
map((responseApi: ResponseApi<Livre>) => {
return responseApi.results;
}
)
);
this.chapters$.subscribe((chapters: Chapter[]) => {
this.chapters = chapters
});
}
}

很高兴听到任何改进解决方案的方法。

相关内容

  • 没有找到相关文章

最新更新