mlrv / ng-material-treetable

Angular Material treetable component :deciduous_tree: :heart: ♻️
http://ng-material-treetable.surge.sh
MIT License
99 stars 65 forks source link

Serving up data for treetable via async obervable #24

Open jh0274 opened 5 years ago

jh0274 commented 5 years ago

Hi! This component looks great, thank you. I'm currently having issues trying to serve data to the treetable using an observable stream. I know that this works with mat-table, should it also work with your treetable?

Thanks

James

mlrv commented 5 years ago

Hi @jh0274, yes the component supports data coming in from streams, I can try to point you in the right direction if you provide an example of your use case.

jh0274 commented 5 years ago

Hi @mlrv

thank you so much for confirming that! So the issue i'm having is that the table renders first with the data that i initialised the 'Node' property with (ie 1, a, 2 - see below) but it's not updating when the async call to the web API returns the complete dataset (via the observable stream). I know the observable stream returns the dataset because i've debugged it to the console.. any help you can give me is much appreciated! Code below..

html:

<treetable [tree]="assets" [options]="treeOptions" (nodeClicked)="logNode($event)">

component.ts:

import { Component, OnInit, AfterViewInit } from '@angular/core'; import { HeroService } from '../hero.service'; import { Hero } from '../hero'; import { Asset} from '../asset'; import { Options, Node } from 'ng-material-treetable'; import { AssetsService } from '../assets.service';

@Component({ selector: 'app-heroes', templateUrl: './heroes.component.html', styleUrls: ['./heroes.component.css'] }) export class HeroesComponent implements OnInit {

selectedHero: Hero; onSelect(hero: Hero): void { this.selectedHero = hero; }

constructor( private assetsService: AssetsService, ) { }

assets: Node = { value: { id: 1, AA1: "a", percent: 2, }, children: [] };

errorMessage: String;

getAssets(): void { this.assetsService.getAssets() .subscribe( assets => { this.assets = assets; }, error => this.errorMessage = error ); } ngOnInit() { this.getAssets(); } }

assets.Service.ts:

import { Injectable } from '@angular/core';

import { Observable, of } from 'rxjs'; import { MessageService } from './message.service'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { catchError, map, tap, filter, retry } from 'rxjs/operators';

import { Node } from 'ng-material-treetable'; import {Asset } from './asset';

@Injectable({ providedIn: 'root' }) export class AssetsService {

constructor(private http: HttpClient, private messageService: MessageService) { }

getAssets (): Observable<Node<Asset>>{
  return this.http.get<Node<Asset>>(this.assetsUrl)
    .pipe(
      tap(data => this.log('fetched assets' + JSON.stringify(data))),
    );
}

private log(message: string) {
  this.messageService.add(`AssetsService: ${message}`);
  }
  private handleError<T> (operation = 'operation', result?: T) {
    return (error: any): Observable<T> => {

      // TODO: send the error to remote logging infrastructure
      console.error(error); // log to console instead

      // TODO: better job of transforming error for user consumption
      this.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }

private assetsUrl = 'api/assets';

}

in-memory-data.service.ts (Mock DB):

import { InMemoryDbService } from 'angular-in-memory-web-api'; import { Hero } from './hero'; import { Injectable } from '@angular/core'; import { Node } from 'ng-material-treetable'; import {Asset } from './asset';

@Injectable({ providedIn: 'root', }) export class InMemoryDataService implements InMemoryDbService { createDb() { const assets: Node = { value: { id: 1, AA1: 'Listed Equities', percent: 15.6 }, children: [ { value: { id: 2, AA1: 'Emerging Markets(ex China)', percent: 4 }, children: [] }, { value: { id: 3, AA1: 'Emerging Markets(ex China)', percent: 4 }, children: [] }, { value: { id: 4, AA1: 'Emerging Markets(ex China)', percent: 4 }, children: [] } ] };

return {assets};

}

Stromwerk commented 5 years ago

I also have a similar issue. I load the tree structure once, but I have a way to change the current tree. When the event is triggered for the tree change and all the data is loaded from the Observables the tree still doesn't update. I've checked the network tab and the request is there and it finishes.

this.masterDataService.getItemListExplosion(indexKey, refDate, unit, true, 1)
.subscribe((itemListExplosion: IItemListExplosion[]) => {
    console.log(this.itemListExplosionToTree(itemListExplosion));
    this.treeDataStructure = this.itemListExplosionToTree(itemListExplosion);
});

This is the code in question. this.treeDataStructure is the value which is passed to the [tree] input. As you can see there is a console.log there, which logs the Tree which is then assigned to treeDataStructure. The value is logged to the console, however the tree doesn't update.

peterjester commented 4 years ago

Hi! Was this ever implemented/resolved? Would love to implement this tree-table in my application, however my data is dynamic. @mlrv

The use case @jh0274 used is essentially exactly what I am trying to do.

ferhattelci commented 4 years ago

Hi all! i'm also trying to do this and getting

node is undefined! An error occured. We'll work on it. Please take a screenshot, describe how the error could occur and contact your system administrator.

This exception occurs from the validator.service, because my tree-array is undefined

Some kind of ideas?

Thanks!

mthor commented 4 years ago

I added an *ngIf="data" and that seemed to work

<treetable *ngIf="ctCloudDepartmentService.departmentsGridData.length" [tree]="ctCloudDepartmentService.departmentsGridData" [options]="treeOptions" (nodeClicked)="logNode($event)"> </treetable>