doc-viewer-angular
Version:
Angular document viewer.
176 lines (167 loc) • 7.06 kB
text/typescript
import { Component, Input, NgZone, OnDestroy, OnChanges, SimpleChanges, Output } from '@angular/core';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { take } from 'rxjs/operators';
import { Subscription, interval } from 'rxjs';
import { EventEmitter } from '@angular/core';
declare var mammoth;
export type viewerType = 'google' | 'office' | 'mammoth' | 'pdf';
export class NgxDocViewerComponent implements OnChanges, OnDestroy {
public fullUrl: SafeResourceUrl = null;
public externalViewer = false;
public docHtml = '';
public configuredViewer: viewerType = 'google';
private checkIFrameSubscription: Subscription = null;
constructor(private domSanitizer: DomSanitizer, private ngZone: NgZone) { }
loaded: EventEmitter<any> = new EventEmitter();
url = '';
googleCheckInterval = 3000;
disableContent: 'none' | 'all' | 'popout' | 'popout-hide' = 'none';
googleCheckContentLoaded = true;
set viewer(viewer: viewerType) {
if (viewer !== 'google' && viewer !== 'office' && viewer !== 'mammoth' && viewer !== 'pdf') {
console.error(`Unsupported viewer: '${viewer}'. Supported viewers: google, office, mammoth and pdf`);
}
if (viewer === 'mammoth') {
if (mammoth === null) {
console.error('please install mammoth when using local viewer');
}
}
this.configuredViewer = viewer;
}
ngOnDestroy(): void {
if (this.checkIFrameSubscription) {
this.checkIFrameSubscription.unsubscribe();
}
}
async ngOnChanges(changes: SimpleChanges): Promise<void> {
if (this.disableContent !== 'none' && this.viewer !== 'google') {
}
if ((changes.url && changes.url.currentValue !== changes.url.previousValue) ||
changes.viewer && changes.viewer.currentValue !== changes.viewer.previousValue) {
this.docHtml = '';
this.externalViewer = this.configuredViewer === 'google' || this.configuredViewer === 'office';
if (this.checkIFrameSubscription) {
this.checkIFrameSubscription.unsubscribe();
}
if (!this.url) {
this.fullUrl = null;
} else if (this.configuredViewer === 'office' || this.configuredViewer === 'google'
|| this.configuredViewer === 'pdf') {
const u = this.url.indexOf('/') ? encodeURIComponent(this.url) : this.url;
this.fullUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(
this.configuredViewer === 'google' ?
`https://docs.google.com/gview?url=${u}&embedded=true` :
this.configuredViewer === 'office' ?
`https://view.officeapps.live.com/op/embed.aspx?src=${u}` :
this.url);
// see:
// https://stackoverflow.com/questions/40414039/google-docs-viewer-returning-204-responses-no-longer-working-alternatives
// hack to reload iframe if it's not loaded.
// would maybe be better to use view.officeapps.live.com but seems not to work with sas token.
if (this.configuredViewer === 'google' && this.googleCheckContentLoaded) {
this.ngZone.runOutsideAngular(() => {
let iframe = document.querySelector('iframe');
this.checkIFrame(iframe);
// if it's not loaded after the googleIntervalCheck, then open load again.
this.checkIFrameSubscription = interval(this.googleCheckInterval)
.pipe(
take(Math.round(this.googleCheckInterval === 0 ? 0 : 20000 / this.googleCheckInterval)))
.subscribe(() => {
if (iframe == null) {
iframe = document.querySelector('iframe');
this.checkIFrame(iframe);
}
this.reloadIFrame(iframe);
});
});
}
} else if (this.configuredViewer === 'mammoth') {
if (!mammoth) {
console.error('Please install mammoth and make sure mammoth.browser.min.js is loaded.');
}
this.docHtml = await this.getDocxToHtml(this.url);
}
}
}
checkIFrame(iframe: HTMLIFrameElement) {
if (iframe) {
iframe.onload = () => {
this.loaded.emit(null);
if (this.checkIFrameSubscription) {
this.checkIFrameSubscription.unsubscribe();
}
};
}
}
reloadIFrame(iframe: HTMLIFrameElement) {
if (iframe) {
console.log('reloading..');
iframe.src = iframe.src;
}
}
private async getDocxToHtml(url: string) {
const arrayBuffer = await this.fileToArray(url);
const resultObject = await mammoth.convertToHtml({ arrayBuffer });
return resultObject.value;
}
private fileToArray(url: string): Promise<ArrayBuffer> {
return new Promise<ArrayBuffer>((resolve, reject) => {
try {
const request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'blob';
request.onload = () => {
const reader = new FileReader();
reader.readAsArrayBuffer(request.response);
reader.onloadend = (e) => {
resolve(reader.result as ArrayBuffer);
};
};
request.send();
} catch {
reject(`error while retrieving file ${url}.`);
}
});
}
}