Enhancing the loading speed of hefty Angular components

The issue I encountered involved a large component that loads over 1000 data elements, populated by a service called only once. Initially, the service was being called each time the component initialized, which seemed to be causing performance problems. To address this, I created a shared service that is invoked only once in a grandparent component.

The structure of the components is hierarchical:

  • AddComponent (grandparent)
    • Type1AddComponent
      • BlockComponent (component with 1000+ data elements)
    • Type2AddComponent
      • BlockComponent
    • Type3AddComponent
      • BlockComponent

It's worth noting that the BlockComponent is utilized in all three TypeComponents. Despite a different title @Input, the displayed BlockComponent data remains the same across the three TypeComponents. While the initial loading of a TypeComponent works fine, switching between them results in load times exceeding 3 seconds, which seems unreasonable. Is there a way to optimize load times?

I understand there is extensive code shared below. If possible, I would create a Plunker for a webpack project.

shared-data.service.ts

import { Injectable } from '@angular/core';
import { DataResolver } from '../../blocking/data/data.resolver';
import { Data } from 'si-data-model';
import { Observable } from 'rxjs/Observable';
import { Logger } from 'ngx-data-utils';

@Injectable()
export class DataService {

  data: Data[] = []; // shared data
  dataObs$: Observable<Data[]>;
  logger: Logger;
  completed = false; // used to signal whether the service has completed to components

  constructor(private resolver: DataResolver,
              logger: Logger) {
    this.logger = logger;
  }

  ngOnInit() {
    this.logger.debug('Data Service initialized.');
  }

  ngOnDestroy() {
    this.logger.debug('Data Service destroyed.');
  }

  load() {
    return this.resolver.resolve(); // retrieves the data elements from a web service
  }

  initData() {
    this.dataObs$ = this.load();
    this.dataObs$.subscribe((res: Data[]) => {
      this.data = res;
      this.completed = true;
    });
  }

}

add.component.ts

import { Component, OnDestroy, OnInit } from '@angular/core';
import { DataService } from '../../shared/data/data.service';

@Component({
  selector: 'add',
  templateUrl: './add.component.html',
  styleUrls: ['./add.component.scss']
})
export class AddComponent implements OnInit, OnDestroy {

  radioUrl: string;

  constructor(private service: DataService) {
    console.log('Add component built');
  }

  ngOnInit() {
    this.service.initData();
    console.log('Add component initialized');
  }

  ngOnDestroy() {
    console.log('Add component destroyed');
  }

}

type1.component.ts

import { Component, OnDestroy, OnInit } from '@angular/core';

@Component({
  selector: 'type1',
  templateUrl: './type1.component.html',
  styleUrls: ['./type1.component.scss']
})
export class Type1Component implements OnInit, OnDestroy {

  title = 'Add Block Stuff';

  constructor() {
    console.log('Type1 component built');
  }

  ngOnInit() {
    console.log('Type1 component initialized');
  }

  ngOnDestroy() {
    console.log('Type1 component destroyed');
  }

  onType1Change($event: any) {
    console.log($event);
  }
}

block.component.ts

import { Component, OnDestroy, OnInit, Output, EventEmitter, Input } from '@angular/core';
import { Logger } from 'ngx-data-utils';
import { Observable } from 'rxjs/Observable';
import { Data } from 'si-data-model';
import { DataService } from '../../shared/data/data.service';

@Component({
  selector: 'block',
  templateUrl: './block.component.html',
  styleUrls: ['./block.component.scss']
})

export class BlockComponent implements OnInit, OnDestroy {

  dataLoaded = false;
  labels = ['Label1', 'Label2', 'Label3', 'Label4', 'Label5'];
  selected: any[] = [];
  data1: string;
  data2: string;
  data3: string;
  data4: string;
  data5: string;
  data6: string;
  datas: Data[] = [];
  @Output() change: EventEmitter<any> = new EventEmitter();
  @Input() title: string;
  @Input() data: Data[];

  // private criteriaCodes = [6, 2, 3, 11, 29, 25];

  constructor(private logger: Logger,
              private dataService: DataService) {
                // TODO
  }

  ngOnInit() {
    this.display();
    this.logger.debug('BlockComponent initialized.');
  }

  ngOnDestroy() {
    this.logger.debug('BlockComponent destroyed.');
  }

  initData () {
    this.dataService.data.forEach((dt: Data) => {
      this.datas.push(dt);
      this.dataLoaded = true;
    });
  }

  display() {
    if (this.dataService.completed) 
      this.initData();
  }

  propagateChange() {
    this.change.emit(this.selected); // doesn't do anything yet
  }

}

block.component.html

<div class="row" *ngIf="dataLoaded">
  <div class="row">
    <div class="col-md-4">
      <label>{{'DATA.BLOCK.LABEL1' | translate}}</label>
      <select class="custom-select form-control" [(ngModel)]="data1" (change)="propagateChange()">
        <option *ngFor="let c of data[0].value">{{c.code}} - {{c.description}}</option>
      </select>
    </div>
    <div class="col-md-4">
      <label>{{'DATA.BLOCK.LABEL2' | translate}}</label>
      <select class="custom-select form-control" [(ngModel)]="data2" (change)="propagateChange()">
        <option *ngFor="let mt of data[1].value">{{mt.code}} - {{mt.description}}</option>
      </select>
    </div>
    <div class="col-md-4">
      <label>{{'DATA.BLOCK.LABEL3' | translate}}</label>
      <select class="custom-select form-control" [(ngModel)]="data3" (change)="propagateChange()">
        <option *ngFor="let pem of data[2].value">{{pem.code}} - {{pem.description}}</option>
      </select>
    </div>
  </div>
  <div class="row">
    <div class="col-md-4">
      <label>{{'DATA.BLOCK.LABEL4' | translate}}</label>
      <select class="custom-select form-control" [(ngModel)]="data4" (change)="propagateChange()">
        <option *ngFor="let tt of data[3].value">{{tt.code}} - {{tt.description}}</option>
      </select>
    </div>
    <div class="col-md-4">
      <label>{{'DATA.BLOCK.LABEL5' | translate}}</label>
      <select class="custom-select form-control" [(ngModel)]="data5" (change)="propagateChange()">
        <option *ngFor="let cl of data[4].value">{{cl.code}} - {{cl.description}}</option>
      </select>
    </div>
    <div class="col-md-4">
      <label>{{'DATA.BLOCK.LABEL6' | translate}}</label>
      <input type="text">
    </div>
  </div>
</div>

Answer №1

It seems like your add.component.ts is fetching the data every time it gets initialized again?

Why not try this approach:

  initData() {
    if (!this.data) {
      this.dataObs$ = this.load();
      this.dataObs$.subscribe((res: Data[]) => {
        this.data = res;
        this.completed = true;
      });
    }
  }

This way, it will only fetch the data once and won't reload it.

As for the child components, it looks like one of them is iterating through EVERY element and creating its own copy of the data array?

  initData () {
    this.dataService.data.forEach((dt: Data) => {
      this.datas.push(dt);
      this.dataLoaded = true;
    });

Instead, consider using a getter method:

export class BlockComponent implements OnInit, OnDestroy {

  get datas(): Data[] {
      return this.dataService.data;
  }

  // ...

}

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

Sending template reference from one Angular component to another

I have a main grid component that includes smaller grid-item components. The majority of these grid items navigate to a specific route when clicked. However, there is one particular item that should open a modal window instead of navigating. Is there a wa ...

Utilizing checkboxes for toggling the visibility of buttons in Angular

I want to dynamically show or hide buttons based on a checkbox. Here is the HTML code I am using: <input class="form-check-input" [(ngModel)]="switchCase" type="checkbox" id="flexSwitchCheckChecked" (change)=" ...

Access the JSON data containing sub array values and showcase them on an HTML page by utilizing ngFor

Greetings! I am currently working on a web application where I need to showcase student data that is being received in JSON format. Below is the TypeScript code snippet that outlines the structure of the student data: export interface studentData{ ...

What is the best way to bring attention to a field that is outside the current viewport?

Is there a way to automatically scroll to the specific invalid field after clicking on submit in a large form, without having to manually search for it by scrolling through the page? ...

The URL dynamically updates as the Angular application loads on GitHub Pages

Encountering an unusual issue when trying to access my angular website on GitHub pages. The URL unexpectedly changes upon opening the page. Please check it out at this link: The original expected URL should be However, as the page loads, the URL gets alt ...

I tried implementing enums in my Angular Material select component, but unfortunately, it seems to be malfunctioning

Here is my TypeScript file I am working on creating a select list with enums in my project, but I am encountering an error. I have shared screenshots showing the enums with both keys and values, but I only want to display their keys and save their values ...

The 'Subscription' type does not contain the properties _isScalar, source, operator, lift, and several others that are found in the 'Observable<any>' type

Looking to retrieve data from two APIs in Angular 8. I have created a resolver like this: export class AccessLevelResolve implements Resolve<any>{ constructor(private accessLevel: AccessLevelService) { } resolve(route: ActivatedRouteSnapshot, sta ...

Guide on submitting a form through the Angular 2 HTTP post method with JavaScript

Currently working on grasping the concepts of Angular2, but facing challenges when attempting to utilize http.post() for submitting a form to my Web API. ...

Zod data structure featuring optional fields

Is there a more efficient way to define a Zod schema with nullable properties without repeating the nullable() method for each property? Currently, I have defined it as shown below: const MyObjectSchema = z .object({ p1: z.string().nullable(), p2 ...

Setting up roles and permissions for the admin user in Strapi v4 during the bootstrap process

This project is built using Typescript. To streamline the development process, all data needs to be bootstrapped in advance so that new team members working on the project do not have to manually insert data into the strapi admin panel. While inserting ne ...

Incorporate a JavaScript array into a TypeScript document

Having a file named array.js with a large collection of strings, structured like this: module.exports = ["word1","word2",...] I attempted to utilize this array in my validation.ts file by adding the following line: let wiki = require('./array.js&a ...

Embedding Globalize.js into an Angular component

Hey there! I'm currently working on building an Angular 4 application that needs to support L10n. I've decided to incorporate globalize into my project. Below is a snippet of my App component: import { Component, OnInit } from '@angular/c ...

Getting the id of a single object in a MatTable

I'm currently working on an angular 8 application where I've implemented angular material with MatTableDatasource. My goal is to retrieve the id from the selected object in my list: 0: {id: "e38e3a37-eda5-4010-d656-08d81c0f3353", family ...

When uploading from Angular 2, PHP fails to populate the $_POST and $_FILES variables

I'm encountering difficulties when trying to upload files and data to my server using Angular 2 and PHP. After following the instructions in File Upload In Angular 2? to upload data and files from Angular 2, everything appears to be functioning corre ...

Error code 1 in Ionic V5 Capacitor - cordova-plugin-media indicates a problem with media playback

Despite installing the plugin and ensuring all necessary permissions are set, I am still encountering error code 1 with the media plugin. I have also included <application android:requestLegacyExternalStorage="true" /> in <edit-config&g ...

Changing the state variable leads to an endless rendering cycle

I am working on creating a comment section using React. The component retrieves all comments and replies to each comment in one large dataset by performing an outer join, then it separates the results into two arrays (comment array and replies array). How ...

Tips on resolving issues with cellclickable functionality in Angular with gridster2

VERSION: ^9.3.3 HTML <button (click)="toggleEditing()">{ editing ? 'cancel' : 'editing' }</button> <button>ADD</button> <gridster [options]="options"> &l ...

Buttons for camera actions are superimposed on top of the preview of the capacitor camera

I am currently using the Capacitor CameraPreview Library to access the camera functions of the device. However, I have encountered a strange issue where the camera buttons overlap with the preview when exporting to an android device. This issue seems to on ...

Could Angular be automatically applying margins or padding to my component?

I'm encountering a minor issue.. I'm attempting to create a "Matrix" to construct a snake game in angular, but there seems to be an unremovable margin/padding. Below is my code: <!-- snake.component.html --> <div id="ng-snake-main&q ...

The value of type 'string' cannot be assigned to type '"menu" | "selectedMenu" | undefined' as it is not compatible with the specified types

I'm working on creating a multiple select feature using TypeScript, material-ui, and React. I am encountering an error when trying to set MenuProps.variant = 'menu'. The error message reads: "Type '{ variant: string; PaperProps: { styl ...