Ensure that a function is performed only once upon the creation of a component

A new function has been implemented to manage the increment of two integers:

  • The initial variable, initialCount, should be increased by one only during the first rendering of the component. Even if the component is destroyed and re-rendered, it should not increment again.
  • The second variable, incrementalCount, will start at 1 and continue to increase as needed during the lifecycle of the component.

In summary, initialCount must be a shared value across all components and incremented only once per rendering, regardless of how many times a component is rendered. On the other hand, incrementalCount is specific to each component and increments independently from others.

To assist with understanding, a sample project can be accessed on StackBlitz. This project showcases three different components, where the desired outcome is to display 1.1 for the first component, 2.1 for the second component, and 3.1 for the third component. If another tag increments the inner counter, the sequence would follow accordingly (e.g., 1.2, 1.3, 1.4 for the first component). Any assistance in achieving this functionality would be greatly appreciated!

Answer №1

A convenient service is available that comprises two objects known as outerCounterMap and innerCounterMap. Their purpose is to store the counter values based on the component name.

The unique condition is that the outer counter can only be incremented once, specifically when its value is zero.

 incrementOuterCounter(className: string): void {
    if (!this.outerCounterMap[className]) {
      this.outerCounterMap[className] = 0;
    }
    if (this.outerCounterMap[className] === 0) { // update only once!
      this.internalInnerCounter++;
      this.outerCounterMap[className] = this.internalInnerCounter;
    }
  }

Now, by using this.constructor.name, we can extract the component name for tracking indexes in relation to this name. The maps of the service (outerCounterMap and innerCounterMap) will provide the desired output.

Complete Code:

one.component.ts

import { AfterViewInit, Component, OnDestroy } from '@angular/core';
import { CounterService } from '../../services/counter.service';

@Component({
  selector: 'app-one',
  standalone: true,
  imports: [],
  templateUrl: './one.component.html',
  styleUrl: './one.component.css',
})
export class OneComponent {
  className: string = this.constructor.name;
  label: string = '';
  constructor(public counter: CounterService) {
    this.counter.incrementOuterCounter(this.className);
    this.counter.incrementInnerCounter(this.className);
  }

  getLabel() {
    return (
      this.counter.getOuterCounter(this.className) +
      '.' +
      this.counter.getInnerCounter(this.className)
    );
  }
}

service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})
export class CounterService {
  private outerCounterMap: { [key: string]: number } = {};
  private innerCounterMap: { [key: string]: number } = {};
  internalInnerCounter = 0;
  constructor() {}

  getInnerCounter(className: string): number {
    return this.innerCounterMap[className] || 0;
  }

  incrementInnerCounter(className: string): void {
    if (!this.innerCounterMap[className]) {
      this.innerCounterMap[className] = 0;
    }
    this.innerCounterMap[className] = this.innerCounterMap[className] + 1;
  }

  getOuterCounter(className: string): number {
    return this.outerCounterMap[className] || 0;
  }
  incrementOuterCounter(className: string): void {
    if (!this.outerCounterMap[className]) {
      this.outerCounterMap[className] = 0;
    }
    if (this.outerCounterMap[className] === 0) {
      this.internalInnerCounter++;
      this.outerCounterMap[className] = this.internalInnerCounter;
    }
  }
}

See Stackblitz Demo

Answer №2

Here is a method that utilizes 2 service instances:

@Injectable({
    providedIn: "root"
})
export class Counter {

    private _counter = 0;

    increment() {
       this._counter++;
    }

    get counter(): number {
       return this._counter;
    }
}

This section shows how the components interact with the counters:


import { Counter } from './counter';
...

@Component({
    templateUrl: '....',
    standalone: true,
    imports[ ... ],
    providers: [
       Counter
    }
})
export class MyComponent {
    innerCounter = inject(Counter); // accessing the instance provided by this component
    outerCounter = inject(Counter, { skipSelf: true }); // using the instance provided in root

    ....
}

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

Troubleshooting problems with deploying an Angular app on Heroku

When I attempt to deploy my Angular app on Heroku, I am running into a 'Not found' error and getting additional information in the console ("Failed to load resource: the server responded with a status of 404"). Below is the entire Heroku build l ...

What steps can be taken to stop Internet Explorer from caching Ajax requests in Angular2 using TypeScript?

Imagine a situation where a user has 10 points. When they click a button, an ajax call is made to the server to update the user's points after they have been used. The server should send back 9 points, which is functioning correctly on all browsers ex ...

Arranging the properties of an object following the reduction process

I am currently working on replicating the functionality of an Outlook mailbox by organizing a list of Outlook emails based on their conversation ID. However, I am facing the challenge of needing to sort my list twice - once to order the emails in each grou ...

The data structure does not match the exact object type

Why isn't this code snippet functioning as expected? It seems that 'beta' has keys of type string and their values are compatible (id is a number type, and temp is also a number type). Additionally, the Record function should make the values ...

Presenting SQL information in a hierarchical Angular grid for easy visualization

As a newcomer to Angular, I have a requirement to display data in a multilevel/hierarchical Angular Grid. The data is retrieved from a SQL Database using a query with arguments provided in the where clause. Some questions that come to mind are: Is there ...

What is the correct syntax for declaring a variable within a switch statement in TypeScript?

How can I properly use a switch statement in TypeScript to assign a new variable a value? For example: let name: string switch(index) { case 0: name = "cat" case 1: name = "dog" .... } I keep getting the err ...

The element is inferred to have an 'any' type due to the fact that a 'string' type expression cannot be used to access elements in the type '{ Categories: Element; Admin: Element; }'

As someone who is new to TypeScript, I am trying to understand why I am encountering a type error in the code below (simplified): "use client"; import { ArrowDownWideNarrow, Settings } from "lucide-react"; interface LinkItemProps { ...

Error message from webpack: It appears you are missing a necessary loader to handle this specific file type

I'm struggling with building my server.ts typescript file for the backend. I have some imports, but my app is not building. Here is a snippet from my typescript file: import * as Express from 'express' import * as Session from 'expres ...

Implementing binding of JSON API responses to dropdown menus in Angular 4

In my current Angular 4 application, I am faced with the challenge of populating a dropdown menu with data from an API response. Specifically, I am struggling to retrieve the necessary information for each section from the API. The API provides data on C ...

Matching packages with mismatched @types in Webpack 2: A comprehensive guide

Having trouble implementing SoundJS (from the createJS framework) in my TypeScript project using webpack 2. In my vendors.ts file, I have the following import: import "soundjs"; Among other successful imports. The @types definitions installed via npm a ...

What is the most efficient way to retrieve child entity ids in TypeORM without having to fetch all the child entities?

I am working with two models: @Entity('user') class UserModel { @PrimaryColumn({ name: 'id', type: 'varchar', }) id: Ulid = generate() ... @OneToMany(() => CourseModel, (course) => c ...

Tips for displaying the string value of an elementFinder when encountering an error in protractor

I have the following code snippet: export async function waitTillClickable(e: ElementFinder): Promise<ElementFinder> { const conditions = EC.visibilityOf(e); await browser.wait(conditions, DEFAULT_TIMEOUT, `Element did not return ...

Using Fixed Patterns and Combining Types in an Interface

Presently, I am working with this interface: export interface User{ name: string birthday: number | Timestamp ... } When strictTemplates:false is enabled, I have no issue using this interface for server data retrieval with the birthday parameter in ...

Issue encountered while defining a component in Angular 16

Currently, I am in the process of learning Angular by following the tutorial provided on Angular.io. As part of this journey, I have declared a home component within my Angular application using the given code: ng generate component home --standalone --in ...

The specified "ID" type variable "$userId" is being utilized in a positional context that is anticipating a "non-null ID" type

When attempting to execute a GraphQL request using the npm package graphql-request, I am exploring the use of template literals. async getCandidate(userId: number) { const query = gql` query($userId: ID){ candidate( ...

Upgrade from Angular version 5.6 to 6.0 including the latest rxjs 6.0 update

My current application is built on Angular version 5.6, but I am considering upgrading to version 6.x for some reason. Is there a migration script available or any other approach to help with the upgrade without needing to make changes in the code or ref ...

Tips for implementing loading functionality with Interceptor in Angular version 17

I am attempting to implement an interceptor in the latest version of Angular 17. export const loadingInterceptor: HttpInterceptorFn = (req, next,) => { const loadingService = inject(LoadingService); loadingService.show(); return next(req).pipe( finaliz ...

Utilizing Fullcalendar 5 in conjunction with Angular: Embedding Components within Events

Recently, my team made the transition from AngularJS to Angular 12. With this change, I upgraded Fullcalendar from version 3 to version 5 and started using the Angular implementation of Fullcalendar: https://fullcalendar.io/docs/angular While navigating t ...

Top location for securely storing information in Angular 8

I have developed a web application using Angular 8. My goal is to secure routes and pages with dynamic access levels. For instance, I want to verify if a user has access to a specific route, and if not, redirect them to the login page. To do this, I cur ...

Setting the template url dynamically in Angular 2.0.0

My goal was to design a ContainerComponent that encompasses ng-content based on the value of the boolean variable, isModal. Unfortunately, my current approach is flawed as it only recognizes the last instance of <ng-content>. To remedy this issue, I ...