how can I create a nested, recursive table that can drill down x levels in Angular

◇◆丶佛笑我妖孽 提交于 2019-12-08 13:41:27

问题


I need to make a table with data with nested subtasks. I need to make this table recursive. There is a lot of literature about ngFor recursively showing data, but not in a table format. There are a lot of things that make tables harder to work with(e.g. using div tags outside of td tags, etc.)

here is a stackblitz of what I got so far.

https://stackblitz.com/edit/angular-xk9nw6?file=src%2Fapp%2Ftable%2Ftable.component.html

As you can see, I can only drill down a single level before I encounter issues with ngFor

<H1 style="text-indent: 10px">Table</H1>

<style>
table{
  background:rgb(223, 221, 221);
}
</style>

<table  class="table">
  <thead>
  <tr>
    <th>expand</th>
    <th>task id</th>
    <th>task name</th>
    <th>button</th>
  </tr>
</thead>  
<tbody *ngFor="let datum of data; index as i">
  <tr>
      <td class="" (click)="task(datum, i); $event.stopPropagation()" > 
        <i [ngClass]="(index != i)?'fas fa-plus':'fas fa-minus'"></i> 
      </td>
      <td class="">{{datum.taskID}}</td>
      <td class="">{{datum.taskName}}</td>
      <td ALIGN="center">

        <!-- <i class="fas fa-ellipsis-h" data-toggle="dropdown"></i> -->


          <select>        
               <option (click)="dropdown(datum)" value="add">Add</option>
               <option (click)="dropdown(datum)" value="edit">Edit</option>
               <option (click)="dropdown(datum)" value="delete">Delete</option>
          </select>

        </td> 
  </tr>
  <tr [hidden]="index != i" *ngFor="let subtask of datum.subtasks; index as j" >
    <div [hidden]="!subtask.subtasks">
      <td class="" style="text-indent: 15px" (click)="subtaskQ(subtask, j)"> <i class="fas fa-plus"></i> </td>
    </div>
    <div [hidden]="subtask.subtasks">
        <td class="" style="text-indent: 15px" (click)="subtaskQ(subtask, j)"></td>
    </div>
      <td class="">{{subtask?.taskID}}</td>
      <td class="">{{subtask?.taskName}}</td>
      <td ALIGN="center">
          <select>        
               <option (click)="dropdown(subtask)" value="add">Add</option>
               <option (click)="dropdown(subtask)" value="edit">Edit</option>
               <option (click)="dropdown(subtask)" value="delete">Delete</option>
          </select>
        </td>
  </tr>
  <tr [hidden]="index != i && index !=j" *ngFor="let subtaskL of subtask; index as e" >

        <td class="" style="text-indent: 15px" (click)="subtaskQ(subtask, j)"></td>
        <td class="">{{subtaskL?.taskID}}</td>
        <td class="">{{subtaskL?.taskName}}</td>
        <td ALIGN="center">
            <select>        
                 <option (click)="dropdown(subtask)" value="add">Add</option>
                 <option (click)="dropdown(subtask)" value="edit">Edit</option>
                 <option (click)="dropdown(subtask)" value="delete">Delete</option>
            </select>
          </td>
    </tr>
</tbody> 



</table>

component

import { Component, OnInit,ViewChild } from '@angular/core';
import { sampleData } from '../datasource';

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

  constructor() { }

  @ViewChild('treegrid')
  public data: Object[];
  public foo: boolean;
  public subtaskArray: any;
  public subSubtaskArray: any;
  public index: number;

  dropdown(e){
    console.log(e);
  }

  task(e, i){
    console.log(e);

    this.subtaskArray = e.subtasks
    this.index = i;
  }

  subtaskQ(e, j){
    this.subSubtaskArray = e.subtasks;
    console.log(j);
    console.log(e);

  }

  switch(){
    if(this.foo){
      this.foo =false;
    } else {
    this.foo = true;
    }
  }

  ngOnInit(): void {
    this.data = sampleData;
}
}

sample data

/**
 * Test cases data source
 */
export let sampleData: Object[] = [
    {
        taskID: 1,
        taskName: 'Planning',
        startDate: new Date('02/03/2017'),
        endDate: new Date('02/07/2017'),
        progress: 100,
        duration: 5,
        priority: 'Normal',
        approved: false,
        isInExpandState: true,
        subtasks: [
            { taskID: 2, taskName: 'Plan timeline', startDate: new Date('02/03/2017'), endDate: new Date('02/07/2017'), duration: 5, progress: 100, priority: 'Normal', approved: false },
            { taskID: 3, taskName: 'Plan budget', startDate: new Date('02/03/2017'), endDate: new Date('02/07/2017'), duration: 5, progress: 100, approved: true },
            { taskID: 4, taskName: 'Allocate resources', startDate: new Date('02/03/2017'), endDate: new Date('02/07/2017'), duration: 5, progress: 100, priority: 'Critical', approved: false },
            { taskID: 5, taskName: 'Planning complete', startDate: new Date('02/07/2017'), endDate: new Date('02/07/2017'), duration: 0, progress: 0, priority: 'Low', approved: true }
        ]
    },
    {
        taskID: 6,
        taskName: 'Design',
        startDate: new Date('02/10/2017'),
        endDate: new Date('02/14/2017'),
        duration: 3,
        progress: 86,
        priority: 'High',
        isInExpandState: false,
        approved: false,
        subtasks: [
            { taskID: 7, taskName: 'Software Specification', startDate: new Date('02/10/2017'), endDate: new Date('02/12/2017'), duration: 3, progress: 60, priority: 'Normal', approved: false },
            { taskID: 8, taskName: 'Develop prototype', startDate: new Date('02/10/2017'), endDate: new Date('02/12/2017'), duration: 3, progress: 100, priority: 'Critical', approved: false },
            { taskID: 9, taskName: 'Get approval from customer', startDate: new Date('02/13/2017'), endDate: new Date('02/14/2017'), duration: 2, progress: 100, approved: true },
            { taskID: 10, taskName: 'Design Documentation', startDate: new Date('02/13/2017'), endDate: new Date('02/14/2017'), duration: 2, progress: 100, approved: true },
            { taskID: 11, taskName: 'Design complete', startDate: new Date('02/14/2017'), endDate: new Date('02/14/2017'), duration: 0, progress: 0, priority: 'Normal', approved: true }
        ]
    },
    {
        taskID: 12,
        taskName: 'Implementation Phase',
        startDate: new Date('02/17/2017'),
        endDate: new Date('02/27/2017'),
        priority: 'Normal',
        approved: false,
        duration: 11,
        subtasks: [
            {
                taskID: 13,
                taskName: 'Phase 1',
                startDate: new Date('02/17/2017'),
                endDate: new Date('02/27/2017'),
                priority: 'High',
                approved: false,
                duration: 11,
                subtasks: [{
                    taskID: 14,
                    taskName: 'Implementation Module 1',
                    startDate: new Date('02/17/2017'),
                    endDate: new Date('02/27/2017'),
                    priority: 'Normal',
                    duration: 11,
                    approved: false,
                    subtasks: [
                        { taskID: 15, taskName: 'Development Task 1', startDate: new Date('02/17/2017'), endDate: new Date('02/19/2017'), duration: 3, progress: '50', priority: 'High', approved: false },
                        { taskID: 16, taskName: 'Development Task 2', startDate: new Date('02/17/2017'), endDate: new Date('02/19/2017'), duration: 3, progress: '50', priority: 'Low', approved: true },
                        { taskID: 17, taskName: 'Testing', startDate: new Date('02/20/2017'), endDate: new Date('02/21/2017'), duration: 2, progress: '0', priority: 'Normal', approved: true },
                        { taskID: 18, taskName: 'Bug fix', startDate: new Date('02/24/2017'), endDate: new Date('02/25/2017'), duration: 2, progress: '0', priority: 'Critical', approved: false },
                        { taskID: 19, taskName: 'Customer review meeting', startDate: new Date('02/26/2017'), endDate: new Date('02/27/2017'), duration: 2, progress: '0', priority: 'High', approved: false },
                        { taskID: 20, taskName: 'Phase 1 complete', startDate: new Date('02/27/2017'), endDate: new Date('02/27/2017'), duration: 0, priority: 'Low', approved: true }

                    ]
                }]
            },
            {
                taskID: 21,
                taskName: 'Phase 2',
                startDate: new Date('02/17/2017'),
                endDate: new Date('02/28/2017'),
                priority: 'High',
                approved: false,
                duration: 12,
                subtasks: [{
                    taskID: 22,
                    taskName: 'Implementation Module 2',
                    startDate: new Date('02/17/2017'),
                    endDate: new Date('02/28/2017'),
                    priority: 'Critical',
                    approved: false,
                    duration: 12,
                    subtasks: [
                        { taskID: 23, taskName: 'Development Task 1', startDate: new Date('02/17/2017'), endDate: new Date('02/20/2017'), duration: 4, progress: '50', priority: 'Normal', approved: true },
                        { taskID: 24, taskName: 'Development Task 2', startDate: new Date('02/17/2017'), endDate: new Date('02/20/2017'), duration: 4, progress: '50', priority: 'Critical', approved: true },
                        { taskID: 25, taskName: 'Testing', startDate: new Date('02/21/2017'), endDate: new Date('02/24/2017'), duration: 2, progress: '0', priority: 'High', approved: false },
                        { taskID: 26, taskName: 'Bug fix', startDate: new Date('02/25/2017'), endDate: new Date('02/26/2017'), duration: 2, progress: '0', priority: 'Low', approved: false },
                        { taskID: 27, taskName: 'Customer review meeting', startDate: new Date('02/27/2017'), endDate: new Date('02/28/2017'), duration: 2, progress: '0', priority: 'Critical', approved: true },
                        { taskID: 28, taskName: 'Phase 2 complete', startDate: new Date('02/28/2017'), endDate: new Date('02/28/2017'), duration: 0, priority: 'Normal', approved: false }

                    ]
                }]
            },

            {
                taskID: 29,
                taskName: 'Phase 3',
                startDate: new Date('02/17/2017'),
                endDate: new Date('02/27/2017'),
                priority: 'Normal',
                approved: false,
                duration: 11,
                subtasks: [{
                    taskID: 30,
                    taskName: 'Implementation Module 3',
                    startDate: new Date('02/17/2017'),
                    endDate: new Date('02/27/2017'),
                    priority: 'High',
                    approved: false,
                    duration: 11,
                    subtasks: [
                        { taskID: 31, taskName: 'Development Task 1', startDate: new Date('02/17/2017'), endDate: new Date('02/19/2017'), duration: 3, progress: '50', priority: 'Low', approved: true },
                        { taskID: 32, taskName: 'Development Task 2', startDate: new Date('02/17/2017'), endDate: new Date('02/19/2017'), duration: 3, progress: '50', priority: 'Normal', approved: false },
                        { taskID: 33, taskName: 'Testing', startDate: new Date('02/20/2017'), endDate: new Date('02/21/2017'), duration: 2, progress: '0', priority: 'Critical', approved: true },
                        { taskID: 34, taskName: 'Bug fix', startDate: new Date('02/24/2017'), endDate: new Date('02/25/2017'), duration: 2, progress: '0', priority: 'High', approved: false },
                        { taskID: 35, taskName: 'Customer review meeting', startDate: new Date('02/26/2017'), endDate: new Date('02/27/2017'), duration: 2, progress: '0', priority: 'Normal', approved: true },
                        { taskID: 36, taskName: 'Phase 3 complete', startDate: new Date('02/27/2017'), endDate: new Date('02/27/2017'), duration: 0, priority: 'Critical', approved: false },
                    ]
                }]
            }
        ]
    }
];

I am hoping to achieve a table that handles data recursively, placing subtasks under their parent tasks, and sub-sub-tasks under their parent subtasks, etc, etc.

currently, I can only drill down one level in with non dynamic markup. there are plenty of examples of how this works with unordered lists, but these methods don't seem to work with tables as well. dropping a selector tag on inside it's own markup causes issues.


回答1:


A recursive component it's only make a component like

@Component({
  selector: 'recursive-component',
  template:`
  <ng-container>
  {{index}}--some template--, e.g {{item.title}}
  </ng-container>
     <ng-container *ngIf="item.children">
      <recursive-component *ngFor="let item of item.children" [index]="index+1" [item]="item">
      </recursive-component>
     <ng-container>  `

})
export class MenuComponent {
  @Input() item:any
  @Input() index:number=0;
}

I like know the "level of recursion" it's the reason of this "index". WE must use to avoid create extra html tags, and take a look too, that we must feed the component with an object with children, so. if we has an array, like

items = [{ title: "item1" },
    {
      title: "item2", children: [
        { title: "item 2.1" },
        { title: "item 2.2" }]
    },
    { title: "item3" }
    ]

We can create "on fly" an object with children and our .html will be

<recursive-component [item]="{children:menu}"  ></recursive-component>

But you can not do using tables, because always you'll get a table inside another table or a tr inside another tr

If you need use table you can take another aproach: generate an array based in your data and if is expanded or not. So, you can do a function like

  getItems(data, items,index) {
    data.forEach(x => {
      if (!items)
        items=[];
      items.push(x);
      items[items.length-1].index=index

      if (x.subtasks && x.expanded)
        this.getItems(x.subtasks,items,index+1);
    }
    )
    return items;
  }

Then you can simple

//In your .html
<table>
  <tr (click)="expanded(item)" *ngFor="let item of items">
    <td>{{item.taskID}}</td>
    <td>{{item.taskName}}</td>
  </tr>
  </table>

//And in your .ts
  ngOnInit()
  {
    this.items=this.getItems(this.sampleData,null,0)
  }

  expanded(item:any)
  {
    item.expanded=!item.expanded;
    this.items=this.getItems(this.sampleData,null,0)

  }

In stackblitz, you can see the two options

NOTE: I simple use div (click) or tr (click) to change the "expanded" property.

NOTE2: In the stackblitz I use the "index" to change the margin or the padding of the elements



来源:https://stackoverflow.com/questions/55305905/how-can-i-create-a-nested-recursive-table-that-can-drill-down-x-levels-in-angul

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!