Guide to sorting through intricate structured Json information within Angular 6

I'm working on an Angular 6 App that involves handling complex structured JSON data with advanced filtering requirements.

JSON Data:

[{
    "StudentId": 1,
    "StudentName": "Student1",
    "Sex":"M",
    "Programs": [
        {
            "StudentId": 1,
            "ProgramName": "Java",
            "ProgramCategory": "Engineering",
            "ProgramStatus": "Full Time"
        },
        {
            "StudentId": 1,
            "ProgramName": "HR Management 2",
            "ProgramCategory": "HR",
            "ProgramStatus": "Part Time"
        },
        {
            "StudentId": 1,
            "ProgramName": "Accounting 1",
            "ProgramCategory": "Finance",
            "ProgramStatus": "Full Time"
        }
    ]
 },
...

Filter Options:

To achieve the desired results, I need to create 3 drop-down lists in my HTML page for filtering by:

... ... ...

Help:

I've been struggling to get the exact output I need. Any suggestions or modifications to my current code are greatly appreciated!

Thank you in advance!

Answer №1

Here is a solution I have that utilizes reactive forms and rxjs BehaviorSubjects:

Check out the full solution here

The core of the filtering problem you were facing can be found in this code snippet:

private setFilters() {
  // Implementation details...
}

Filtering for both ProgramCategory and ProgramStatus together requires a different approach compared to filtering them separately. In the provided stack blitz example, the filters are grouped into a FormGroup to achieve the desired behavior.

If you're interested, consider switching your table implementation to utilize @angular/cdk/table. This aligns well with the rxjs-centric method used in this solution and could enhance your application, especially if you appreciate this approach.

Answer №2

Customize your filters then execute the provided function with the corresponding values.

const individuals = [{
  "PersonId": 1,
  "Name": "Person1",
  "Gender": "Male",
  "Courses": [
    {
      "StudentId": 1,
      "CourseName": "Java",
      "CourseCategory": "Engineering",
      "CourseStatus": "Full Time"
    },
    {
      "StudentId": 1,
      "CourseName": "HR Management 2",
      "CourseCategory": "HR",
      "CourseStatus": "Part Time"
    },
    {
      "StudentId": 1,
      "CourseName": "Accounting 1",
      "CourseCategory": "Finance",
      "CourseStatus": "Full Time"
    }
  ]
},
{
  "PersonId": 2,
  "Name": "Person2",
  "Gender": "Female",
  "Courses": [
    {
      "StudentId": 2,
      "CourseName": "HR Management 1",
      "CourseCategory": "HR",
      "CourseStatus": "Part Time"
    },
    {
      "StudentId": 2,
      "CourseName": "Accounting 3",
      "CourseCategory": "Finance",
      "CourseStatus": "Full Time"
    }
  ]
},
{
  "PersonId": 3,
  "Name": "Person3",
  "Gender": "Female",
  "Courses": [
    {
      "StudentId": 3,
      "CourseName": "Java 3",
      "CourseCategory": "Engineering",
      "CourseStatus": "Full Time"
    }
  ]
},
{
  "PersonId": 4,
  "Name": "Person4",
  "Gender": "Male",
  "Courses": [
    {
      "StudentId": 4,
      "CourseName": "Java 2",
      "CourseCategory": "Engineering",
      "CourseStatus": "Full Time"
    },
    {
      "StudentId": 4,
      "CourseName": "Accounting 2",
      "CourseCategory": "Finance",
      "CourseStatus": "Part Time"
    }
  ]
},
{
  "PersonId": 5,
  "Name": "Person5",
  "Gender": "Male",
  "Courses": [
    {
      "StudentId": 5,
      "CourseName": "JavaScript",
      "CourseCategory": "Engineering",
      "CourseStatus": "Part Time"
    },
    {
      "StudentId": 5,
      "CourseName": "HR Management 5",
      "CourseCategory": "HR",
      "CourseStatus": "Full Time"
    }
  ]
}];

const filterSelectedStudents = (students, gender, category, status) => {
  const filteredStudents = students.filter(student => {
    // compare students to selected gender
    if (gender && student.gender !== gender) {
      return false;
    }

    // check if a student has the selected category
    if (category) {
      const hasCategory = student.Courses.find(course => course.CourseCategory === category);
      if (!hasCategory) {
        return false;
      }
    }

    // check if a student has the selected status
    if (status) {
      const hasStatus = student.Courses.find(course => course.CourseStatus === status);
      if (!hasStatus) {
        return false;
      }
    }

    return true;
  });

  return filteredStudents;
};

const chosenStudents = filterSelectedStudents(individuals, null, 'HR', 'Part Time');

chosenStudents.forEach(student => {
  console.log(student);
})

Answer №3

Since the key of this.filters[property] is constantly Programs, it leads to consistently overwriting the previous selection. Consequently, only the latest of the 2 sub-filters will be applied.

To address this issue, it is recommended to first check if a filter is already defined for this.filters[property]. If it is, ensure that it is also validated.

You can update your filterMatchSub method in the following way:

 filterMatchSub(property: string, childProperty: string, value: any) {
    let existing = (val) => true; // Create a function that always returns true
    // Store a reference to the existing filter if one is already defined
    if (this.filters[property]) {
      existing = this.filters[property];
    }

    // Invoke the existing function as well
    this.filters[property] = val => val.find( child => child[childProperty]  === value) && existing(val);
    this.setFilters();
  }

Feel free to explore a demo on StackBlitz here

Answer №4

My detailed perspective on the handling of this issue can be found here with a complete example on stackblitz.

Module:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  imports: [BrowserModule, FormsModule, ReactiveFormsModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

Component:

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { STUDENTS } from './students';

interface FilterFormValue {
  sex: string;
  category: string;
  status: string;
}

interface Program {
  studentId: number;
  programName: string;
  programCategory: string;
  programStatus: string;
}

export interface Student {
  studentId: number;
  studentName: string;
  sex: string;
  programs: Array<Program>;
}

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  students: Array<Student> = [];
  filteredStudents: Array<Student> = [];

  sexOptions: Array<string> = [];
  programCategoryOptions: Array<string> = [];
  programStatusOptions: Array<string> = [];

  filterForm: FormGroup;

  constructor(private formBuilder: FormBuilder) { }

  ngOnInit() {
    this.getStudents();
  }

  private getStudents() {
    // Content of this method is simulated data initialization for demonstration purposes.
    // In real scenario, students data would be fetched from an API.
    
    this.students = STUDENTS;
    this.filteredStudents = this.students;
    
    this.getSexOptions();
    this.getProgramCategoryOptions();
    this.getProgramStatusOptions();

    this.initFilterForm();
  }

  // Other methods and functionalities as described in the original code remain unchanged.

}

HTML:

<div class="row">
  <div class="col-sm-12">
    <div class="panel panel-sm ">
      <div class="panel-body">
        <h5>Basic Info</h5>
        <div class="hs-lead">

          <form [formGroup]="filterForm">

            <!-- Form content as per original code -->

          </form>

        </div>
      </div>
    </div>
  </div>
</div>
<div class="row">
  <div class="col-sm-12">
    <div class="panel panel-xl">
      <div class="panel-body">
        <h5>Result
          <span class="badge badge-info badge-pill pull-right">{{ filteredStudents.length }}</span>
        </h5>
        <div class="hs-lead">
          <div class="table-responsive">
            <table class="table table-hover">
              <thead>
                <tr>
                  <th>#</th>
                  <th>Name</th>
                  <th>Sex</th>
                  <th>Programs</th>
                </tr>
              </thead>
              <tbody>
                <tr *ngFor="let student of filteredStudents">
                  <td>{{ student.studentId }}</td>
                  <td>{{ student.studentName }}</td>
                  <td>{{ student.sex }}</td>
                  <td>
                    {{ student.programs.length }}
                    <ol *ngFor="let program of student.programs">
                      <li>{{ program.programCategory }} / {{ program.programStatus }}</li>
                    </ol>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

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

What is the best way to invoke a function in a React component from another component?

I am working with a component structure that includes the Input component. In this component, there is a function called validate that is triggered by the onChange event. Here is a snippet of the code: https://i.sstatic.net/WjCLy.png import React, {FC, us ...

Troubleshooting problem with fullscreen mode in videojs on Ionic 2 due to StatusBar issue

Utilizing a Component to run video js in an ionic application I am seeking for the application to cover the status bar when clicking fullscreen on the video The code functions only when placed in the 'constructor' section, but fails to work w ...

Datatable - pagination - retrieving data from a specific page

Can someone tell me how to retrieve the records from a specific page in a PrimeNG datatable using Angular 2.0? ...

best practices for filtering custom data returned from valuePreparefunction in ng2 smart table

Having an issue with the filter in ng2 smart table because I am returning custom data from the valueprepareFunction. This is my setup... columns: { id: { title: 'Id', type: 'string' }, surname: { title: 'surname', ty ...

Unable to load the default value for ion-select in TypeScript

I have reviewed this question, this question, and this question. However, none of them seem to provide a straightforward solution for what I am searching for. <ion-list> <ion-item> <ion-label>Select Book</ion-label> <i ...

In order to launch an Angular project

Currently, I am in the process of creating a VSS web extension using Angular... To generate a .vsix file, I need to reference an HTML file. The challenge arises when working with Angular because we typically use ng serve which loads our page at http://lo ...

Differences Between APP_INITIALIZER and platformBrowserDynamic with provide

I've discovered two different approaches for delaying an Angular bootstrap until a Promise or Observable is resolved. One method involves using APP_INITIALIZER: { provide: APP_INITIALIZER, useFactory: (configService: ConfigurationService) => ( ...

What is the best way to save a JSON stream to a file in Node.js?

I am facing an issue with writing a complete JSON object to a file as it is received. Currently, I am using const file = fs.createWriteStream('./example.file'); var inputStream = JSON.parse(generatedData); fs.write(inputStream+"\n"); The d ...

Using TypeScript to create a unique object type that is mapped with generics for each key

Suppose I have a type representing an array of two items: a list of parameters and a function with arguments corresponding to the parameters. I've created a handy function to infer the generic type for easy use. type MapParamsToFunction<A extends a ...

What is the best way to import and export modules in Node.js when the module names and directories are given as strings?

Here is the structure of my folder: modules module-and index.js module-not index.js module-or index.js module-xor index.js moduleBundler.js The file I'm currently working on, moduleBundler.js, is re ...

When attempting to import a component from an external library in React/Next, you may run into an error that states: "Element type is invalid: Expected a string or

I am currently working on developing a React components library that can be seamlessly integrated into a NextJs project. The library includes a versatile "CmsBlock" component which takes a type and data as inputs to generate either a Paragraph or ZigZag co ...

Creating sophisticated TypeScript AngularJS directive

Recently, I came across a directive for selecting objects from checkboxes which can be found at this link: The issue I'm facing is that we are using TypeScript and I am unsure of how to implement the directive in TypeScript. From what I understand, ...

Can we combine two arrays of objects based on their unique identifiers?

I am working with two arrays of objects: firstAry = [{ "status": "Creating", "datacenter-id": "1test", "datacenter-name": "1name" }, { "status": "Creating", ...

Using Angular CLI to enhance Angular 2 with Material Design elements

Up until now, I have been using the Angular 2 quickstart to kick off new projects. However, I recently made the decision to switch over to using the Angular 2 CLI and created a whole new project through it. I had to transfer all my files and re-install ...

Regular Expressions: Strategies for ensuring a secure password that meets specific criteria

Struggling to craft a regex for Angular Validators pattern on a password field with specific criteria: Minimum of 2 uppercase letters Minimum of 2 digits At least 1 special character. Currently able to validate each requirement individually (1 uppercase ...

What is the best way to implement an Angular Guard that utilizes an API service for validation and redirects in case of failure?

Hello there! I am currently working on an Angular 7 application that deals with time cards. One of the main features I have implemented is a CanActivate Guard for controlling access to certain components. The CanActivate code utilizes Observables to decid ...

Typescript: defining index signatures with numerical types in the range of 1 to 3

type X = {[K in '1' | '2']: string} // valid type Y = {[K in 1 | 2]: string} // invalid https://i.sstatic.net/8iBoK.png Could there be a legitimate explanation for this inconsistency? I couldn't find any related problem on github ...

Tips for virtualizing the choices in React-Select?

I've been attempting to integrate virtualization into a React-Select component, but so far, I have not been successful. Every virtualization package I have tried has presented some kind of critical issue that I haven't been able to resolve, speci ...

Access information from government websites by filling out a form or connecting to their API

I'm having trouble figuring out how our local government website operates. Specifically, when I enter the URL When I start entering a postcode (not my own), like 'ME15 7HQ', a list of addresses appears without even submitting the form. If ...

Parsing JSON with JsonConvert.DeserializeObject is sensitive to casing

My goal is to deserialize a string content into an object, with the condition that the content must be case sensitive. The deserialization process should only pass if the string contains lower case properties, and fail if it contains upper case properties. ...