Skip to content

Commit 92e78af

Browse files
authored
Fix handling of dynamic updates to public-id. Fixes #83 (#94)
Implement OnChanges for image and video components to support data binding of "public-id" attrbiute. Update photo_album sample with usage example.
1 parent 5e4e681 commit 92e78af

File tree

6 files changed

+130
-16
lines changed

6 files changed

+130
-16
lines changed

samples/photo_album/app/js/photo-list/photo-list.component.html

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@
22
<div id="posterframe">
33
<!-- This will render the fetched Facebook profile picture using Cloudinary according to the
44
requested transformations. This also shows how to chain transformations -->
5-
<cl-image
6-
class="static-photo"
7-
responsive
8-
width="auto"
9-
crop="scale"
10-
public-id="officialchucknorrispage"
11-
type="facebook"
12-
angle="20"
13-
>
14-
<cl-transformation effect="art:hokusai"></cl-transformation>
15-
<cl-transformation border="3px_solid_rgb:00390b" radius="20"></cl-transformation>
16-
</cl-image>
17-
5+
<a (click)=changePublicId()>
6+
<cl-image
7+
class="static-photo"
8+
responsive
9+
width="auto"
10+
crop="scale"
11+
[public-id]="publicId"
12+
type="facebook"
13+
angle="20"
14+
>
15+
<cl-transformation effect="art:hokusai"></cl-transformation>
16+
<cl-transformation border="3px_solid_rgb:00390b" radius="20"></cl-transformation>
17+
</cl-image>
18+
</a>
1819
</div>
1920

2021
<h1 class="welcome">Welcome!</h1>
@@ -221,4 +222,4 @@ <h2 *ngIf="photo.context">{{photo.context.custom.photo}}</h2>
221222
supported transformations.
222223
</div>
223224
</div>
224-
</div>
225+
</div>

samples/photo_album/app/js/photo-list/photo-list.component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {Photo} from '../model/photo';
1111
export class PhotoListComponent implements OnInit {
1212

1313
private photos: Observable<Photo[]>;
14+
private publicId: string = 'officialchucknorrispage';
1415

1516
constructor(
1617
private photoAlbum: PhotoAlbum
@@ -19,4 +20,8 @@ export class PhotoListComponent implements OnInit {
1920
ngOnInit(): void {
2021
this.photos = this.photoAlbum.getPhotos();
2122
}
23+
24+
changePublicId() {
25+
this.publicId = (this.publicId === 'officialchucknorrispage') ? 'billclinton' : 'officialchucknorrispage';
26+
}
2227
}

src/cloudinary-image.component.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,5 +183,40 @@ describe('CloudinaryImage', () => {
183183
expect(img.attributes.getNamedItem('data-src')).toBeNull();
184184
});
185185
});
186+
187+
describe('Bound public-id', () => {
188+
@Component({
189+
template: `<cl-image id="image1" [public-id]="publicId"></cl-image>`
190+
})
191+
class TestComponent {
192+
publicId: string = 'sample';
193+
}
194+
195+
let fixture: ComponentFixture<TestComponent>;
196+
let des: DebugElement; // the elements w/ the directive
197+
198+
beforeEach(() => {
199+
fixture = TestBed.configureTestingModule({
200+
declarations: [CloudinaryTransformationDirective, CloudinaryImage, TestComponent],
201+
providers: [{ provide: Cloudinary, useValue: localCloudinary }]
202+
}).createComponent(TestComponent);
203+
204+
fixture.detectChanges(); // initial binding
205+
// all elements with an attached CloudinaryImage
206+
des = fixture.debugElement.query(By.directive(CloudinaryImage));
207+
});
208+
209+
it('creates an img element with a bound public-id', () => {
210+
const img = des.children[0].nativeElement as HTMLImageElement;
211+
expect(img.src).toEqual(jasmine.stringMatching(/image\/upload\/sample/));
212+
213+
// Update data-bound publicId
214+
fixture.componentInstance.publicId = 'updatedId';
215+
fixture.detectChanges();
216+
217+
// Verify that the img src has updated
218+
expect(img.src).toEqual(jasmine.stringMatching(/image\/upload\/updatedId/));
219+
});
220+
});
186221
});
187222

src/cloudinary-image.component.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
QueryList,
77
AfterViewInit,
88
OnInit,
9+
OnChanges,
10+
SimpleChanges,
911
OnDestroy
1012
} from '@angular/core';
1113
import {Cloudinary} from './cloudinary.service';
@@ -15,7 +17,7 @@ import {CloudinaryTransformationDirective} from './cloudinary-transformation.dir
1517
selector: 'cl-image',
1618
template: '<img>'
1719
})
18-
export class CloudinaryImage implements AfterViewInit, OnInit, OnDestroy {
20+
export class CloudinaryImage implements AfterViewInit, OnInit, OnChanges, OnDestroy {
1921

2022
@Input('public-id') publicId: string;
2123

@@ -39,6 +41,14 @@ export class CloudinaryImage implements AfterViewInit, OnInit, OnDestroy {
3941
this.observer.observe(this.el.nativeElement, config);
4042
}
4143

44+
ngOnChanges(changes: SimpleChanges) {
45+
// Listen to changes on the data-bound property 'publicId'.
46+
// Update component unless this is the first value assigned.
47+
if (changes.publicId && !changes.publicId.isFirstChange()) {
48+
this.loadImage();
49+
}
50+
}
51+
4252
ngOnDestroy(): void {
4353
this.observer.disconnect();
4454
}

src/cloudinary-video.component.spec.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,5 +225,58 @@ describe('CloudinaryVideo', () => {
225225
}
226226
});
227227
});
228+
229+
describe('Bound public-id', () => {
230+
@Component({
231+
template: `
232+
<cl-video cloud-name="my_other_cloud" [public-id]="publicId" secure="true" class="my-videos">
233+
<cl-transformation overlay="text:arial_60:watchme" gravity="north" y="20"></cl-transformation>
234+
</cl-video>
235+
`
236+
})
237+
class TestComponent {
238+
publicId: string = 'watchme';
239+
}
240+
241+
let fixture: ComponentFixture<TestComponent>;
242+
let des: DebugElement; // the elements w/ the directive
243+
244+
beforeEach(() => {
245+
fixture = TestBed.configureTestingModule({
246+
declarations: [CloudinaryTransformationDirective, CloudinaryVideo, TestComponent],
247+
providers: [{ provide: Cloudinary, useValue: localCloudinary }]
248+
}).createComponent(TestComponent);
249+
250+
fixture.detectChanges(); // initial binding
251+
252+
// Our element under test, which is attached to CloudinaryVideo
253+
des = fixture.debugElement.query(By.directive(CloudinaryVideo));
254+
});
255+
256+
it('creates a video element with a bound public-id', () => {
257+
const video = des.children[0].nativeElement as HTMLVideoElement;
258+
// Created <video> element should have 3 child <source> elements for mp4, webm, ogg
259+
expect(video.childElementCount).toBe(3);
260+
261+
const testMarkup = (id: string) => {
262+
for (let i = 0; i < 3; i++) {
263+
expect(video.children[i].attributes.getNamedItem('src')).toBeDefined();
264+
expect(video.children[i].attributes.getNamedItem('src').value).toEqual(
265+
jasmine.stringMatching
266+
(new RegExp(`https:\/\/res.cloudinary.com\/my_other_cloud\/video\/upload\/g_north,l_text:arial_60:watchme,y_20\/${id}`)));
267+
}
268+
};
269+
270+
// Check initial binding
271+
testMarkup('watchme');
272+
273+
// Update data-bound publicId
274+
fixture.componentInstance.publicId = 'updatedId';
275+
fixture.detectChanges();
276+
277+
// Verify that the video elememnt has updated
278+
testMarkup('updatedId');
279+
});
280+
});
228281
});
229282

src/cloudinary-video.component.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
QueryList,
77
AfterViewInit,
88
OnInit,
9+
OnChanges,
10+
SimpleChanges,
911
OnDestroy
1012
} from '@angular/core';
1113
import {Cloudinary} from './cloudinary.service';
@@ -16,7 +18,7 @@ import {CloudinaryTransformationDirective} from './cloudinary-transformation.dir
1618
template: '<video></video>'
1719
})
1820
// See also video reference - http://cloudinary.com/documentation/video_manipulation_and_delivery#video_transformations_reference
19-
export class CloudinaryVideo implements AfterViewInit, OnInit, OnDestroy {
21+
export class CloudinaryVideo implements AfterViewInit, OnInit, OnChanges, OnDestroy {
2022

2123
@Input('public-id') publicId: string;
2224

@@ -40,6 +42,14 @@ export class CloudinaryVideo implements AfterViewInit, OnInit, OnDestroy {
4042
this.observer.observe(this.el.nativeElement, config);
4143
}
4244

45+
ngOnChanges(changes: SimpleChanges) {
46+
// Listen to changes on the data-bound property 'publicId'.
47+
// Update component unless this is the first value assigned.
48+
if (changes.publicId && !changes.publicId.isFirstChange()) {
49+
this.loadVideo(changes.publicId.currentValue);
50+
}
51+
}
52+
4353
ngOnDestroy(): void {
4454
this.observer.disconnect();
4555
}

0 commit comments

Comments
 (0)