Reputation: 35703
I have to convert Image urls to base64 encoded strings (because of this)
import { Pipe, PipeTransform } from '@angular/core';
declare var base64;
@Pipe({
name: 'tobase64'
})
export class Tobase64Pipe implements PipeTransform {
transform(value: any, args?: any): any {
var xmlHTTP = new XMLHttpRequest();
xmlHTTP.open('GET', value, true);
xmlHTTP.responseType = 'arraybuffer';
xmlHTTP.onload = (e) => {
console.log('e',e);
var arr = new Uint8Array(e.currentTarget.response);
var raw = String.fromCharCode.apply(null, arr);
var b64 = base64.encode(raw);
var dataURL = "data:image/png;base64," + b64;
return dataURL;
};
xmlHTTP.send();
return null;
}
}
Html
<image x="0" y="0" width="500" height="500" [attr.xlink:href]="'assets/hand.jpg' | tobase64" mask="url(#hole)" />
Error:
ERROR RangeError: Maximum call stack size exceeded
Upvotes: 0
Views: 1370
Reputation: 3315
It is completely okay to do HTTP requests from a pipe. In your case you will eventually do just one. Consider this: if you bind a src
attribute of an img
to a property that can change dynamically, you will eventually make a server call any time the property changes; it's not really a big deal whether that call is an XHR call or a simple request for an image. The only two things I do not quite understand about your pipe is following:
null
? It seems that even if you did not get any errors, you won't still get the result This solution will require two pipes to be clear: one is a custom pipe for making XHR calls and the other is the Angular's built-in pipe async
. Here is our custom pipe:
import { Pipe, PipeTransform } from '@angular/core';
import { Http, RequestOptions, Headers, ResponseContentType } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
@Pipe({name: 'toBase64'})
export class ImagePipe implements PipeTransform {
constructor(private http: Http) {}
transform(url: string) {
const headers = new Headers({'Content-Type': 'image/*'}); /* tell that XHR is going to receive an image as response, so it can be then converted to blob */
return this.http.get(url, new RequestOptions({headers: headers, responseType: ResponseContentType.Blob})) // specify that response should be treated as blob data
.map(response => response.blob()) // take the blob
.switchMap(blob => {
// return new observable which emits a base64 string when blob is converted to base64
return Observable.create(observer => {
const reader = new FileReader();
reader.readAsDataURL(blob); // convert blob to base64
reader.onloadend = function() {
observer.next(reader.result); // emit the base64 string result
}
});
});
}
}
And here goes your html:
<image x="0" y="0" width="500" height="500" [attr.xlink:href]="('assets/hand.jpg' | toBase64) | async" mask="url(#hole)" />
We use our pipe to get an observable of a base64 string, and async
to insert the actual emitted string inside the src
tag.
One thing you need to keep in mind is CORS: your image serving server should be configured in a way that it accepts XHR calls for images from the domain your Angular app is running on, also, you will have to provide absolute urls to the custom pipe, otherwise it will make requests to the Angular app's domain itself. But in your case I assume you serve images from your own app, so this should not really be a concern.
Upvotes: 2