Page MenuHomec4science

tln-tree-component.component.ts
No OneTemporary

File Metadata

Created
Fri, Apr 26, 19:34

tln-tree-component.component.ts

import {Component, Injectable, Input, OnInit} from '@angular/core';
import {CollectionViewer, SelectionChange, DataSource} from '@angular/cdk/collections';
import {FlatTreeControl} from '@angular/cdk/tree';
import {BehaviorSubject, merge, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {NavTreeDef} from '../../models/models';
import {QueryService} from '../../services/query.service';
import * as _ from 'lodash';
import {CrossrefEditorDataServiceService} from '../crossref-editor-data-service.service';
/** Flat node with expandable and level information */
export class TlnTreeNode {
constructor(public label: string, public iri: string, public level = 1, public expandable = false,
public isLoading = false, public type? ) {
}
}
/**
* Database for dynamic data. When expanding a node in the tree, the data source will need to fetch
* the descendants data from the database.
*/
@Injectable({providedIn: 'root'})
export class DynamicDatabase {
constructor() {
}
}
/**
* File database, it can build a tree structured Json object from string.
* Each node in Json object represents a file or a directory. For a file, it has filename and type.
* For a directory, it has filename and children (a list of files or directories).
* The input will be a json object string, and the output is a list of `FileNode` with nested
* structure.
*/
export class DynamicDataSource implements DataSource<TlnTreeNode> {
dataChange = new BehaviorSubject<TlnTreeNode[]>([]);
genericLevel: number = 0;
get data(): TlnTreeNode[] { return this.dataChange.value; }
set data(value: TlnTreeNode[]) {
this._treeControl.dataNodes = value;
this.dataChange.next(value);
}
constructor(private _treeControl: FlatTreeControl<TlnTreeNode>,
private _database: DynamicDatabase, private queryService: QueryService, private treeDefs: NavTreeDef[], private generic: boolean) {
}
expandable(level: number) {
if (this.generic) {return true} else {
return level < this.treeDefs.length-1
}
}
connect(collectionViewer: CollectionViewer): Observable<TlnTreeNode[]> {
this._treeControl.expansionModel.changed.subscribe(change => {
if ((change as SelectionChange<TlnTreeNode>).added ||
(change as SelectionChange<TlnTreeNode>).removed) {
this.handleTreeControl(change as SelectionChange<TlnTreeNode>);
}
});
return merge(collectionViewer.viewChange, this.dataChange).pipe(map(() => this.data));
}
disconnect(collectionViewer: CollectionViewer): void {}
/** Handle expand/collapse behaviors */
handleTreeControl(change: SelectionChange<TlnTreeNode>) {
if (change.added) {
change.added.forEach(node => this.toggleNode(node, this.expandable(node.level)));
}
if (change.removed) {
change.removed.slice().reverse().forEach(node => this.toggleNode(node, false));
}
}
toggleNode(node: TlnTreeNode, expand: boolean) {
node.isLoading = true;
const index = this.data.indexOf(node);
if (expand) {
this.getChildData(node).then(children => {
if (!children || index < 0) { // If no children, or cannot find the node, no op
return; }
this.data.splice(index + 1, 0, ...children);
// notify the change
this.dataChange.next(this.data);
});
} else {
let count = 0;
for (let i = index + 1; i < this.data.length
&& this.data[i].level > node.level; i++, count++) {}
this.data.splice(index + 1, count);
// notify the change
this.dataChange.next(this.data);
}
node.isLoading = false;
}
getChildData(node: TlnTreeNode): Promise<TlnTreeNode[]> {
console.log('getting children');
if (this.generic) {
return this.getGenericChildren(node);
} else {
return this.getChildNodesFromDef(node);
}
}
getParent(node: TlnTreeNode): TlnTreeNode {
if (node.level < 1) {
return null;
}
const index= this.data.indexOf(node);
for (let i = index; i >= 0; i--) {
const currentNode = this.data[i];
if (currentNode.level < node.level) {
return currentNode;
}
}
}
async getGenericChildren(node: TlnTreeNode){
let treeDef: NavTreeDef;
const parentNode = this.getParent(node);
let query: string;
console.log('node ', node);
if (node.level % 2 === 0 || 0 ) { // Then we are querying for properties
treeDef = this.treeDefs[1]; // we want to get resources/ objects of that clicked prop
query = await this.queryService.parametrizeQueryWithItem(treeDef.apiDef.query, node.iri);
} else { // we want all objects of the s&p
console.log(this.data);
treeDef = this.treeDefs[2];
query = await this.queryService.parametrizeQueryWithItem(treeDef.apiDef.query, parentNode.iri, node.iri);
}
return this.getData(query, treeDef, node.level+1);
}
async getChildNodesFromDef(node: TlnTreeNode): Promise<TlnTreeNode[]> {
const treeDef = this.treeDefs.filter(def => def.idx === node.level+1 )[0];
let query: string;
if (treeDef.apiDef.paramTriple && treeDef.apiDef.paramTriple === 2) {
query = await this.queryService.parametrizeQueryWithItem(treeDef.apiDef.query, '', '', node.iri);
} else { query = await this.queryService.parametrizeQueryWithItem(treeDef.apiDef.query, node.iri);}
return this.getData(query, treeDef);
}
async getData(query: string, treeDef: NavTreeDef, level?: number): Promise<TlnTreeNode[]> {
let nodes: TlnTreeNode[] = [];
await this.queryService.getData(treeDef.apiDef.baseUrl, query, 'SELECT').then(res => {
const treeData: any[] = _.get(res, treeDef.apiDef.dataArray);
treeData.forEach(entry => {
const tlnNode = new TlnTreeNode(_.get(entry, treeDef.apiDef.mapping.label),
_.get(entry, treeDef.apiDef.mapping.iri), level ||
treeDef.idx, this.expandable(treeDef.idx), false, _.get(entry, treeDef.apiDef.mapping.type) );
nodes.push(tlnNode);
});
});
return nodes;
}
}
@Component({
selector: 'app-tln-tree-component',
templateUrl: './tln-tree-component.component.html',
styleUrls: ['./tln-tree-component.component.scss']
})
export class CrossRefTreeComponentComponent implements OnInit {
@Input() treeDefs: NavTreeDef[];
@Input() generic: boolean;
ngOnInit() {
this.treeControl = new FlatTreeControl<TlnTreeNode>(this.getLevel, this.isExpandable);
this.dataSource = new DynamicDataSource(this.treeControl, this.database, this.queryService, this.treeDefs, this.generic);
this.initRootLevelNodes();
}
constructor(public database: DynamicDatabase, private queryService: QueryService, private dataService: CrossrefEditorDataServiceService) {
}
initRootLevelNodes() {
const rootTreeDef = this.treeDefs.filter(def => def.idx === 0)[0];
const query = rootTreeDef.apiDef.query;
this.dataSource.getData(query, rootTreeDef).then(rootNodes => this.dataSource.data = rootNodes);
}
onClick(node:TlnTreeNode): void {
this.dataService.clickedNodeEvent.emit(node);
}
treeControl: FlatTreeControl<TlnTreeNode>;
dataSource: DynamicDataSource;
getLevel = (node: TlnTreeNode) => node.level;
isExpandable = (node: TlnTreeNode) => node.expandable;
hasChild = (_: number, _nodeData: TlnTreeNode) => _nodeData.expandable;
}

Event Timeline