The information from the latest component instance will replace the data from all preceding instances

I am currently utilizing the @ViewChild decorator to instantiate new occurrences of a previously declared component. This process dynamically unfolds by employing a dialog window for data input upon form submission. While the creation of components is successful, I am encountering an issue where each subsequent instance's data overrides and displays on top of all preceding instances. Essentially, all components show data from the same origin instead of retaining their unique information. I am seeking a solution to prevent this behavior.

The container housing these components: container.component.ts

@Component({
  selector: 'container-component',
  templateUrl: 'container.component.html',
  styleUrls: ['container.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ContainerComponent implements OnInit, AfterViewInit {
  addressData: AddressAddition;
  loginData: any;
  loginAddress: string;
  index: number;

  @ViewChild("devicesStatusTemplate", { static: true, read: ViewContainerRef })
    devicesStatusComponent: ViewContainerRef;
    componentRef: ComponentRef<any>;

  constructor(
    public dialog: MatDialog,
    private authService: AuthService,
    private store: Store<AppState>
  ) { }

  async createDevicesStatus() {
    const devicesStatusComponentRef = this.devicesStatusComponent;

    const { DevicesStatusComponent } = await import('./devices-status/devices-status.component');
    this.componentRef = devicesStatusComponentRef.createComponent(DevicesStatusComponent);
    this.componentRef.changeDetectorRef.detectChanges();
    DevicesStatusComponent.counter++;
  }

  openDialog(): void {
    const dialogRef = this.dialog.open(AddressAdditionDialog, {
      width: '250px',
      data: AddressAddition
    });

    dialogRef.afterClosed().subscribe((res: AddressAddition) => {
      this.loginData = { userName: res?.username, password: res?.password };
      this.loginAddress = res?.address;
  
      if (!res) {
        return;
      }
  
      this.authService.login(this.loginAddress, this.loginData).subscribe(() => {
        this.createDevicesStatus();
      });
      this.store.dispatch(authLogin());
    });
  }
}

The container's template: container.component.html

<div class="multi-apis">
  <ng-template #devicesStatusTemplate></ng-template>
</div>

The replicable component: devices-status.component.ts

@Component({
  selector: 'devices-status-component',
  templateUrl: 'devices-status.component.html',
  styleUrls: ['devices-status.component.scss'],
  providers: [DevicesStatusService],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class DevicesStatusComponent implements OnInit {
  allStatusProps: number[] = [];
  allStatusPropsSub: Subscription;
  static counter: number = 0;
  index: number;

  constructor(
    private ref: ChangeDetectorRef,
    private devicesStatusService: DevicesStatusService
  ) { }
    

  ngOnInit() {
    this.getAllStatusPropsSubscribe();
  }

  getAllStatusPropsSubscribe() {
    this.index = DevicesStatusComponent.counter;
    this.allStatusPropsSub = timer(0, 5000)
        .pipe(
            exhaustMap((_) => this.devicesStatusService.getAllStatusProps())
        )
        .subscribe((response: number[]| any) => {
            this.allStatusProps = response;
            this.ref.detectChanges();
        });
  }
}

The component's template: devices-status.component.html

<div id="{{ index }}" class="api-container">
  <div class="api-bg">
    <div class="table-container">
      <gateways-status-count-component
        [allStatusProps]="allStatusProps"
      ></gateways-status-count-component>

      <lum-status-count-component
        [allStatusProps]="allStatusProps"
      ></lum-status-count-component>

      <extenders-status-count-component
        [allStatusProps]="allStatusProps"
      ></extenders-status-count-component>

      <wlessIO-status-count-component
        [allStatusProps]="allStatusProps"
      ></wlessIO-status-count-component>
    </div>
  </div>
</div>

The service handling the API call for the component: devices-status.service.ts

@Injectable({
  providedIn: 'any'
})
export class DevicesStatusService {
  index: number = 0;

  constructor(
    private apiHttpService: ApiHttpService,
    private apiEndpointsService: ApiEndpointsService,
    private authService: AuthService,
    private constants: Constants
  ) { }

  getAllStatusProps(): Observable<number[] | any> {
    this.index = DevicesStatusComponent.counter - 1;
    this.constants.API_ENDPOINT = this.constants.ENDPOINTS[this.index];
    return this.apiHttpService.get(<any>this.apiEndpointsService.getAllStatusProps(), this.authService.httpOptions(this.index));
  }
}

The concept was to capture user-inputted data from dialog windows and store them in arrays, specifically, authentication tokens and API addresses. Upon each dialog form submission, the goal is to save the address, undergo an internal login process to acquire the token, and store it alongside other relevant details. Utilizing a counter to track the number of created components allows me to access the stored data arrays.

Upon creating the initial instance of the DevicesStatusComponent, it accurately populates with the correct information. However, when generating additional instances, the subsequent component's data from a different API source supersedes the previous instance's data, resulting in both components displaying identical content.

While I could consolidate the code, I opted for keeping it organized by introducing a service to manage API calls. Although I am aware that there should be a method for each component to preserve its scope and data values autonomously, implementing this solution temporarily evades me.

I have experimented with possible solutions such as removing the service, omitting a provider for the service, and implementing switch cases to assign specific data to each component. Nonetheless, I am hesitant about creating a service for every individual component instance unless absolutely necessary. Any insights or suggestions would be greatly appreciated.

Answer №1

The issue with your current code is in the service implementation. It consistently retrieves a static index from the component class, causing it to return the same API for every component. To resolve this, you should pass the index as a parameter:

  getAllStatusProps(index: number): Observable<number[] | any> {
    this.constants.API_ENDPOINT = this.constants.ENDPOINTS[index];
    return this.apiHttpService.get(<any>this.apiEndpointsService.getAllStatusProps(), this.authService.httpOptions(index));
  }

This adjustment should address the current problem, although the complexity of this approach is unclear. A simpler method would involve creating an array with the required values and iterating through it using *ngFor. Here's an example:

@Component({
  selector: 'container-component',
  templateUrl: 'container.component.html',
  styleUrls: ['container.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ContainerComponent implements OnInit, AfterViewInit {
  // Your component properties and methods here
}
<div class="multi-apis">
  <ng-container *ngFor="let api of apiData">
    <devices-status-component
      [api]="api"
    ></devices-status-component>
  </ng-container>
</div>

You can then add an input to DeviceStatusComponent and pass all the necessary data through it.

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

The element id verification only functions with optional chaining, not with type checking

While attempting to apply a gradient to a line chart, I encountered the task of accessing its canvas element. I made sure to implement a typecheck before proceeding with the canvas manipulation. However, I received an error message from Vetur stating that ...

Issue with Angular: Global variable not updating within Subscription function

I'm encountering difficulties with updating a global variable in Angular 7 by using TypeScript. I am utilizing a service that retrieves JSON data from a database via a Restful API The service : export class myService { constructor(private client ...

Struggling to successfully deploy an Angular application on Azure

I'm currently in the process of deploying my Angular app to Azure. Utilizing VS Code and the "Azure App Service" extension. I have diligently followed this guide step by step: Upon completing the guide, I was successful in deploying the Angular app ...

Adding files to an Angular ViewModel using DropzoneJS

I am facing a challenge in extracting file content and inserting it into a specific FileViewModel. This is necessary because I need to bundle all files with MainViewModel which contains a list of FileViewModel before sending it from the client (angular) to ...

Using jQuery with Angular 4 allows for powerful front-end development

To include jQuery in an Angular4 project, I follow these steps: npm install --save jquery npm install --save-dev @types/jquery In the app.component.ts file import $ from 'jquery'; or import * as $ from 'jquery'; When running "ng se ...

Beautiful parentheses for Typescript constructors

I'm working on a project where I've installed prettier. However, I've noticed that it always reformats the code snippet below: constructor(public url: string) { } It changes it to: constructor(public url: string) {} Is there any way to sto ...

Incorporate service providers into models with Ionic3/Angular4

I am seeking feedback from individuals with more experience than me to determine if my approach is correct. I am currently working on an Ionic3-Angular app that involves a CRUD functionality for "Clientes". From what I have researched, the recommended st ...

Unable to transfer data through Ionic popover

I've encountered an issue when trying to pass data to my popover component, as the data doesn't seem to be sent successfully. Code HTML <div class="message" id="conversation" *ngFor="let message of messages.notes"> <ion-row class= ...

How to delete all markers on a Google map in Angular 6

<div #gmap class="map"></div> for (let marker of this.markersDisplay) { let markerColor = (marker.MarkerType == MarkerType.Ok) ? "green" : (marker.MarkerType == MarkerType.Warning) ? "yellow" : "red"; let markerClick = new ...

Unable to locate the specified environment variable in the current nest

Currently, I am referring to the official documentation on the NestJs website that provides a guide on using config files: https://docs.nestjs.com/techniques/configuration Below is the code snippet I am working with: app.module import { Module } from &ap ...

Following a docker run command, Docker undergoes an automatic shutdown process

I am currently working on an Angular 7 application and I'm looking to deploy it using Docker. I have created a Dockerfile in the root folder, but when I attempt to run Docker, it seems to terminate unexpectedly. Below is the content of my Dockerfile: ...

What is causing the ESLint error when trying to use an async function that returns a Promise?

In my Next.js application, I have defined an async function with Promise return and used it as an event handler for an HTML anchor element. However, when I try to run my code, ESLint throws the following error: "Promise-returning function provided t ...

Challenges encountered when retrieving parameters from union types in TypeScript

Why can't I access attributes in union types like this? export interface ICondition { field: string operator: string value: string } export interface IConditionGroup { conditions: ICondition[] group_operator: string } function foo(item: I ...

Tips on narrowing down the type of callback event depending on the specific event name

I've been working on implementing an event emitter, and the code is pretty straightforward. Currently, tsc is flagging the event type in eventHandler as 'ErrorEvent' | 'MessageEvent'. This seems to be causing some confusion, and I ...

Utilizing process.env in TypeScript can be a bit tricky as dot notation is not effective for accessing its properties

When I set my scripts to: "start": "NODE_ENV=development nodemon dist/Server.js", I am encountering an issue when trying to access NODE_ENV in my code. Both dot and bracket notation return undefined: The dependencies in my project are: "@types/node": "^8. ...

Utilizing Angular 2 Observable for showcasing a seamless flow of real-time information

Currently, my Angular 2 Web application is utilizing a Couchbase Server as its database. The data communication occurs through WebAPIs that interact with the CouchBase server. As of now, I am uncertain if this method is optimal, but I am constantly polling ...

Unlocking the secrets of achieving full-screen printing with jspdf and html2canvas

Check out the DEMO here Hey! I'm currently working with Angular8 in my project. I've integrated jspdf and html2canvas to convert HTML to PDF. However, I'm facing an issue where only half of the page is being printed instead of the full page ...

How do I send events from iOS (Swift) to JavaScript in React Native?

Currently, I am in the midst of a project where my goal is to seamlessly integrate React-Native into a native Swift application. To ensure that both sides are kept in sync with the state, I have implemented a 'message bus' - a mechanism designed ...

Troubleshooting: Angular 2 component directive malfunctioning

I am new to Angular 2 and I'm trying to get my first app up and running using TypeScript. I have the app.component.ts file where I created a directive to another component called todos.component, but I'm encountering this error during compilation ...

Link a YAML file with interfaces in JavaScript

I'm currently learning JavaScript and need to convert a YAML file to an Interface in JavaScript. Here is an example of the YAML file: - provider_name: SEA-AD consortiumn_name: SEA-AD defaults: thumbnail Donors: - id: "https://portal.brain ...