【问题标题】:How To dynamically generate an HTML Table using ngFor. in Angular如何使用 ngFor 动态生成 HTML 表。在角
【发布时间】:2021-08-29 12:37:51
【问题描述】:

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

我能够使用虚拟数据手动创建表格,但我的问题是我正在尝试组合多个数据源以实现此 HTML 表格结构。

完整示例请参见 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' 表示活动的 ID Power。 4 代表 Attempt1 - Grade & '' 代表 Attempt1 - Note;等(请参阅屏幕截图以进行说明)。

完整的活动列表存储在不同的变量/文件中,并具有以下结构。

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>

注意: attemptCounts(n) 只是一个返回 n 元素数组的函数。例如attemptCounts(3) 将返回[0,1,2]

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

“越来越好”、“优秀”的值由教师从表单字段中输入。所以它们可以是常数,也可以不是。甚至只是空字符串。这样也行。

有人可以帮我动态生成这张表吗?我已经为此苦苦挣扎了好几天,似乎找不到适合我的解决方案。

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

【问题讨论】:

  • @GaurangDhorda,你可以这样做this.groupedActivities = customGroupByFunction(this.activities, 'activity');
  • @robert,我刚刚更新了这个问题。谢谢
  • @AllJs stackblitz.com/edit/… 先把这个 stacblitz 分叉,然后在这个 stackblitz 中添加你所有的代码,然后在这里分享你更新的 stackblitz 代码链接。理解起来会更有帮助
  • 这里是堆栈闪电战@robert stackblitz.com/edit/angular-ivy-xkatki?file=src/app/…。我还用指向 stackblitz 的链接更新了问题。谢谢
  • @AllJs 这里是演示 stackblitz.com/edit/… 看看。

标签: javascript html angular html-table ngfor


【解决方案1】:

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

首先是一些接口:

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

【讨论】:

  • 嘿@Robert 谢谢你的回答。它按预期工作。我真的很感谢你在这方面的帮助。我的最后一个是:如何使用setColorPalette() 函数将之前创建的调色板应用于每个主题。谢谢。
  • 谢谢。这是我将配色方案应用于每个主题&lt;td [attr.rowspan]="actsBySubj.activities.length + 1" [style.background]="colors[actsBySubj.subject]"&gt; {{ actsBySubj.subject }} &lt;/td&gt; 所做的工作。我添加的只是[style.background]="colors[actsBySubj.subject]"
  • 嘿@Robert,请提问。我用一个新问题更新了这个问题。更新表格的下拉菜单。这是 stackblitz 链接stackblitz.com/edit/angular-ivy-rnkym4?file=src/app/…。谢谢
  • @AllJs 链接已损坏
【解决方案2】:

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

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…
  }
]

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

【讨论】:

    猜你喜欢
    • 2016-10-11
    • 2018-09-04
    • 2015-04-25
    • 1970-01-01
    • 2015-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    相关资源
    最近更新 更多