I've a file upload screen where users can click Choose File button and the files are supposed to display as a list. But if the file is too big or doesn't meet file type requirement then that file should display in a light red color with error message.
I've validation code in my .ts file but the error message shows for all the files even if the file doesn't have any error.
Here's my .ts file:
public validationError: { messages: string[] } = null;
public validateFiles(attachedFiles: AttachedFile[]) {
const extensions = this.accept && this.accept
.split(',')
.map(x => x.toLowerCase().trim())
.filter(x => !!x);
const maxSize = this.maxFileSizeMB * 1024 * 1024;
const errors = attachedFiles.map(file => {
if (extensions && !extensions.some(x => file.name.toLowerCase().endsWith(x))) {
return `File Type is not accepted`;
}
if (file.size > maxSize) {
return `Exceeds maximum file size of ${this.maxFileSizeMB}MB.`
}
return null;
}).filter(x => !!x);
if (errors.length) {
return {
messages: errors
};
}
return null;
}
And this is my html file. I know there's something I should do with validation.messages but how do I assign it to a particular file?
<ul *ngIf="attachedFiles && validationError" class="title-attached-files__list-with-errors">
<li *ngFor="let file of attachedFiles; let i = index" class="c-file-with-errors" >
<ng-container *ngIf="validationError">
<span class="c-title-attached-files__validation-error" *ngFor="let message of validationError.messages">
{{ message }}
</span>
</ng-container>
<mat-icon class="c-file__icon" color="primary">upload_file</mat-icon>
<span class="c-file__name">{{file.name}}</span>
<span class="c-file__size">{{file.size | fileSize }}</span>
<span *ngFor="let message of validationError.messages">
{{ message }}
</span>
<button *ngIf='!readonly' class="c-file__delete" type="button"
(click)="removeFile(i)"
mat-icon-button color="primary"
aria-label="Delete Added File">
<mat-icon>close</mat-icon>
</button>
</li>
</ul>
As this validationError
instance is shared among each file
in attachedFiles
array, you will see the error message is applied to each file
.
Would suggest that you should make the validationError
as an array (validationErrors
), and store the error instance for the file
individually.
validationErrors
instance with the type of array.public validationErrors: { messages: string[] }[] = null;
validateFiles
method to return the value of an array. If the file contains an error, the array will store an object in its position, otherwise it stores null
.public validateFiles(attachedFiles: AttachedFile[]) {
const extensions =
this.accept &&
this.accept
.split(',')
.map((x) => x.toLowerCase().trim())
.filter((x) => !!x);
const maxSize = this.maxFileSizeMB * 1024 * 1024;
return attachedFiles.map((file) => {
let errors: string[] = [];
if (
extensions &&
!extensions.some((x) => file.name.toLowerCase().endsWith(x))
) {
errors.push(`File Type is not accepted`);
}
if (file.size > maxSize) {
errors.push(`Exceeds maximum file size of ${this.maxFileSizeMB}MB.`);
}
if (errors.length > 0) {
return {
messages: errors,
};
}
return null;
});
}
validationErrors[i]
). With optional chaining ?.
to handle when the validationErrors[i]
is null
.<ul
*ngIf="attachedFiles && validationErrors"
class="title-attached-files__list-with-errors"
>
<li
*ngFor="let file of attachedFiles; let i = index"
class="c-file-with-errors"
>
<ng-container *ngIf="validationErrors">
<span
class="c-title-attached-files__validation-error"
*ngFor="let message of validationErrors[i]?.messages"
>
{{ message }}
</span>
</ng-container>
<mat-icon class="c-file__icon" color="primary">upload_file</mat-icon>
<span class="c-file__name">{{file.name}}</span>
<!--<span class="c-file__size">{{file.size | fileSize }}</span>-->
<span class="c-file__size">{{file.size }}</span>
<span *ngFor="let message of validationErrors[i]?.messages">
{{ message }}
</span>
<button
*ngIf="!readonly"
class="c-file__delete"
type="button"
(click)="removeFile(i)"
mat-icon-button
color="primary"
aria-label="Delete Added File"
>
<mat-icon>close</mat-icon>
</button>
</li>
</ul>