Skip to content

Instantly share code, notes, and snippets.

@SpeedoPasanen
Last active September 22, 2017 15:35
Show Gist options
  • Save SpeedoPasanen/8f0bd518c82e707770915018691acdd1 to your computer and use it in GitHub Desktop.
Save SpeedoPasanen/8f0bd518c82e707770915018691acdd1 to your computer and use it in GitHub Desktop.
FrameComponent
import { Router } from '@angular/router';
import { ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild, Input } from '@angular/core';
import * as $ from 'jquery'; // ToDo: Get rid of jQuery to comply with Angular Universal. Or just don't server-render the frame, cause what's the point anyway.
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { StateService } from '../../state/state.service';
import * as nodeUrl from 'url';
@Component({
selector: 'm-frame',
template: `
<div class="frame-component">
<div *ngIf="loading" class="loading"><i class="fa fa-spin fa-spinner fa-3x"></i></div>
<iframe #iframe></iframe>
</div>
`,
styleUrls: ['./frame.component.css']
})
export class FrameComponent implements OnInit, OnDestroy {
private _src: string;
private subs: Subscription[] = [];
public loading = false;
@ViewChild('iframe') _iframe: ElementRef;
constructor(
private router: Router,
protected cdRef: ChangeDetectorRef,
private state: StateService
) { }
ngOnInit() {
$(this.iframe).on('load', () => {
this.loaded();
});
this.subs.push(Observable.fromEvent(window, 'message').subscribe(evt => {
// Let's change queryParams.frame when a navigation occurs inside the frame.
// All inner routes trigger a window.postMessage at load with the URL as payload.
this.receiveMsg(evt);
}));
this.subs.push(Observable.fromEvent(window, 'resize').debounceTime(300).subscribe(evt => {
this.updateHeight(); // I want the frame to fill the available height. ToDo: use flexbox x)
}));
}
ngOnDestroy() {
this.subs.forEach(sub => sub.unsubscribe());
$(this.iframe).off('load');
$(window).off('message', this.receiveMsg);
}
private get iframe(): HTMLIFrameElement { return this._iframe.nativeElement; }
private receiveMsg(evt) {
if (evt.data && evt.data.loaded) {
const src = evt.data.loaded.replace(/^\//, '').replace(/\/$/, '');
if (this._src !== src) {
this.router.navigate([], { queryParams:
src.length
? { frame: src }
: {} // Instead of showing the old front page, just leave the frame.
});
}
return;
}
}
private loaded() {
this.updateHeight();
this.loading = false;
this.iframe.contentWindow.postMessage({ func: 'initFrame' }, '*');
this.cdRef.markForCheck();
}
@Input() public set src(src: string) {
if (src === this._src) {
return;
}
this.loading = true;
this._src = src;
this.iframe.src = '/' + this.getFrameSrc(src);
this.cdRef.markForCheck();
}
public get src(): string { return this._src; }
//// Less interesting stuff. Resize the frame, add query params.
/*
Add some state info as query params to the frame src,
so for example, the same customer can be pre-selected inside the frame that's already selected outside it.
*/
public getFrameSrc(urlStr: string) {
const urlObj = nodeUrl.parse(urlStr, true);
urlObj.query = { customerId: this.state.state.customerId };
if (urlObj.search && urlObj.search.length) {
const pairs = urlObj.search.replace(/^\?/, '').split('&');
pairs.forEach(pair => {
const parts = pair.split('=');
urlObj.query[parts[0]] = parts[1];
});
urlObj.search = null;
}
return nodeUrl.format(urlObj);
}
public updateHeight() {
const padding = this.getPadding($(this.iframe).parent());
const newHeight = Math.max(this.windowHeight(), $(document).height()) - padding - this.getOffset();
this.setHeight(newHeight - 10);
}
private getPadding($elem, levelsDown = 0): number {
if ((levelsDown > 5) || ($elem.is('body'))) {
return 0;
}
const result = parseInt($elem.css('margin-bottom')) + parseInt($elem.css('padding-bottom'));
return result + this.getPadding($elem.parent(), levelsDown + 1);
}
private setHeight(n: number) {
$(this.iframe).height(n);
}
private getOffset() {
return $(this.iframe).offset().top;
}
private windowHeight(): number {
return Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment