What is the best way to retrieve the properties and values of nested objects within JSON data?

The structure is as follows: A book consists of multiple fragments, each containing chapters that further include verses.

For fragments, the property of interest is the title. For verses, the properties of interest are verse number and verse text. (The data related to chapters is not relevant to the user).

Below are the models for the respective entities:

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;
    }
}

The main objective is to present the book's content to the user on a web page: beginning with the title of a fragment, followed by its verses, then moving on to the next fragment and its verses, and so forth.

Currently, the code in the "livre-detail.component.ts" component successfully retrieves the entire book content, including fragments and nested data down to the verse text, logging the JSON data correctly in the console or browser when using the template snippet below:

<div *ngFor= 'let fragment of fragments'>
  {{ fragment | json}}
</div>

In the template, looping through the fragments via *ngFor directive effectively displays the title of each fragment ("fragment.title_string").

However, I'm facing challenges when attempting a nested loop to display the text of each verse within each fragment.

I've experimented with various approaches:

  • Using Angular's keyvalue property as recommended in Angular2 - *ngFor / loop through json object with array

  • Creating a variable for the verses in the component file utilizing a nested map, as proposed in Angular2 nested *ngFor. The concept is similar to the one discussed at Angular2 nested *ngFor (refer to the code under Alternative 2)

Here is the current code implementation:

livre-detail-component.ts:

 // Component code here

livre-detail-component.html:

// Template code here

The error messages and issues encountered are detailed within the code comments above. Any assistance would be highly appreciated.

Answer №1

After numerous attempts, I finally managed to resolve the issue by implementing the following solution:

  1. To address the problem, I made a modification to the Fragment model in fragment.ts by converting Verse into an array. The line
public verses?: Verse;

was updated to

public verses?: Verse[];
  1. Additionally, adjustments were made to the backend code (utilizing Django Rest Framework): I introduced the "fragments" field in the Chapter serializer and established a direct reference to the Fragment serializer. Consequently, I had to rearrange the order of the serializers in the serializer.py file, placing the Fragment serializer above the Chapter serializer since the Chapter serializer now referenced the FragmentSerializer class. This adjustment enabled the url associated with the ChapterList view (inherited from generics.ListAPIView) to return a nested JSON structure containing all fragments under each chapter, as well as all verses under each fragment - resulting in a double layer of nesting that was not present previously.
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. Subsequently, within the livre-detail.component.html file, I could now access every verse property using dot notation:
<div *ngFor= 'let chapter of chapters' >
  <span class="chapter_number">
    Chapter {{ 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. The final refined typescript file for livre-detail.component.ts is as follows:
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 {
        // Acquire the short form of the book from the current route.
        const routeParams = this.route.snapshot.paramMap;
        const bookDiminutiveFromRoute = String(routeParams.get('bookDiminutive'));
        console.log(bookDiminutiveFromRoute)

        // Retrieve the name of the displayed book 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;
          // Single book returned:
          this.livre = this.livres[0]
          this.livre_name_ordinal = this.livre.name_ordinal
          this.livre_name_text = this.livre.name_text
        });

        // List chapters belonging to the displayed book
        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
        });
      }
    }

If there are any suggestions for enhancing this approach, please feel free to share them!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Autocomplete employs a SQL "LIKE" query to retrieve all possible results

Here is the code I am using : var db = Database.Open("dbase"); var term = Request.Form["username"] + "%"; var sql = "SELECT * from Users where Username LIKE @0"; var result = db.Query(sql, term); var data = result.Select(p => new{label = p.username}); ...

PHP can be executed and accessed directly from JavaScript without the need for any form submission

While I have come across a lot of information on PHP post/get methods for transmitting data, I find myself stuck with an interesting issue that requires some assistance. My HTML page contains data in different inputs, and I run algorithms on the JavaScrip ...

Unending loop caused by nested subscriptions - Angular / RxJS

I am currently implementing a nested subscribe operation, although I am aware that it may not be the most efficient method. Here is an example of what I have: this.route.params.subscribe((params) => { this.order$ .getMa ...

Transform a CSV file into a JSON format using Python

I am having a successful conversion of my code. However, I encountered an issue when trying to import the JSON file into Firebase as it stated that the files are invalid. import csv import json csvfile = open('C:/Users/Senior/seaborn-data/Denver Dat ...

Error: Unable to import the function 'maketrans'

When I run my code, I keep encountering the error message "ImportError: cannot import name 'package name'". This issue occurs not only in this particular code snippet but also in any other code where I try to import a module. from string import ...

What steps are required to set up a proxy for my HTTP request?

As a beginner in http requests, I am currently exploring how to retrieve data from . Since they do not support CORS, they recommend using a proxy server to access their data. In my Angular project, I have created a service for the http request: import { ...

The data type 'AbstractControl | null' cannot be assigned to type 'FormGroup'

I am facing an issue with passing the [formGroup] to child components in Angular. The error message says Type 'AbstractControl | null' is not assignable to type 'FormGroup'. I have double-checked my conditions and initialization, but I ...

The installation of the NPM package for @progress/kendo-angular-buttons is unsuccessful

While attempting to install a new KendoUI component using the Node Package Manager, I encountered an error that is displayed below: For more information, you can refer to : http://www.telerik.com/kendo-angular-ui/getting-started/ https://i.sstatic.net/0E ...

Angular 9 rxjs - forkJoin now provides an array of Observables instead of an array of values when called

I'm currently facing a challenge with my code. Here is a simplified version of the issue: this.store.pipe( select(arr), switchMap(arr => { const data = arr.map(arrItem => this.dataService.getData(arrItem.id)); return forkJoin(...data ...

Tips for managing table scroll in a flexbox design

Check out the demo here I am working with a table that has an indefinite number of rows and columns. When the table has a small number of rows and columns, it adjusts its width according to the available page space minus the width of the sidebar. Everythi ...

Ways to identify whether JSON data-content is the same? (Calculating JSON checksum?)

How can I determine if the data-content of two JSON files/expressions is functionally equivalent? I want to compare them while ignoring syntax issues such as: All whitespace and newlines (except within strings). The order of object members. Equivalent U ...

Creating a specialized Angular pipe to filter arrays

My data includes arrays of Requirement and Product objects. Each requirement has a one-to-many relationship with products, meaning each requirement can have multiple associated products. By accessing the requirement.id value in a product object, we can det ...

Uncovering the parent key name and values of objects through recursive extraction using jq

In order to extract a list of all installed npm packages in a specific format, I need to parse the output generated by running the command npm ls --global --json. The desired format for the package information is as follows: $package;$version;js;$resolved ...

Error in Ionic2 TypeScript: 'google' and '$' names not found, despite Google Maps and jQuery functioning correctly

I am currently working on developing an ionic2 application using TypeScript. Within the index.html file, I have attempted to integrate jquery and the Google Maps JS API before cordova.js: <!-- Vendor --> <script src="https://maps.googleapis. ...

Changing the Value of an Input Element Dynamically in React: A Step-by-Step Guide

In a scenario where I have a component that takes an element, such as <input />, and I need to update its value programmatically after 15 seconds. Initially, I had the following approach in mind: const MyComponent = (myInput: JSX.Element) => { ...

Converting a Class Object to JSON in Python using EasyPost in Django

Creating a JsonResponse view in Django 1.7 using Python 2.7.9 to track requests made through the EasyPost API. Below is the code for the view: def TrackingTable(request, trackingnumber): easypost.api_key = 'xxxxxxxxxxxxxxxxxxx' tracker ...

The depth parameter for PHP's json_decode function appears to malfunction

One interesting feature of PHP's json_decode function is the "depth" parameter, which controls how deep the recursion goes. However, when running the code below: test = array( 'name' => 'sean', 'dob' => &ap ...

Trying to customize the appearance of vaadin-date-picker's text input

My goal is to make the input text turn red when the date picker is within an element with the class 'has-Error'. Below is an example of what I tried on my index.html page, but unfortunately it didn't work: <style is="custom-style"> ...

Is there a notable reduction in performance due to the use of the second

Is the presence of the second optional chaining causing any negative impact? let flag = somePotentialNullObj?.someNumProp > 0 && somePotentialNullObj?.someOtherProp; The second optional chaining is unnecessary as it works the same without it: ...

What is the correct way to invoke a method from a generic in TypeScript?

My goal is to develop a function that invokes a specific method on a generic object. Here's my initial attempt: type MethodName<S extends Object, M extends keyof S> = S[M] extends Function ? M : never const callMethod = <S, M extends keyof ...