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

Is it possible to drive without nest.js?

I currently have a node-ts-express-setup that does not utilize nest.js. Unfortunately, the documentation and examples for drivine do not provide setup instructions without nest.js. Is there a way to use drivine without having to include nest as a dependen ...

Obtaining the NativeElement of a component in Angular 7 Jasmine unit tests

Within the HTML of my main component, there is a my-grid component present. Main.component.html: <my-grid id="myDataGrid" [config]="gridOptions" </my-grid> In main.component.specs.ts, how can I access the NativeElement of my-grid? Cu ...

Error Message: "Unable to locate module for Angular 5 UI Components packaging"

In the process of developing UI Components to be used in various web projects throughout the company, we are aiming to publish these components as an npm package on our local repository. It is crucial for us to include the sources for debugging purposes. F ...

Associative TypeScript Arrays

I'm attempting to organize reservations based on business ID in order to achieve a specific end result. Here is the desired output: [ [businessID1] => [Object1,Object2, Object3], [businessID2] => [Object1,Object2], [businessID3] => [Obje ...

What is the process of deploying Angular Server Side Rendering (SSR) build files to Azure Web App service using FileZilla FTP?

I'm currently working on Angular SSR and utilizing the Angular official sample project for testing purposes. Steps for building the Angular SSR project: Execute npm run build:ssr This will automatically generate the dist folder, which contains both ...

Using an aria-label attribute on an <option> tag within a dropdown menu may result in a DAP violation

Currently, I am conducting accessibility testing for an Angular project at my workplace. Our team relies on the JAWS screen reader and a helpful plugin that detects UI issues and highlights them as violations. Unfortunately, I've come across an issue ...

Is it possible to transfer files using Angular 2?

Currently, I am utilizing Angular2 and TypeScript to transmit a file in conjunction with JSON Data to a designated server. HTML Code <input type="file" class="form-control" name="avatar" id="uploadyour" name="uploadyour" #uploadyour="ngModel" [(ngMode ...

The BeanStub initialization failed due to a missing bean reference to frameworkOverrides

I recently updated my ag-grid version to 23.2.0 in my Angular 7 project. Everything was working fine, but when I tried filtering a page and then navigating away, I encountered the following error message: "unable to find bean reference frameworkOverrides ...

Refresh the mapbox source features in real-time

Currently, I am mapping out orders on a map with layers and symbols that have different statuses. When the status of an order changes, I want to update the color of the symbol accordingly. The layer configuration looks like this: map.addLayer({ id: &q ...

Informing Typescript that a variable has already been null-checked

An unusual yet structurally sound code snippet is presented below: function doFoo(food: string | null = null) { food = food ?? getFood(); // getFood: () => string | null if (!food) { throw Error("There's still no food :("); } plate[fo ...

The creation of fsm.WriteStream is invalid as it is not a recognized constructor

Can you help me with this issue? I am attempting to install @ng-idle/keepalive using the command npm install --save @ng-idle/core, but I encountered the following error: npm ERR! fsm.WriteStream is not a constructor npm ERR! Log files were not written due ...

Tips on how to properly format a DateTime String

I need help with formatting a DateTime string retrieved from an API where it is in the format of YYYY-MM-DDTHH:MM:SS +08:00 and I want to change it to DD-MM-YY HH:MM getDataFromApi(res) { this.timestamp = this.timestamp.items[0].timestamp; console ...

A guide on incorporating ngFor with the flipping card feature in Angular

I am attempting to use ngfor to create some flip cards. However, the cards are not starting a new line and are overlapping in the first line. I have a total of 4 cards, but the 4th card is overlapping with the 1st card. I believe this issue is related to t ...

Should we implement REST API with authentication?

I am seeking guidance on building an application from scratch and I have encountered some challenges. The plan is to create a front-end using Angular and a backend that will communicate via REST API. This application will be deployed on individual devices, ...

What is the best way to retrieve TemplateRef from an Angular component?

TS Component ngOnInit() { if(somecondition) // The line of code that is causing issues this.openModal(#tempName); } HTML Component <ng-template #tempName> Content goes here! </ng-template> this.openModal(#tempNa ...

Tips for obtaining the OneSignal playerID

When launching the app, I need to store the playerID once the user accepts notifications. This functionality is located within the initializeApp function in the app.component.ts file. While I am able to retrieve the playerID (verified through console.log) ...

How to transfer data between components in Angular 6 using a service

I'm facing an issue with passing data between the course-detail component and the course-play component. I tried using a shared service and BehaviorSubject, but it didn't work as expected. Strangely, there are no errors thrown, and the data remai ...

Removing outlines on <p> <a> or <div> elements with Ionic and Angular seems to be a challenging task

Hey there, I’m currently working on my application which includes a login page. I've implemented a “Forgotten password ?” link, but no matter what I try, like using .class and :focus {outline: none}, I still see a yellow square around the item. ...

Tips on organizing a typescript object/StringMap in reverse order to prioritize the last element

I've just started working with TS/React in a .tsx file and I'm trying to add a key/value pair to a StringMap at the very first index. Below is the code snippet that: takes the StringMap 'stats' as input, iterates through each row, re ...

Sorting complex strings in Typescript based on the dates contained within them

How can I sort a list of 2 strings with dates inside them so that the earlier one always comes first? The date is always after the second comma. For example, const example = ["AAA,5,2020-09-17T21:14:09.0545516Z", "AAA,0,2020-09-03T20:38:08. ...