Search code examples
angulartypescripthttpclient

Angular changeDetection not trigger


Im working in multiple file upload component so far this is what i have:

upload.html

<div class="upload-file-wrapper nx-padding-x-2m">
    <ng-container *ngIf="progressFileInfo.length > 0">
        <div class="file-uploaded nx-margin-bottom-m" *ngFor="let progressFile of progressFileInfo">
            <span class="icon-document mpt-icon"></span>
            <div class="file-upload-info nx-padding-x-s">
                <p>{{ progressFile.fileName }}</p>
                <mat-progress-bar *ngIf="progressFile.value != 0 || progressFile.value != 100" mode="determinate" [value]="progressFile.value"></mat-progress-bar>
            </div>
            <button mat-icon-button>
                <span class="icon-trash-can mpt-icon nx-margin-0"></span>
        </button>
        </div>
    </ng-container>
</div>

upload.ts

@Input() serviceId!: string;
@Input() planId!: string;
@Input() set FileSelected(files: FileList) {
    if (files) {
        this.upLoadFiles(files);
    }
}
readonly progressFileInfo: any[] = [];
private readonly _destroyed$ = new Subject<void>();
constructor(
    private readonly resourceManagementService: ResourceManagementService,
    private readonly servicePlanService: ServicePlansService,
    private readonly cd: ChangeDetectorRef,
) {}

ngOnInit() {}

upLoad(index: number, file: File): void {
    this.progressFileInfo[index] = { value: 0, fileName: file.name };
    this.resourceManagementService
        .uploadAdditionalDocument(this.serviceId, this.planId, file)
        .pipe(takeUntil(this._destroyed$))
        .subscribe(
            event => {
                if (event.type === HttpEventType.UploadProgress) {
                    this.progressFileInfo[index].value = Math.round((100 * event.loaded) / event.total);
                    console.log(this.progressFileInfo);
                    this.cd.markForCheck();
                }
            },
            error => {
                this.progressFileInfo[index].value = 0;
                this.progressFileInfo[index].fileName = `Error uploading file - ${this.progressFileInfo[index].fileName}`;
            },
        );
}

upLoadFiles(files: FileList): void {
    Array.from(files).forEach((file: File, index: number) => {
        this.upLoad(index, file);
    });
}

upload.service.ts

uploadAdditionalDocument(serviceId: string, planId: string, file: File): Observable<any> {
    const url = `${this.baseUrl}/${serviceId}/plan/${planId}/api-specification/document`;

    const body: FormData = new FormData();
    body.append('file', file, file.name);

    return this.http.post<{ fileId: string }>(url, body, {
        reportProgress: true,
        responseType: 'json',
        observe: 'events',
    });
}

the idea is to track the progress of each file uploaded and display the percentaje in the progress bar but for some reason the progress bar is alway at 100%, when there is and error in any of the request i change the name of the file to 'Error uploading file ${fileName}' and change the upload % to 0, but for some reason none of that is reflected on the view, so for some reason change detection is not trigger on the component, as you can see im using the ChangeDetectionRef to try to trigger the change detection manualy, but no change in the view, i forgot to say that the component is using the ChangeDetectionStrategy.OnPush.

enter image description here

Just try without mutation

            if (event.type === HttpEventType.UploadProgress) {
                const c = this.progressFileInfo[index];
                c.value = Math.round((100 * event.loaded) / event.total);
                Object.assign([], this.progressFileInfo, { [index]: c });
                console.log(this.progressFileInfo);
                this.cd.detectChanges();
            }

Solution

  • markForCheck does not trigger the change detection. It will simply mark the component to be checked next time when angular runs the change detection runs by default.

    Use detectChanges() to trigger the change detection manually instead of markForCheck.

    Also, since you are mutating the same object, Change detection will not run by default. Try changing the reference of the object progressFileInfo instead of mutating it.

    if (event.type === HttpEventType.UploadProgress) {
        const exist = this.progressFileInfo;
        exist[index] = { ...exist[index], value: Math.round((100 * event.loaded) / event.total) };
        this.progressFileInfo = [...exist]
        console.log(this.progressFileInfo);
        this.cd.detectChanges();
    }