From 590c9279a888c279e21a269880390430ed357332 Mon Sep 17 00:00:00 2001 From: Eitan Peer Date: Sun, 28 Jan 2018 11:41:50 +0200 Subject: [PATCH] Add onLoad and onError event emitters (#128) * Add onLoad and onError event emitters Emit events on onload and onerror of the underlying . Update photo_album sample. Fixes #81 --- .../js/photo-list/photo-list.component.html | 6 +++ .../app/js/photo-list/photo-list.component.ts | 9 +++- src/cloudinary-image.component.spec.ts | 47 ++++++++++++++++++- src/cloudinary-image.component.ts | 34 +++++++++----- src/cloudinary-video.component.ts | 2 +- 5 files changed, 84 insertions(+), 14 deletions(-) diff --git a/samples/photo_album/app/js/photo-list/photo-list.component.html b/samples/photo_album/app/js/photo-list/photo-list.component.html index 26f2c2fd..4bf6e4b4 100644 --- a/samples/photo_album/app/js/photo-list/photo-list.component.html +++ b/samples/photo_album/app/js/photo-list/photo-list.component.html @@ -11,10 +11,16 @@ [public-id]="publicId" type="facebook" angle="20" + (onLoad)="onLoadImage($event)" > + + diff --git a/samples/photo_album/app/js/photo-list/photo-list.component.ts b/samples/photo_album/app/js/photo-list/photo-list.component.ts index 87ce75b4..b7175440 100644 --- a/samples/photo_album/app/js/photo-list/photo-list.component.ts +++ b/samples/photo_album/app/js/photo-list/photo-list.component.ts @@ -22,6 +22,13 @@ export class PhotoListComponent implements OnInit { } changePublicId() { - this.publicId = (this.publicId === 'officialchucknorrispage') ? 'billclinton' : 'officialchucknorrispage'; + this.publicId = (this.publicId === 'officialchucknorrispage') ? 'billclinton' : 'officialchucknorrispage'; + } + + onLoadImage(success) { + console.log('On load', success); + } + onErrorImage(err) { + console.log('On error!!', err); } } diff --git a/src/cloudinary-image.component.spec.ts b/src/cloudinary-image.component.spec.ts index e789ea99..5ac8235b 100644 --- a/src/cloudinary-image.component.spec.ts +++ b/src/cloudinary-image.component.spec.ts @@ -220,6 +220,52 @@ describe('CloudinaryImage', () => { }); }); + describe('event emitters', () => { + let onImageLoad; + let onImageError; + let des: DebugElement; // the elements w/ the directive + + @Component({ + template: `` + }) + class TestComponent { + publicId: string = 'sample'; + } + + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + declarations: [CloudinaryTransformationDirective, CloudinaryImage, TestComponent], + providers: [{ provide: Cloudinary, useValue: localCloudinary }] + }).createComponent(TestComponent); + + fixture.detectChanges(); // initial binding + // all elements with an attached CloudinaryImage + des = fixture.debugElement.query(By.directive(CloudinaryImage)); + onImageLoad = jasmine.createSpy('onImageLoad'); + onImageError = jasmine.createSpy('onImageError'); + des.componentInstance.onLoad.subscribe(onImageLoad); + des.componentInstance.onError.subscribe(onImageError); + }); + + it('calls the onLoad callback when image loads successfully', () => { + // Simulate the load event + const img = des.children[0].nativeElement as HTMLImageElement; + img.dispatchEvent(new Event('load')); + expect(onImageLoad).toHaveBeenCalled(); + expect(onImageError).not.toHaveBeenCalled(); + }); + + it('calls the onError callback when image fails to load', () => { + // Simulate the error event + const img = des.children[0].nativeElement as HTMLImageElement; + img.dispatchEvent(new CustomEvent('error', { detail: 'Eitan was here' })); + expect(onImageLoad).not.toHaveBeenCalled(); + expect(onImageError).toHaveBeenCalled(); + }); + }); + describe('responsive images with nested transformations using the cld-responsive attribute', () => { @Component({ template: ` @@ -254,5 +300,4 @@ describe('CloudinaryImage', () => { /c_scale,l_text:roboto_25_bold:SDK,w_300\/e_art:hokusai\/f_auto\/responsive_sample.jpg/)); }); }); - }); diff --git a/src/cloudinary-image.component.ts b/src/cloudinary-image.component.ts index 6cc1c4f8..70e22c2f 100644 --- a/src/cloudinary-image.component.ts +++ b/src/cloudinary-image.component.ts @@ -1,14 +1,16 @@ import { - Component, - ElementRef, - Input, - ContentChildren, - QueryList, - AfterViewInit, - OnInit, - OnChanges, - SimpleChanges, - OnDestroy, + Component, + ElementRef, + EventEmitter, + Input, + Output, + ContentChildren, + QueryList, + AfterViewInit, + OnInit, + OnChanges, + SimpleChanges, + OnDestroy } from '@angular/core'; import { Cloudinary } from './cloudinary.service'; import { CloudinaryTransformationDirective } from './cloudinary-transformation.directive'; @@ -24,6 +26,9 @@ export class CloudinaryImage @ContentChildren(CloudinaryTransformationDirective) transformations: QueryList; + @Output() onLoad: EventEmitter = new EventEmitter(); // Callback when an image is loaded successfully + @Output() onError: EventEmitter = new EventEmitter(); // Callback when an image is loaded with error + observer: MutationObserver; constructor(private el: ElementRef, private cloudinary: Cloudinary) {} @@ -55,7 +60,7 @@ export class CloudinaryImage } ngOnDestroy(): void { - if (this.observer) { + if (this.observer && this.observer.disconnect) { this.observer.disconnect(); } } @@ -75,6 +80,13 @@ export class CloudinaryImage } const nativeElement = this.el.nativeElement; const image = nativeElement.children[0]; + // Add onload and onerror handlers + image.onload = e => { + this.onLoad.emit(e); + } + image.onerror = e => { + this.onError.emit(e); + } const options = this.cloudinary.toCloudinaryAttributes( nativeElement.attributes, this.transformations diff --git a/src/cloudinary-video.component.ts b/src/cloudinary-video.component.ts index c11b7b2f..9efa139c 100644 --- a/src/cloudinary-video.component.ts +++ b/src/cloudinary-video.component.ts @@ -55,7 +55,7 @@ export class CloudinaryVideo } ngOnDestroy(): void { - if (this.observer) { + if (this.observer && this.observer.disconnect) { this.observer.disconnect(); } }