Browse Source

fix(iot-hub): resolve ExpressionChangedAfterItHasBeenCheckedError in device install dialog

Inject ChangeDetectorRef and call detectChanges() after async state
mutations (entity step status changes, stepper auto-advance, ngOnInit
completion). The NG0100 error occurred because async operations
(setTimeout, HTTP calls) mutated component state outside Angular's
change detection cycle.
pull/15347/head
Andrii Shvaika 6 months ago
parent
commit
5d1586fc4e
  1. 8
      ui-ngx/src/app/modules/home/pages/iot-hub/device-install-dialog/device-install-dialog.component.ts

8
ui-ngx/src/app/modules/home/pages/iot-hub/device-install-dialog/device-install-dialog.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, OnInit, ViewChild } from '@angular/core'; import { ChangeDetectorRef, Component, Inject, OnInit, ViewChild } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; import { UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { MatStepper } from '@angular/material/stepper'; import { MatStepper } from '@angular/material/stepper';
@ -98,6 +98,7 @@ export class TbDeviceInstallDialogComponent implements OnInit {
constructor( constructor(
@Inject(MAT_DIALOG_DATA) public data: DeviceInstallDialogData, @Inject(MAT_DIALOG_DATA) public data: DeviceInstallDialogData,
private dialogRef: MatDialogRef<TbDeviceInstallDialogComponent>, private dialogRef: MatDialogRef<TbDeviceInstallDialogComponent>,
private cdr: ChangeDetectorRef,
private deviceProfileService: DeviceProfileService, private deviceProfileService: DeviceProfileService,
private deviceService: DeviceService, private deviceService: DeviceService,
private dashboardService: DashboardService, private dashboardService: DashboardService,
@ -129,6 +130,7 @@ export class TbDeviceInstallDialogComponent implements OnInit {
console.error('Failed to parse device package ZIP', e); console.error('Failed to parse device package ZIP', e);
} }
this.loading = false; this.loading = false;
this.cdr.detectChanges();
} }
// --- Connectivity --- // --- Connectivity ---
@ -357,6 +359,7 @@ export class TbDeviceInstallDialogComponent implements OnInit {
} }
ep.status = 'running'; ep.status = 'running';
ep.errorMessage = null; ep.errorMessage = null;
this.cdr.detectChanges();
try { try {
const startTime = Date.now(); const startTime = Date.now();
const output = await this.createEntity(ep.step); const output = await this.createEntity(ep.step);
@ -384,10 +387,12 @@ export class TbDeviceInstallDialogComponent implements OnInit {
remaining.resolvedName = this.resolveVariables(remaining.step.name); remaining.resolvedName = this.resolveVariables(remaining.step.name);
} }
} }
this.cdr.detectChanges();
} catch (err: any) { } catch (err: any) {
ep.status = 'error'; ep.status = 'error';
ep.errorMessage = err?.error?.message || err?.message || 'Unknown error'; ep.errorMessage = err?.error?.message || err?.message || 'Unknown error';
ws.progressError = ep.errorMessage; ws.progressError = ep.errorMessage;
this.cdr.detectChanges();
return; return;
} }
} }
@ -414,6 +419,7 @@ export class TbDeviceInstallDialogComponent implements OnInit {
if (!this.isLastWizardStep) { if (!this.isLastWizardStep) {
await this.delay(500); await this.delay(500);
this.stepper.next(); this.stepper.next();
this.cdr.detectChanges();
this.onStepActivated(); this.onStepActivated();
} }
} }

Loading…
Cancel
Save