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.