如何使用 ngFor 动态生成 HTML Table。在 Angular

How To dynamically generate an HTML Table using ngFor. in Angular

我正在尝试动态生成以下 html table,如屏幕截图所示

我能够使用虚拟数据手动创建 table,但我的问题是我试图组合多个数据源以实现此 HTML table 结构.

完整示例参见 STACKBLITZ

数据如下所示(关注活动字段):

    let data = {

id: '60bf06e6fc8f613117de1db9',
    activities: {
      '0': ['Power', 4, '', 2.5, '', 0, ''],
      '1': ['Attitude', 2, '', 3, '', 0, ''],
      '2': ['NR', 4.5, '', 1.5, '', 0, ''],
      '3': ['FMS', 4, '', 4, '', 0, ''],
      '4': ['Automation', 2.5, '', 2.5, '', 0, ''],
      '5': ['Path', 4.5, '', 2.5, '', 0, ''],
      '6': ['Systems', 2, '', 2.5, '', 0, ''],
      '7': ['Environment', 4.5, '', 2.5, '', 0, ''],
      '8': ['Planning', 2, '', 2.5, '', 0, ''],
      '9': ['Co-ordinate', 4.5, '', 3, '', 0, ''],
      '10': ['Prioritize', 2.5, '', 3, '', 0, ''],
      '11': ['Workload', 4.5, '', 2.5, '', 0, ''],
      '12': ['Crew', 4, '', 3, '', 0, ''],
      '13': ['ATC', 2.5, '', 3, '', 0, ''],
      '14': ['Identify', 4, '', 2.5, '', 0, ''],
      '15': ['Ass. Risk', 2, '', 4.5, '', 0, ''],
      '16': ['Checklist', 4, '', 2.5, '', 0, ''],
      '17': ['Analysis', 3, '', 3, '', 0, '']
    }}

活动字段共有18个活动。每一个都由其 id 和一系列活动标识。例如'0': ['Power', 4, '', 2.5, '', 0, ''],。 '0'代表activityPower的id。 4 代表 Attempt1 - Grade & '' 代表 Attempt1 - Note;等(请参阅屏幕截图进行说明)。

完整的活动列表存储在不同的 variable/file 中并具有以下结构。

Component.ts

        this.activities = [{
                "id": 0,
                "activity": "Power",
                "subject": "Control",
                "icon": "icon-link"
            },
            {
                "id": 1,
                "activity": "Attitude",
                "subject": "Control",
                "icon": "icon-link"
            },{...}]
    this.groupedActivities = customGroupByFunction(this.activities, 'subject');
this.colors = {Control: '#bfbfbf', 'AFCS': '#bfbfbf', ...}
    this.activitiesListKey = Object.keys(this.activitiesList);

下面是我的 html 代码。

        <table>
        <caption>Session Summary</caption>
        <tr>
          <th style="width: 40%"></th>
          <th style="width: auto"></th>
          <!-- Loop through AttemptCount variable
            to populate this heading -->
          <th style="width: auto" colspan="2" *ngFor="let attempt of attemptCounts(3)">Attempt {{attempt}}</th>
    
        </tr>
    
        <tr>
          <th style="width: 40%">Subject Grouping</th>
          <th style="width: auto"></th>
    
          <!-- Populate these heading using the formula
            AttemptCount * 2 (columns - Grade & Note) -->
          <ng-container *ngFor="let attemptLabel of attemptCounts(3)">
    
            <th style="width: auto">Grade</th>
            <th style="width: auto">Note</th>
          </ng-container>
        </tr>
    
        <!-- Loop through all Activity Subjects
        And create heading accordingly. 6 Main Subjects so far -->
    
        <!-- Next Subject -->
    
        <ng-container *ngFor="let id of activitiesListKey">
          <tr>
    
            <!-- Generate [Rowspan] = (Subject.Array.Length + 1) -->
            <th style="width: 40%;" rowspan="4" [style.background]="colors[id]">
              {{id}}</th>
    
            <!-- Loop through each Subject.Array Elements
            and populate -->
          <tr style="width: auto">
            <th>Power</th>
            <td>2.6</td>
            <td>Can Improve</td>
            <td>5.0</td>
            <td>Excellent</td>
            <td>4.5</td>
            <td>Regressed</td>
          </tr>
          <tr style="width: auto">
            <th>Attitude</th>
            <td>4.0</td>
            <td>Fantastic</td>
            <td>4.5</td>
            <td>Getting Better</td>
            <td>5.0</td>
            <td>Nice</td>
          </tr>
          <tr style="width: auto">
            <th>NR</th>
            <td>2.6</td>
            <td>Can Improve</td>
            <td>5.0</td>
            <td>Excellent</td>
            <td>4.5</td>
            <td>Regressed</td>
          </tr>
          <!-- </tr> -->
        </ng-container>
    
      </table>

N.B: attemptCounts(n) 只是一个函数,return 是一个包含 n 个元素的数组。例如 attemptCounts(3) 将 return [0,1,2]

如果 table 更容易生成,我愿意更改 data.activities 的结构。因此,如果有人有适用于不同数据模型的解决方案,请分享。

值“变得更好”、“优秀”是由讲师从表单字段中输入的。所以它们可以是常量,也可以不是。或者甚至只是空字符串。那也行。

谁能帮我动态生成这个table?几天来我一直在为此苦苦挣扎,我似乎找不到适合我的解决方案。

所以,请帮助有困难的朋友。

哦,如果你能改变你的数据结构,请做。

export interface Attempt {
  grade: number;
  note: string;
}

export interface Activity {
  name: string;
  attempts: Attempt[];
}

// in use
let activities: Activity[] = [
  {
    name: 'Power',
    attempts: [
      { grade: 3.5, note: 'Could do better' },
      { grade: 4.5, note: 'Better' },
      { grade: 5, note: 'Passed' },
    ]
  },
  {
    name: '',
    // etc…
  }
]

行分组的问题仍然存在,但像这样的结构将更容易管理。

一种在不改变数据结构的情况下使其工作的可能方法。

首先是一些接口:

interface Activity {
  id: number;
  activity: string;
  subject: string;
  icon: string;
}

interface Attempt {
  grade: number;
  note: string;
}

interface ActivityAttemp {
  activityName: string;
  attempts: Attempt[];
}

interface ActivitiesBySubject {
  subject: string;
  activities: ActivityAttemp[];
}

data.activities计算尝试次数:

attemptCounts: number[] = [];

const count =
  ((Object.values(this.data.activities)[0] as string[]).length - 1) / 2;
for (let i = 1; i <= count; ++i) {
  this.attemptCounts.push(i);
}

在将数据提供给 Angular 以对其进行一些预处理之前:

this.subjects.forEach((subject: string) => {
  this.activitiesBySubject.push({
    subject,
    activities: this.activities
      .filter((act: Activity) => act.subject === subject)
      .map((act: Activity) => {
        return {
          activityName: act.activity,
          attempts: this.getAttemptsForActivity(act.activity)
        };
      })
  });
});

我们的想法是将所有需要的数据放在一个地方,这样 HTML 模板变得更加简单:

<div style="overflow: auto">
  <table>
    <caption>Session Summary</caption>
    <tr>
      <th style="width: 40%"></th>
      <th style="width: auto"></th>
      <th style="width: auto" colspan="2" *ngFor="let attempt of attemptCounts">Attempt {{attempt}}</th>
    </tr>

    <tr>
      <th style="width: 40%">Subject Grouping</th>
      <th style="width: auto">Activity</th>

      <ng-container *ngFor="let attemptLabel of attemptCounts">
        <th style="width: auto">Grade</th>
        <th style="width: auto">Note</th>
      </ng-container>
    </tr>


    <ng-container *ngFor="let actsBySubj of activitiesBySubject">

      <tr>
        <td [attr.rowspan]="actsBySubj.activities.length + 1">
          {{ actsBySubj.subject }}
        </td>
      </tr>

      <tr *ngFor="let activity of actsBySubj.activities">
        <td>
          {{ activity.activityName }}
        </td>

        <ng-container *ngFor="let attempt of activity.attempts">

          <td>
            {{attempt.grade}}
          </td>
          <td>
            {{attempt.note}}
          </td>

        </ng-container>

      </tr>

    </ng-container>

  </table>
</div>

工作Stackblitz

感谢@GaurangDhorda 的初始 Stackblitz。

更新

要使用下拉列表,必须将处理移至单独的方法:

  private calcTableData() {
    // reset
    this.attemptCounts = [];
    this.activitiesBySubject = [];

    // find exercise from "selectedExerciseId"
    const selectedExercise = this.data.find(
      (x: any) => x.id === this.selectedExerciseId
    );

    if (!selectedExercise) {
      return; // unable to find exercise
    }

    // calc attempt count eg.: 1, 2, 3
    for (let i = 1; i <= selectedExercise.attemptCount; ++i) {
      this.attemptCounts.push(i);
    }

    this.subjects.forEach((subject: string) => {
      this.activitiesBySubject.push({
        subject,
        activities: this.activities
          .filter((act: Activity) => act.subject === subject)
          .map((act: Activity) => {
            return {
              activityName: act.activity,
              attempts: this.getAttemptsForActivity(
                selectedExercise,
                act.activity
              )
            };
          })
      });
    });

    console.log(this.activitiesBySubject);
  }

该方法使用一个成员变量selectedExerciseId来获取选中的项。调色板也移动到:

  colors: { [key: string]: string } = {
    Control: '#bfbfbf',
    AFCS: '#fac090',
    'Situational Awareness': '#c4bd97',
    'Leadership / Teamwork': '#d99694',
    Communication: '#c3d69b',
    'PB. Solving / Decision Making': '#b3a2c7'
  };

事件处理程序 onChange 和生命周期挂钩 ngOnit 仅调用 calcTableData:

  ngOnInit(): void {
    this.calcTableData();
  }

  onChange(e: Event) {
    this.calcTableData();
  }

已更新Stackblitz