Skip to content

Instantly share code, notes, and snippets.

@CodingBash
Created January 31, 2026 13:24
Show Gist options
  • Select an option

  • Save CodingBash/2da85d53b958de164b4b7c16dbe49b90 to your computer and use it in GitHub Desktop.

Select an option

Save CodingBash/2da85d53b958de164b4b7c16dbe49b90 to your computer and use it in GitHub Desktop.
<!-- Hide if not in view or -->
<div [hidden]="!(inView || overrideShow)">
<img [style.maxWidth]="img_width" [style.maxHeight]="img_height" [src]="imageSrc" style="min-width: 100px; min-height: 20px;" class="zoom loading">
</div>
import { isPlatformBrowser } from '@angular/common';
import { ChangeDetectorRef } from '@angular/core';
import { Component, ElementRef, EventEmitter, Inject, Input, OnChanges, OnDestroy, OnInit, Output, PLATFORM_ID, Renderer2, SimpleChanges } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { ComponentBasicModelViewer } from 'src/app/models/postproc-component-classes';
import { FitDetailsService } from 'src/app/services/fit-details.service';
/**
* Component from https://medium.com/allenhwkim/simple-lazy-loading-with-angular-716dd3b174a
*/
@Component({
selector: 'image-in-view',
templateUrl: './image-in-view.component.html',
styleUrls: ['./image-in-view.component.css']
})
export class ImageInViewComponent implements OnInit, OnDestroy, OnChanges {
observer: IntersectionObserver;
inView: boolean = false;
once50PctVisible: boolean = false;
imageSrc: string;
imageLoadedOrLoading = false;
@Input('representativeFit') representativeFit: number;
@Input('img_width') img_width: string;
@Input('img_height') img_height: string;
@Input('override') overrideShow: boolean = false;
@Input() options: any = { threshold: [.1, .2, .3, .4, .5, .6, .7, .8] };
@Output('inView') inView$: EventEmitter<any> = new EventEmitter();
@Output('notInView') notInView$: EventEmitter<any> = new EventEmitter();
constructor(
public element: ElementRef,
public renderer: Renderer2,
private fitDetailsService: FitDetailsService,
private cdRef: ChangeDetectorRef,
@Inject(PLATFORM_ID) private platformId: any) { }
/**
* When a component is initialized, it will register an IntersectionObserver, so that when element is visible, it calls the callback handleIntersect.
*/
ngOnInit(): void {
if (isPlatformBrowser(this.platformId)) {
this.observer = new IntersectionObserver(this.handleIntersect.bind(this), this.options);
this.observer.observe(this.element.nativeElement);
}
/*
* If override is true on init, then load image
*/
if (this.overrideShow && !this.imageLoadedOrLoading) {
console.log("Image loaded by override true at init")
this.imageLoadedOrLoading = true;
this.getFitLogoSrc(this.representativeFit).subscribe(src => {
console.log("Image source set");
this.imageSrc = src;
});
}
}
ngOnChanges(changes: SimpleChanges) {
/*
* If override becomes true and image is not loaded already loaded, then load.
*/
if (changes.overrideShow != undefined && changes.overrideShow.currentValue != changes.overrideShow.previousValue) {
if (changes.overrideShow.currentValue && this.imageSrc == undefined && !this.imageLoadedOrLoading) {
console.log("Image loaded by change in override")
this.imageLoadedOrLoading = true;
this.getFitLogoSrc(this.representativeFit).subscribe(src => {
console.log("Image source set");
this.imageSrc = src;
this.cdRef.detectChanges();
});
}
}
}
ngOnDestroy(): void {
this.observer.disconnect();
}
/**
* The observer callback handleIntersect sets inView to true and fires inView$ event when the element is visible and fires notInView$ event when the element is not visible.
*
* @param entries
* @param observer
*/
handleIntersect(entries, observer): void {
entries.forEach((entry: IntersectionObserverEntry) => {
if (entry.isIntersecting) {
/*
* If image is in view, load image and remove hidden
*/
if (this.imageSrc == undefined && !this.imageLoadedOrLoading) {
console.log("Image loaded by becoming in view");
this.imageLoadedOrLoading = true;
this.getFitLogoSrc(this.representativeFit).subscribe(src => {
console.log("Image source set");
this.imageSrc = src;
});
}
this.inView = true;
this.inView$.emit(entry);
} else {
this.notInView$.emit(entry);
}
});
}
/**
* TODO: Cache the basicmodelviewer call by creating a separate web rest endpoint for image URLs specifically, the cache that instead of the whole basicModelViewer call since this may change. Alternatively, I can create a runtime object or local storage object to contain fit->image hashtable for quick lookup.
*
* @param fitId
*/
getFitLogoSrc(fitId: number): Observable<string> {
return this.fitDetailsService.getFitPostproc(fitId, "basicModelViewer").pipe(map((postprocComponent) => {
if (postprocComponent == null) {
return null;
} else {
let basicModelViewerComponent: ComponentBasicModelViewer = <ComponentBasicModelViewer>postprocComponent;
let mononucleotideResourceNoAxesForwardLink = basicModelViewerComponent.viewBindingModels[0].mononucleotideResourceNoAxesForwardLink;
return mononucleotideResourceNoAxesForwardLink;
}
}));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment