如何使用 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>
感谢@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
我正在尝试动态生成以下 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>
感谢@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