forked from cloudinary/cloudinary_angular
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcloudinary-image.component.ts
More file actions
148 lines (132 loc) · 4.74 KB
/
Copy pathcloudinary-image.component.ts
File metadata and controls
148 lines (132 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import {
Component,
ElementRef,
EventEmitter,
Input,
Output,
ContentChildren,
QueryList,
AfterViewInit,
OnInit,
OnChanges,
SimpleChanges,
OnDestroy,
ContentChild,
} from '@angular/core';
import { Cloudinary } from './cloudinary.service';
import { CloudinaryTransformationDirective } from './cloudinary-transformation.directive';
import { CloudinaryPlaceHolder } from './cloudinary-placeholder.component';
import { isBrowser } from './cloudinary.service';
@Component({
selector: 'cl-image',
template: `<img [style.display]="shouldShowPlaceHolder ? 'none' : 'inline'" (load)="hasLoaded()">
<div [style.display]="shouldShowPlaceHolder ? 'inline' : 'none'">
<ng-content></ng-content>
</div>
`,
})
export class CloudinaryImage
implements AfterViewInit, OnInit, AfterViewInit, OnChanges, OnDestroy {
@Input('public-id') publicId: string;
@Input('client-hints') clientHints?: boolean;
@Input('loading') loading: string;
@Input('width') width?: string;
@Input('height') height?: string;
@ContentChildren(CloudinaryTransformationDirective)
transformations: QueryList<CloudinaryTransformationDirective>;
@ContentChild(CloudinaryPlaceHolder) placeholderComponent: CloudinaryPlaceHolder;
@Output() onLoad: EventEmitter<boolean> = new EventEmitter(); // Callback when an image is loaded successfully
@Output() onError: EventEmitter<boolean> = new EventEmitter(); // Callback when an image is loaded with error
observer: MutationObserver;
shouldShowPlaceHolder: boolean = true;
constructor(private el: ElementRef, private cloudinary: Cloudinary) {}
ngOnInit(): void {
if (this.width && this.placeholderComponent) {
this.placeholderComponent.setWidth(this.width);
}
if (this.height && this.placeholderComponent) {
this.placeholderComponent.setHeight(this.height);
}
if (this.placeholderComponent) {
this.placeholderComponent.setPublicId(this.publicId);
}
if (isBrowser()) {
// Create an observer instance
this.observer = new MutationObserver(() => {
this.loadImage();
});
// Observe changes to attributes or child transformations to re-render the image
const config = { attributes: true, childList: true };
// pass in the target node, as well as the observer options
this.observer.observe(this.el.nativeElement, config);
}
}
ngOnChanges(changes: SimpleChanges) {
// Listen to changes on the data-bound property 'publicId'.
// Update component unless this is the first value assigned.
if (changes.publicId && !changes.publicId.isFirstChange()) {
this.loadImage();
}
}
ngOnDestroy(): void {
if (this.observer && this.observer.disconnect) {
this.observer.disconnect();
}
}
ngAfterViewInit() {
this.loadImage();
}
hasLoaded() {
this.shouldShowPlaceHolder = false;
}
loadImage() {
// https://github.com/angular/universal#universal-gotchas
// Fetch the image only for client side rendering by the browser
if (isBrowser()) {
if (!this.publicId) {
throw new Error(
'You must set the public id of the image to load, e.g. <cl-image public-id={{photo.public_id}}...></cl-image>'
);
}
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
);
if (this.clientHints || (typeof this.clientHints === 'undefined' && this.cloudinary.config().client_hints)) {
delete options['class'];
delete options['data-src'];
delete options['responsive'];
}
if (this.placeholderComponent) {
this.placeholderHandler(options);
}
const imageTag = this.cloudinary.imageTag(this.publicId, options);
this.setElementAttributes(image, imageTag.attributes());
if (options.responsive) {
this.cloudinary.responsive(image, options);
}
}
}
setElementAttributes(element, attributesLiteral) {
Object.keys(attributesLiteral).forEach(attrName => {
const attr = attrName === 'src' && this.loading === 'lazy' ? 'data-src' : attrName;
element.setAttribute(attr, attributesLiteral[attrName]);
});
}
placeholderHandler(options) {
const placeholderOptions = {};
Object.keys(options).forEach(name => {
placeholderOptions[name] = (name === 'width' && !options[name].startsWith('auto') || name === 'height') ? Math.floor(parseInt(options[name], 10) * 0.1) : options[name];
})
this.placeholderComponent.options = placeholderOptions;
}
}