Video
I made a short 15 second Youtube video demonstrating the bug since its rather hard to explain here is video: https://www.youtube.com/watch?v=YXykw2oSh8E --- please ignore my terrible editing skills
Short Summary
I will summarize the error here: If I have two image tags and I am trying to change the image of the lower one that is located lower in the html file, the image on the higher most img
tag will be changed as opposed to the one I selected even though the logic is in a seperate component instance
Code layout
In my code I have two <app-image-display>
components stacked on top of one another. When I try to update the image of the one with the name of coverPhoto
it will update the image in the one named profile
. If I move the coverPhoto
named instance higher in the html file than profile
named instance and try to change profile
it will update coverPhoto
.
Parent
I have a parent component that looks like this:
....
<div *ngIf="company">
<app-image-display name="profile" id="profile" #profile [image]="company.profileUrl" [uploadPath]='companyProfilePath' (imageUpdated)="updateImage($event, CompanyImage.PROFILE)"></app-image-display>
<app-image-display name="coverPhoto" id="coverPhoto" #coverPhoto [image]="company.coverPhotoUrl" [uploadPath]='companyCoverPath' (imageUpdated)="updateImage($event, CompanyImage.COVER_PHOTO_URL)"></app-image-display>
</div>
.....
You can see it contains two separate instances of the same component called ImageDisplay. One of the instances is designed to handle a profile image and the other a cover photo. I put in temporary logic to show me which on is the cover photo just to be explicit as seen in the video. This component is designed to show the use the previously saved image then allow them to view a new image (tempImage
) before saving it to the db. The ImageDisplayComponent looks like the following:
Image Display Html
<img [src]="image" width="200" *ngIf="!tempImage" height="200" class=" img-thumbnail" alt="Responsive
image" onerror="this.src = 'TEMP_IMG_URL_GOES_HERE'">
<img [src]="tempImage" *ngIf="tempImage" width="200" height="200" class=" img-thumbnail"
alt="Responsive image">
<span *ngIf="uploadPath.includes('cover')">Cover Image</span>
<app-image-picker *ngIf="!percentage" (imageSelected)="imageSelected($event) (tempImageSelected)="tempSelected($event)"></app-image-picker>
Image Display TS
And the .ts looks like this:
...
export class ImageDisplayComponent implements OnInit {
@Output() imageUpdated: EventEmitter<string> = new EventEmitter();
@Input() image: string = '';
@Input() uploadPath: string
tempImage: string;
percentage: Observable<number>;
selectedPictureFile: File;
task: AngularFireUploadTask;
constructor(
private dbs: AngularFireStorage,
private toastService: ToastService
) { }
ngOnInit(): void {
console.log(this.uploadPath)
}
imageSelected(image: File) {
this.selectedPictureFile = image;
this.fileUpload(this.uploadPath, this.selectedPictureFile);
}
async fileUpload(path: string, file: File): Promise<void> {
const ref = this.dbs.ref(path);
this.task = this.dbs.upload(path, file);
this.percentage = this.task.percentageChanges();
let imageUrl = '';
from(this.task).pipe(
switchMap(() => ref.getDownloadURL()),
map((img) => imageUrl = img),
finalize(() => delete this.percentage)
).subscribe(() => {
this.imageUpdated.emit(imageUrl)
},(error) => {
this.toastService.show(`${error.message}`, {
delay: 3000,
autohide: true
});
});
}
tempSelected(imageUrl: string) {
this.tempImage = imageUrl;
}
}
...
ImagePickerComponent Html
app-image-picker looks like the following:
<div style="margin: 5px">
<label for="file-upload" class="custom-file-upload">
{{labelText}}
</label>
<input type="file" id="file-upload" (change)="onPictureSelected($event)" accept=".png, .jpg">
<div class="row">
<button *ngIf="pictureSelected" class="btn btn-info m-1" [disabled]="!pictureSelected" (click)="uploadPicture()">
Save
</button>
<button *ngIf="pictureSelected" class="btn btn-warn m-1" [disabled]="!pictureSelected" (click)="cancel()">
Cancel
</button>
</div>
ImagePickerComponent TS
This is what the onPictureSelected function looks like:
// image selection and verification.
onPictureSelected(event) {
this.selectedPictureFile = event.target.files[0] as File;
const reader = new FileReader();
reader.readAsDataURL(this.selectedPictureFile);
if (!this.imageService.isImage(this.selectedPictureFile.name)) {
this.pictureSelected = false;
} else if (!this.imageService.isAllowedSize(this.selectedPictureFile.size)) {
this.pictureSelected = false;
} else {
// tslint:disable-next-line:no-shadowed-variable
reader.onloadend = (event: any) => {
if (event.target) {
this.selectedPictureURL = event.target.result;
this.tempImageSelected.emit(this.selectedPictureURL);
}
};
this.pictureSelected = true;
}
}
I am on Angular version 9.0.3