Skip to content

Commit

Permalink
Refactored the GeneDetailComponent to work with the GoldenLayout dire…
Browse files Browse the repository at this point in the history
…ctive. This includes moving the getGeneDetails method to the Gene service and deleting the Details service.
  • Loading branch information
alancleary committed Oct 15, 2019
1 parent b968a0f commit 57b9bec
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 150 deletions.
2 changes: 1 addition & 1 deletion client/src/app/core/services/http.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export abstract class HttpService {
protected _makeRequest<T>(
serverID: string,
requestType: string,
body: any,
body: any = {},
makeUrl = ((url: string) => url),
): Observable<T> {
const args = {serverID, requestType, body};
Expand Down
18 changes: 7 additions & 11 deletions client/src/app/gene/components/family-detail.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Observable, Subject } from 'rxjs';
import { take, takeUntil } from 'rxjs/operators';
// App
import { AppConfig } from '@gcv/app.config';
import { DetailsService } from '@gcv/gene/services';
import { Server } from '@gcv/core/models';
import { Track } from '@gcv/gene/models';

Expand All @@ -29,21 +28,23 @@ import { Track } from '@gcv/gene/models';
})
export class FamilyDetailComponent implements OnInit, OnDestroy {

private _serverIDs = AppConfig.SERVERS.map(s => s.id);

@Input() family: string;
@Input() tracks: Observable<Track[]>;

private _serverIDs = AppConfig.SERVERS.map(s => s.id);
private _destroy: Subject<boolean> = new Subject();

genes: string[] = [];
geneString: string = "";
geneString: string = '';
familyTreeLinks: any[] = [];

constructor(private detailsService: DetailsService) { }

// Angular hooks

ngOnDestroy() {
this._destroy.next(true);
this._destroy.complete();
}

ngOnInit() {
this.tracks
.pipe(
Expand All @@ -52,11 +53,6 @@ export class FamilyDetailComponent implements OnInit, OnDestroy {
.subscribe((tracks) => this._process(tracks));
}

ngOnDestroy() {
this._destroy.next(true);
this._destroy.complete();
}

// private

private _process(tracks) {
Expand Down
133 changes: 36 additions & 97 deletions client/src/app/gene/components/gene-detail.component.ts
Original file line number Diff line number Diff line change
@@ -1,128 +1,67 @@
// Angular
import { Component, ComponentFactory, ComponentFactoryResolver, ComponentRef,
Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewContainerRef, ViewChild } from '@angular/core';
import { Component,
Input, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { take, takeUntil } from 'rxjs/operators';
// App
import { AlertComponent } from '@gcv/core/components';
import { AppConfig } from '@gcv/app.config';
import { Server } from '@gcv/core/models';
import { Alert, Gene } from '@gcv/gene/models';
import { DetailsService } from '@gcv/gene/services';
import { GeneService } from '@gcv/gene/services';

@Component({
selector: 'gene-detail',
styles: [`
#alerts {
position: absolute;
left: 0;
right: 0;
}
`],
styles: [ '' ],
template: `
<div id="alerts">
<ng-container #alerts></ng-container>
</div>
<h4>{{gene.name}}</h4>
<p *ngIf="familyTreeLink !== undefined">Family: <a href="{{familyTreeLink}}">{{gene.family}}</a></p>
<p><a [routerLink]="['/search', gene.source, gene.name]" queryParamsHandling="merge">Search for similar contexts</a></p>
<h4>{{ gene }}</h4>
<p *ngIf="familyTreeLink !== undefined">Family: <a href="{{ familyTreeLink }}">{{ family }}</a></p>
<p><a [routerLink]="['/search', source, name]" queryParamsHandling="merge">Search for similar contexts</a></p>
<ul>
<li *ngFor="let link of links">
<a href="{{link.href}}">{{link.text}}</a>
<a href="{{ link.href }}">{{ link.text }}</a>
</li>
</ul>
`,
})

export class GeneDetailComponent implements OnChanges, OnDestroy, OnInit {
@Input() gene: Gene;

@ViewChild('alerts', {read: ViewContainerRef, static: true}) alerts: ViewContainerRef;
export class GeneDetailComponent implements OnDestroy, OnInit {

links: any[];
familyTreeLink: string;
@Input() gene: string;
@Input() family: string;
@Input() source: string

private _serverIDs = AppConfig.SERVERS.map(s => s.id);
private _destroy: Subject<boolean> = new Subject();

// emits when the component is destroyed
private destroy: Subject<boolean>;

constructor(
private resolver: ComponentFactoryResolver,
private detailsService: DetailsService
) {
this.destroy = new Subject();
}
links: any[] = [];
familyTreeLink: string = '';

ngOnChanges(changes: SimpleChanges): void {
this.links = undefined;
if (this.gene !== undefined) {
this.links = undefined;
constructor(private _geneService: GeneService) { }

this.familyTreeLink = undefined;
const idx = this._serverIDs.indexOf(this.gene.source);
if (idx !== -1) {
const s: Server = AppConfig.SERVERS[idx];
if (s.hasOwnProperty('familyTreeLink')) {
this.familyTreeLink = s.familyTreeLink.url + this.gene.family;
}
}

this.detailsService.getGeneDetails(this.gene, (links) => {
this.links = links;
});
}
}
// Angular hooks

ngOnDestroy(): void {
this.destroy.next(true);
this.destroy.complete();
this._destroy.next(true);
this._destroy.complete();
}

ngOnInit(): void {
this.detailsService.requests
.pipe(takeUntil(this.destroy))
.subscribe(([args, request]) => {
this._requestToAlertComponent(args.serverID, request, 'links', this.alerts);
});
const idx = this._serverIDs.indexOf(this.source);
if (idx !== -1) {
const server: Server = AppConfig.SERVERS[idx];
if (server.hasOwnProperty('familyTreeLink')) {
this.familyTreeLink = server.familyTreeLink.url + this.family;
}
}
this._geneService.getGeneDetails(this.gene, this.source)
.pipe(
takeUntil(this._destroy),
take(1))
.subscribe((links) => this._process(links));
}

private _requestToAlertComponent(serverID, request, what, container) {
const source = AppConfig.getServer(serverID).name;
const factory: ComponentFactory<AlertComponent> = this.resolver.resolveComponentFactory(AlertComponent);
const componentRef: ComponentRef<AlertComponent> = container.createComponent(factory);
// EVIL: Angular doesn't have a defined method for hooking dynamic components into
// the Angular lifecycle so we must explicitly call ngOnChanges whenever a change
// occurs. Even worse, there is no hook for the Output directive, so we must
// shoehorn the desired functionality in!
componentRef.instance.close = function(componentRef) {
componentRef.destroy();
}.bind(this, componentRef);
componentRef.instance.float = true;
componentRef.instance.alert = new Alert(
'info',
'Loading ' + what + ' from \'' + source + '\'',
{spinner: true},
);
componentRef.instance.ngOnChanges({});
request
.pipe(takeUntil(componentRef.instance.onClose))
.subscribe(
(response) => {
componentRef.instance.alert = new Alert(
'success',
'Successfully loaded ' + what + ' from \'' + source + '\'',
{closable: true, autoClose: 3},
);
componentRef.instance.ngOnChanges({});
},
(error) => {
componentRef.instance.alert = new Alert(
'danger',
'Failed to load ' + what + ' from \'' + source + '\'',
{closable: true},
);
componentRef.instance.ngOnChanges({});
});
// private

private _process(links: any[]) {
this.links = links;
}
}
21 changes: 18 additions & 3 deletions client/src/app/gene/components/gene.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { GoldenLayoutDirective } from '@gcv/gene/directives';
import { Track } from '@gcv/gene/models';
import { GeneService, MicroTracksService } from '@gcv/gene/services';
import { FamilyDetailComponent } from './family-detail.component';
import { GeneDetailComponent } from './gene-detail.component';
import { LegendComponent } from './legend.component';
import { MacroComponent } from './macro.component';
import { MicroComponent } from './micro.component';
Expand All @@ -27,6 +28,7 @@ export class GeneComponent implements AfterViewInit, OnDestroy {
private _microLegend: Observable<{name: string, id: string}[]>;

layoutComponents = [
{component: GeneDetailComponent, name: 'gene'},
{component: FamilyDetailComponent, name: 'family'},
{component: LegendComponent, name: 'legend'},
{component: MacroComponent, name: 'macro'},
Expand Down Expand Up @@ -94,6 +96,17 @@ export class GeneComponent implements AfterViewInit, OnDestroy {
});
}

private _geneDetailConfigFactory(gene, family, source) {
const id = `gene:${gene}`;
return {
type: 'component',
componentName: 'gene',
id: id,
title: `Gene ${gene}`,
componentState: {inputs: {gene, family, source}}
};
}

private _familyDetailConfigFactory(family) {
const id = `family:${family}`;
return {
Expand Down Expand Up @@ -160,12 +173,14 @@ export class GeneComponent implements AfterViewInit, OnDestroy {
options
},
outputs: {
plotClick: (track) => {
plotClick: ({track}) => {
const plotConfig = this._plotConfigFactory(clusterID, track);
this.goldenLayoutDirective.stackItem(plotConfig, id);
},
geneClick: (name) => {
console.log(name);
geneClick: ({gene, family, source}) => {
const geneConfig =
this._geneDetailConfigFactory(gene, family, source);
this.goldenLayoutDirective.stackItem(geneConfig, id);
},
nameClick: (track) => {
console.log(track);
Expand Down
9 changes: 5 additions & 4 deletions client/src/app/gene/components/micro.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ export class MicroComponent implements AfterViewInit, OnDestroy {
// public

emitPlot(track) {
this.plotClick.emit(track);
this.plotClick.emit({track});
}

emitGene(name) {
this.geneClick.emit(name);
emitGene(gene, family, source) {
this.geneClick.emit({gene, family, source});
}

emitName(track) {
Expand Down Expand Up @@ -105,6 +105,7 @@ export class MicroComponent implements AfterViewInit, OnDestroy {
return tracks.map((t, j) => {
// make track
const track = {
source: t.source,
genus: t.genus,
species: t.species,
chromosome_name: t.name,
Expand Down Expand Up @@ -159,7 +160,7 @@ export class MicroComponent implements AfterViewInit, OnDestroy {
this._destroyViewer();
let options = {
plotClick: (t, i) => this.emitPlot(tracks[i]),
geneClick: (g, t) => this.emitGene(g.name),
geneClick: (t, g, i) => this.emitGene(g.name, g.family, t.source),
nameClick: (t, i) => this.emitName(tracks[i])
};
options = Object.assign(options, this.options);
Expand Down
1 change: 1 addition & 0 deletions client/src/app/gene/gene.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { GeneRoutingModule } from '@gcv/gene/gene-routing.module';
@NgModule({
declarations: [...fromComponents.components, ...fromDirectives.directives],
entryComponents: [
fromComponents.GeneDetailComponent,
fromComponents.FamilyDetailComponent,
fromComponents.LegendComponent,
fromComponents.MacroComponent,
Expand Down
29 changes: 0 additions & 29 deletions client/src/app/gene/services/details.service.ts

This file was deleted.

6 changes: 6 additions & 0 deletions client/src/app/gene/services/gene.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,10 @@ export class GeneService extends HttpService {
select(fromGene.getAlignedMicroTrackClusterGenes(id))
);
}

// fetches source specific details for the given gene
getGeneDetails(gene: string, source: string): Observable<any> {
const makeUrl = (url: string) => url + gene + '/json';
return this._makeRequest<any>(source, 'geneLinks', {}, makeUrl);
}
}
3 changes: 0 additions & 3 deletions client/src/app/gene/services/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { FilterService } from './filter.service';
import { ChromosomeService } from './chromosome.service';
import { DetailsService } from './details.service';
import { GeneService } from './gene.service';
import { MicroTracksService } from './micro-tracks.service';
import { PairwiseBlocksService} from './pairwise-blocks.service';
import { PlotsService } from './plots.service';

export const services: any[] = [
ChromosomeService,
DetailsService,
FilterService,
GeneService,
MicroTracksService,
Expand All @@ -17,7 +15,6 @@ export const services: any[] = [
];

export * from './chromosome.service';
export * from './details.service';
export * from './filter.service';
export * from './gene.service';
export * from './micro-tracks.service';
Expand Down
4 changes: 2 additions & 2 deletions client/src/assets/js/gcv/visualization/micro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export class Micro extends Visualizer {
this.options.highlight = this.options.highlight || [];
this.options.selectiveColoring = this.options.selectiveColoring;
this.options.nameClick = this.options.nameClick || ((c) => { /* noop */ });
this.options.geneClick = this.options.geneClick || ((b) => { /* noop */ });
this.options.geneClick = this.options.geneClick || ((t, g, i) => { /* noop */ });
this.options.plotClick = this.options.plotClick;
this.options.autoResize = this.options.autoResize || false;
this.options.hoverDelay = this.options.hoverDelay || 500;
Expand Down Expand Up @@ -281,7 +281,7 @@ export class Micro extends Visualizer {
.style("cursor", "pointer")
.on("mouseover", (g) => this.setTimeout(publishGeneEvent("select", g)))
.on("mouseout", (g) => this.clearTimeout(publishGeneEvent("deselect", g)))
.on("click", (g) => obj.options.geneClick(g, t))
.on("click", (g, i) => obj.options.geneClick(t, g, i))
// add optional HTML attributes to gene elelements
.addHTMLAttributes();
// add genes to the gene groups
Expand Down

0 comments on commit 57b9bec

Please sign in to comment.