Angular 9: Implementing a synchronous *ngFor loop within the HTML page

After receiving a list of subjects from the server, exercises are taken on each subject using the subject.id (from the server) and stored all together in the subEx variable. Classes are listed at the bottom.

  subjects:Subject[] 
  temp:Exercise[] = []
  subEx:SubjectExercise[] = []

  constructor(private exService: ExerciseService, private subService: SubjectService) { }

  ngOnInit(): void {
    this.getSubjects()
  }

  getSubjects(){
    let id = localStorage.getItem("id")
    this.subService.GetSubjectsByUserId(localStorage.getItem("id")).subscribe(
      res => {
        this.subjects = res
        for (const sub of this.subjects){
          this.exService.GetExercisesBySubject(sub.name).subscribe(
            res=>{
              this.temp = res
              this.subEx.push(new SubjectExercise(sub, this.temp))
              console.log(this.subEx)
            }
          )
        }
      },
    )
  }

Retrieve subjects and exercises through a simple http.get call

  GetExercisesBySubject(subName:string){
    return this.http.get<any>(this._getExercisesBySubject+"/"+subName)
  }

Then display all the subjects as buttons and add exercises as dropdown options to each button.

      <div class="list-group list-group-flush">
         <button *ngFor="let sub of subEx" type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
            {{sub.subject.name}}
            <div class="dropdown-menu">
               <a *ngFor="let ex of sub.exercises" class="dropdown-item">{{ex.title}}</a>
            </div>
         </button>
      </div>

The issue is that after every page reload, subjects have a different order and all subjects have the same random exercises. I suspect this is due to JavaScript's synchronous/asynchronous behavior.

So, how can I ensure that the page renders subjects in the same order and displays exercises individually for each subject?

Subject class

export class Subject
{
    constructor(id: string, name: string){
        this.Id = id
        this.name = name
    }
    Id: string
    name: string
}

Exercise class

import { Subject } from './Subject'

export class Exercise
{
    Id: string
    title: string
    content: string
    correctAnswer: string   
    subject: Subject 
}

SubjectExercise class

import { Subject } from './Subject';
import { Exercise } from './Exercise';

export class SubjectExercise{
    constructor(sub: Subject, ex: Exercise[]){
        this.subject = sub
        this.exercises = ex
    }
    subject: Subject
    exercises:Exercise[]
}

This is how subEx looks after calling getSubjects()

0: SubjectExercise
exercises:
   0: {id: "ea9bce1d-b6bf-471e-b1df-6edb253d68e7", title: "Task2", content: "string", correctAnswer: 
   "string", subject: {...}}
   1: {id: "89121437-d01d-461b-b0a1-92b2798bd66e", title: "Task1", content: "string", correctAnswer: 
   "string", subject: {...}}
   2: {id: "3643f5db-5271-4c70-abb8-cc4a2f00d1ae", title: "Task3", content: "string", correctAnswer: 
   "string", subject: {...}}
subject: {id: "d0d20a1e-8a75-4d56-0c67-08d892dcbbe6", name: "Math"}

1: SubjectExercise
   exercises: []
subject:{id: "b827616b-8ee3-4dee-0c69-08d892dcbbe6", name: "Econometrics"}

2: SubjectExercise
exercises:
   0: {id: "955a704a-00dc-4fe0-a953-5d8dd8ce4d35", title: "Task1", content: "string", correctAnswer: 
   "string", subject: {...}}
   1: {id: "fe0dd1a7-2ee1-4948-9145-864a0b07b506", title: "Task2", content: "string", correctAnswer: 
   "string", subject: {...}}
subject: {id: "aad23a48-f04e-4114-0c68-08d892dcbbe6", name: "GIS"}

Answer №1

If you want to create an observable for SubjectExcersise[] using RxJS operators and then use it in your template with the help of the async pipe, here's how you can do it:

Component Class:

subEx$: Observable<SubjectExercise[]> = new Observable([]);
getSubjects() {
  this.subEx$ = this.subService
    .GetSubjectsByUserId(localStorage.getItem('id'))
    .pipe(
      switchMap((subjects) =>
        combineLatest(
          ...subjects.map((subject) =>
            this.exService
              .GetExcesisesBySubject(subject.map)
              .pipe((res) => new SubjectExercise(subject, res))
          )
        )
      );
}

Component Template

<div class="list-group list-group-flush">
  <button *ngFor="let sub of subEx$ | async" type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
    {{sub.subject.name}}
    <div class="dropdown-menu">
        <a *ngFor="let ex of sub.excersises" class="dropdown-item">{{ex.title}}</a>
    </div>
  </button>
</div>

Give it a try and see if it works for you!

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

employing a findOne within a loop

Recently, I delved into the world of Mongoose and NodeJS. After completing a tutorial, I decided to embark on my own project. My project involves two Models: "Flat" (representing flat numbers) and "User" (encompassing users who own the Flats). var flatsSc ...

Even after setting the handler to return false, Angular continues to submit the form

In the following scenario, I have encountered an issue: Here is the HTML template snippet: <form [action]='endpoint' method="post" target="my_iframe" #confirmForm (ngSubmit)="submitConfirmation()"> <button type="submit" (click)="conf ...

Saving data outside of the Angular 5 subscribe method involves storing the information in a

I need to store data received from an HTTP GET request and save it into another array within the component. getAlerts(){ this.ChatAlertsService.getAlerts() .subscribe((data) => { console.log('ngOnInit', data); ...

Leveraging the power of Angular's function within ElementRef and accessing the

Is there a way to access an Angular function using ElementRef? ngAfterViewInit() { let _self = this; this.datatable.on('m-datatable--on-layout-updated', function(e){ $(_self.elRef.nativeElement).find('.deleteFn').c ...

Challenges with importing and using jspdf and autotable-jspdf in Angular 8

Issue with Generating PDF Using Angular 8, JSPDF, and JSPDF-AutoTable I am facing a challenge with exporting/generating a PDF based on an HTML grid. I need to make some DOM changes with CSS, remove toggle buttons, alter the header, etc. However, all the s ...

Error encountered while injecting Angular dependencies in component constructor

Here is my newly created component. I am looking to allow users to adjust the count variable. import { Component, Inject } from '@angular/core'; @Component({ selector: 'app-likebtn', templateUrl: './likebtn.component.html&apos ...

The ngfor loop seems to be caught in an endless cycle of continuously executing functions and

I am currently working on a sophisticated reporting solution. Essentially, I have created a table using an ngFor loop where I have implemented certain conditions that allow the user to view details of a clicked element by expanding and collapsing it. The ...

Applying the power of Angular function binding within .html() function in d3 visualizations

I am attempting to create a clickable d3 foreignObject span that triggers a function in the component TypeScript file. Below is a snippet of the code I have been working on: .append("foreignObject") .attr("x", x) .attr("y" ...

Creating a TypeScript interface where the type of one property is based on the type of another property

One of my Interfaces has the following structure: export enum SortValueType { String = 'string', Number = 'number', Date = 'date', } export interface SortConfig { key: string; direction: SortDirection; type: Sort ...

Differences between Typescript static methods and functions defined outside of classesWhen comparing Types

What distinguishes a static class method from a function defined outside a class in TypeScript, especially when visibility is not a concern? While there are differences in visibility from other classes and files, what factors should be considered when dec ...

Troubleshooting await and async functions in Express.js applications

How can I create an array like the one shown below? [{ "item":"item1", "subitem":[{"subitem1","subitem2"}] },{ "item":"item2", "subitem":[{"subitem3","subitem4&q ...

Angular components are persisting instead of being destroyed

When navigating to a new page in my Angular application, I've noticed that the component from the previous page remains in memory instead of being destroyed. This results in a new instance being created when I navigate back to that page. The applicat ...

Angular's queryParams do not appear to properly connect with the query parameters

My code seems to have a mistake somewhere, but I can't figure it out. In my [queryParams] = "{allowEdit: server.id == 3 ? 1 : 0}", the params object is empty when I subscribe to it in the edit-server component. Why is it empty and how do I a ...

Automatic generation of generic types in higher-order functions in TypeScript

function createGenerator<P extends object>(initialize: (params: P) => void) { return function (params: P): P { initialize(params) return params } } const gen = createGenerator(function exampleFunction<T>(param: T) { console.lo ...

Ways to verify the existence and non-empty status of a directory?

zip.loadAsync(files).then(function(directory:any){ if (directory.folder("Mary")){ console.log("fail"); } else{ directory.folder("Mary").forEach(function (filename: any) {Console.log(filename);}); }; } I am attem ...

Enrich TypeScript objects by incorporating additional properties beyond the ones already present

If I have an expression and want to add extra properties without repeating existing ones, how can I achieve that? For instance, if the expression is a variable, it's simple to include additional fields (like adding field e): const x = { a: 1 }; cons ...

What could be causing my D3.js stacked bar chart to show inaccurate results?

I'm encountering some challenges while creating a stacked bar chart in d3.js. The current appearance of my chart is depicted here (developed with D3.js): https://i.sstatic.net/G6UA6.png However, I aim to achieve a design similar to this one (crafted ...

Using RxJs: switchMap conditionally based on the emitted value being empty

Below is the code snippet I am currently dealing with: const id = 1; // id = 2 of([{id: 1, name: 'abc'}]).pipe( map(items => items.find(item => item.id === id)), switchMap(item => item ? of(item) : this.makeHttp ...

Obtaining a document using Angular and Spring MVC API

Having trouble downloading a file upon clicking a button using Angular. The downloaded file is showing as 0 KB in size and won't open. This is the code snippet I'm currently using with Angular and Spring MVC: Angular : public downloadFile(file ...

What is preventing type guarding in this particular instance for TypeScript?

Here is an example of some code I'm working on: type DataType = { name: string, age: number, } | { xy: [number, number] } function processInput(input: DataType) { if (input.name && input.age) { // Do something } e ...