如何对动态嵌套数组字段的值求和并为每个输入字段创建一个对象
How to sum the value from dynamic nested array field and create a object for per input field
我正在尝试使用 Angular 8 为一门课程开发一个数字评分系统。实际情况非常复杂,所以我将这部分分开并创建了一个新项目来解释我的问题所在。如果你看一下图像,你就会明白我正在努力完成什么。假设这是一门课程的成绩单。对于一门课程,可以有不同的考试。注册本课程的学生将参加本课程的特定考试,然后老师将使用此表格提交号码。假设,对于第一行第一列(Mid Exam),表格不应允许您提供超过 25 分或少于零分。如果您在 Mid Exam 字段中输入 20,那么 Total
将显示 20,GP 将显示 0.00,Grade 将显示 F。然后如果您在 Final exam 字段中输入 30,Total 值将更新为 50,GP 将将更新为 0.00 并将更新为 F。同样,如果您在评估字段中输入 40,则总值将更新为 90,GP 将更新为 4.00 并将更新为 A。同样的事情将发生在所有人身上该行。如果您单击“保存”按钮,将提交号码。
现在我正在描述我尝试过的代码和我的问题,这里 studentList
、examList
和 gradePointList
数组将从后端服务填充课程数据。我创建了 populateSomeDemoData
方法来生成此数据。 studentList 是包含学生 ID 和姓名的 Student 对象的数组。 examList 是 Exam 对象的数组,其中包含考试 ID、考试名称和本次考试的总分。 gradePointList 包含字母等级和等级点数的范围。假设总分在 90(最小值)和 100(最大值)之间,则成绩点将为 4.00,字母等级将为 A。我尝试创建一个表格 gradeForm
并在 examList 上为每行应用一个循环以创建输入字段。但是为此,我无法使用表单控件访问特定字段,这就是我无法计算一行的总分和成绩点的原因。同样出于同样的原因,我未能创建将发送到后端的成绩对象列表。 Grade
是每个字段的对象,将包含学生、考试和数字。如果一个字段被更新,这个成绩对象也会被更新。如果保存按钮触发,这个号码列表将被传递到后端。
HTML部分是:
<form [formGroup]="gradeForm" (ngSubmit)="saveGrade()">
<table>
<thead>
<tr>
<th> #</th>
<th>Student</th>
<th *ngFor="let exam of examList">{{exam.name}} ({{exam.marks}}%)</th>
<th>Total</th>
<th>GP</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let student of studentList; let i = index">
<th> {{ i + 1 }} </th>
<th> {{ student.id }} <br/> {{student.name}} </th>
<th *ngFor="let grade of gradeFields.controls; let j = index" formArrayName="gradeFields">
<input type="number" [formControlName]="j">
</th>
<th>??</th>
<th>??</th>
<th>??</th>
</tr>
</tbody>
</table>
<div class="wrapper">
<button class="button">Save Grade</button>
</div>
</form>
TypeScript 部分是:
export class AppComponent {
public gradeForm: FormGroup;
// This arrays will populate dynamically from the database
public examList: Exam[] = [];
public studentList: Student[] = [];
public gradePointList: GradePoint[] = [];
public gradeList: Grade[] = [];
constructor(private formBuilder: FormBuilder) {
this.populateSomeDemoData();
this.createGradeForm();
}
createGradeForm(): void {
this.gradeForm = this.formBuilder.group({
gradeFields: this.formBuilder.array([
this.formBuilder.control('')
])
});
for (let i = 0; i < this.examList.length - 1; i++) {
this.gradeFields.push(this.formBuilder.control(''));
}
}
get gradeFields() {
return this.gradeForm.get('gradeFields') as FormArray;
}
saveGrade(): void {
console.log(this.gradeForm.get('gradeFields').value);
console.log(this.gradeList);
console.log('Grade List Saved');
}
populateSomeDemoData(): void {
// Static value for populating arrays for easy explanation
const midTerm = new Exam();
midTerm.id = '1';
midTerm.name = 'Mid Exam';
midTerm.marks = 25;
const finalExam = new Exam();
finalExam.id = '2';
finalExam.name = 'Final Exam';
finalExam.marks = 30;
const assessmentExam = new Exam();
assessmentExam.id = '3';
assessmentExam.name = 'Assessment';
assessmentExam.marks = 40;
const attendance = new Exam();
attendance.id = '4';
attendance.name = 'Attendance';
attendance.marks = 5;
this.examList.push(midTerm);
this.examList.push(finalExam);
this.examList.push(assessmentExam);
this.examList.push(attendance);
// Static value for populating arrays for easy explanation
const student1 = new Student();
student1.id = '14101561';
student1.name = 'Petey Cruiser';
const student2 = new Student();
student2.id = '14112201';
student2.name = 'Bob Frapples';
const student3 = new Student();
student3.id = '14112202';
student3.name = 'Paul Molive';
const student4 = new Student();
student4.id = '16113004';
student4.name = 'Anna Mull';
const student5 = new Student();
student5.id = '16113005';
student5.name = 'Gail Forcewind';
this.studentList.push(student1);
this.studentList.push(student2);
this.studentList.push(student3);
this.studentList.push(student4);
this.studentList.push(student5);
// Static value for populating arrays for easy explanation
const aGradePoint = new GradePoint();
aGradePoint.minimumNumber = 90;
aGradePoint.maximumNumber = 100;
aGradePoint.gradePoint = 4;
aGradePoint.letterGrade = 'A';
const bGradePoint = new GradePoint();
bGradePoint.minimumNumber = 80;
bGradePoint.maximumNumber = 89;
bGradePoint.gradePoint = 3.5;
bGradePoint.letterGrade = 'B';
const cGradePoint = new GradePoint();
cGradePoint.minimumNumber = 70;
cGradePoint.maximumNumber = 79;
cGradePoint.gradePoint = 3;
cGradePoint.letterGrade = 'C';
const dGradePoint = new GradePoint();
dGradePoint.minimumNumber = 60;
dGradePoint.maximumNumber = 69;
dGradePoint.gradePoint = 2.5;
dGradePoint.letterGrade = 'D';
const fGradePoint = new GradePoint();
fGradePoint.minimumNumber = 0;
fGradePoint.maximumNumber = 59;
fGradePoint.gradePoint = 0;
fGradePoint.letterGrade = 'F';
this.gradePointList.push(aGradePoint);
this.gradePointList.push(bGradePoint);
this.gradePointList.push(cGradePoint);
this.gradePointList.push(dGradePoint);
this.gradePointList.push(fGradePoint);
}
}
export class Student {
public id: string;
public name: string;
constructor(student?) {
student = student || {};
this.id = student.id || null;
this.name = student.name || null;
}
}
export class Exam {
public id: string;
public name: string;
public marks: number;
constructor(exam?) {
exam = exam || {};
this.id = exam.id || null;
this.name = exam.name || null;
this.marks = exam.marks || 0;
}
}
export class GradePoint {
public minimumNumber: number;
public maximumNumber: number;
public gradePoint: number;
public letterGrade: string;
constructor(gradePoint?) {
gradePoint = gradePoint || {};
this.minimumNumber = gradePoint.minimumNumber || 0;
this.maximumNumber = gradePoint.maximumNumber || 59;
this.gradePoint = gradePoint.gradePoint || 0;
this.letterGrade = gradePoint.letterGrade || 'F';
}
}
export class Grade {
public student: Student;
public exam: Exam;
public number: number;
constructor(grade?) {
grade = grade || {};
this.student = grade.student || null;
this.exam = grade.exam || null;
this.number = grade.number || null;
}
}
为了更好地理解代码,我创建了一个 StackBlitz。请点击这个link查看代码。 Click here
我看到了很多与表单数组相关的 post 但我的情况却大不相同。在这里我没有任何添加按钮来推送数组或计算值。所有情况都有固定的表格。但在我的例子中,数组也将从列表中生成,因为 examList 是动态的。
我假设我在表单上创建时遇到问题或者我的方法是错误的。请帮助我完成我的 objective 生成总分和创建成绩列表。我们将不胜感激。提前致谢。
好的,我知道为了满足您的要求,您必须使用嵌套数组,您需要遵循这种结构,或者至少是一个二维数组,
grades[][]: grades[studentId][examId],看了代码你会明白很多,你的代码里少了很多,比如marks的验证,total和GP字段的计算,我不会打扰这些部分(我添加了一个用于计算和验证的示例,但您需要对其进行修改),但我将向您展示如何正确填充表单组,以及如何使用它,具有动态视图。
正如我提到的,你需要一个二维数组,对我来说,我提供了更多信息,所以我有一个对象数组,该对象有 2 个属性,一个是学生控制(持有学生 ID),一个是成绩数组学生
这是我用来填充表单组的循环
createGradeForm(): void {
this.gradeForm = this.formBuilder.group({
gradeFields: this.formBuilder.array([])
});
//loop all students
for (let b = 0; b < this.studentList.length; b++) {
// create a form group to hold student data
const userGroup = this.formBuilder.group({
student: this.formBuilder.control(this.studentList[b].id),
grades: this.formBuilder.array([])
})
//populate grades form array
for (let i = 0; i < this.examList.length; i++) {
//Bonus, I added a validator for you, max value is the mark, please apply min and other validations on your own.
userGroup.get('grades').push(this.formBuilder.control('', [Validators.max(this.examList[i].marks)]));
}
// add the form group to your array
this.gradeFields.push(userGroup);
}
//Check result here
console.log(this.gradeFields);
}
对于您的视图,您将有嵌套的 ng-for,一个用于循环学生的行,一个用于循环您的成绩(列)
<tbody>
<!-- loop through the higher level array (students) -->
<ng-container *ngFor="let student of gradeFields.controls; let i = index" formArrayName='gradeFields'>
<ng-container [formGroupName]='i'>
<tr>
<th> {{ i + 1 }} </th>
<-- this is student control -->
<th> {{ student.get('student').value }} </th>
<-- loop through your grades array -->
<th *ngFor="let grade of student.get('grades').controls; let j = index" formArrayName="grades">
<-- bonus here, I added an example for validation, add style to your css for .inValid to see when invalid -->
<input [class.inValid]='!grade.valid' type="number" [formControlName]="j">
</th>
<-- one more bonus, an example of calculation getTotal -->
<th>{{getTotal(i)}}</th>
<th>??</th>
<th>??</th>
</tr>
</ng-container>
</ng-container>
</tbody>
获取总函数
//It takes the index of the student, get all grades, and accumlate them, the logic is wrong, please modify it, I just sum all grades, you have to add the logic
getTotal(ind) {
const studentGrades = this.gradeFields.value[ind].grades;
return studentGrades.reduce((a, b) => Number(a) + Number(b));
}
感谢 Munzer。他给了我想法并给我看了一个样品。我按照他的回答彻底解决了问题。由于我完全解决了它,如果它对某人有帮助,我会分享细节。为了解决这个问题,我动态创建了表单。对于每个学生,我做了一个小组,并为每次考试创建了一个表单控件。然后我遍历多个学生列表并再次遍历每个学生组的标记字段并添加表单控件。您将从 createGradeForm
方法中获得清晰的思路。这是 StackBlitz link:https://stackblitz.com/edit/grade-upload-9nnr8p
TS 文件:
export class AppComponent {
public gradeForm: FormGroup;
// This arrays will populate dynamically from the database
public examList: Exam[] = [];
public studentList: Student[] = [];
public gradePointList: GradePoint[] = [];
constructor(private formBuilder: FormBuilder) {
this.populateSomeDemoData();
this.createGradeForm();
}
private createGradeForm(): void {
this.gradeForm = this.formBuilder.group({});
this.studentList.forEach(student => {
this.gradeForm.addControl(student.id, this.formBuilder.array([]));
this.examList.forEach(exam => {
const grade: Grade = new Grade();
grade.student = student;
grade.exam = exam;
grade.number = 0; // Replace the number if previously saved
const studentGrades: FormArray = this.gradeForm.get(student.id) as FormArray;
studentGrades.push(this.createGradeFormGroup(grade));
});
});
}
createGradeFormGroup(grade: Grade): FormGroup {
return this.formBuilder.group({
student: [grade.student, [Validators.required]],
exam: [grade.exam, [Validators.required]],
number: [grade.number, [Validators.required, Validators.min(-1), Validators.max(grade.exam.marks)]]
});
}
getMaxNumberValue(index: number): number {
if (this.examList[index]) {
return this.examList[index].marks;
}
return 0;
}
getTotalNumber(studentId: string): number {
let totalNumber = 0;
const studentWiseGradeList: any[] = this.gradeForm.getRawValue();
if (studentWiseGradeList && studentWiseGradeList[studentId]) {
studentWiseGradeList[studentId].forEach(grade => {
if (grade.student.id === studentId) {
if (grade.number < 0 || grade.number > grade.exam.marks) {
// Ignoring the error number
} else {
totalNumber += grade.number;
}
}
});
}
return totalNumber;
}
getGradePoint(studentId: string): number {
let gradePoint = 0;
const totalMarks = this.getTotalNumber(studentId);
this.gradePointList.forEach(gp => {
if (gp.minimumNumber <= totalMarks && gp.maximumNumber >= totalMarks) {
gradePoint = gp.gradePoint;
}
});
return gradePoint;
}
getLetterGrade(studentId: string): string {
let letterGrade = 'F';
const totalMarks = this.getTotalNumber(studentId);
this.gradePointList.forEach(gp => {
if (gp.minimumNumber <= totalMarks && gp.maximumNumber >= totalMarks) {
letterGrade = gp.letterGrade;
}
});
return letterGrade;
}
saveGrade(): void {
if (this.gradeForm.status === 'INVALID') {
alert('Grade has invalid entry');
return;
}
const gradeList: Grade[] = [];
const studentWiseGradeList: any[] = this.gradeForm.getRawValue();
if (studentWiseGradeList) {
for (const studentId in studentWiseGradeList) {
if (studentId) {
studentWiseGradeList[studentId].forEach(gradeObj => {
if (gradeObj.student.id === studentId) {
if (gradeObj.number < 0 || gradeObj.number > gradeObj.exam.marks) {
alert('Grade has invalid entry');
return;
} else {
const grade: Grade = new Grade();
grade.student = gradeObj.student;
grade.exam = gradeObj.exam;
grade.number = gradeObj.number;
gradeList.push(grade);
}
}
});
}
}
} else {
alert('Grade is invalid');
return;
}
console.log(gradeList);
console.log('Grade list saved');
}
}
HTML:
<form [formGroup]="gradeForm" (ngSubmit)="saveGrade()">
<table>
<thead>
<tr>
<th> #</th>
<th>Student</th>
<th *ngFor="let exam of examList">{{exam.name}} ({{exam.marks}}%)</th>
<th>Total</th>
<th>GP</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let student of studentList; let i = index">
<th> {{ i + 1 }} </th>
<th> {{ student.id }} <br/> {{student.name}} </th>
<th *ngFor="let grade of gradeForm.get(student.id)?.controls; let j = index">
<input required min="0" [max]="getMaxNumberValue(j)" [class.inValid]="!gradeForm.get(student.id)?.controls[j].valid" type="number" [formControl]="gradeForm.get(student.id)?.controls[j].controls.number" />
</th>
<th>{{getTotalNumber(student.id)}}</th>
<th>{{getGradePoint(student.id)}}</th>
<th>{{getLetterGrade(student.id)}}</th>
</tr>
</tbody>
</table>
<div class="wrapper">
<button class="button">Save Grade</button>
</div>
</form>
<pre>{{this.gradeForm.getRawValue() | json}}</pre>
如果有人遇到类似情况,我希望这个例子对其他人有所帮助。
我正在尝试使用 Angular 8 为一门课程开发一个数字评分系统。实际情况非常复杂,所以我将这部分分开并创建了一个新项目来解释我的问题所在。如果你看一下图像,你就会明白我正在努力完成什么。假设这是一门课程的成绩单。对于一门课程,可以有不同的考试。注册本课程的学生将参加本课程的特定考试,然后老师将使用此表格提交号码。假设,对于第一行第一列(Mid Exam),表格不应允许您提供超过 25 分或少于零分。如果您在 Mid Exam 字段中输入 20,那么 Total
将显示 20,GP 将显示 0.00,Grade 将显示 F。然后如果您在 Final exam 字段中输入 30,Total 值将更新为 50,GP 将将更新为 0.00 并将更新为 F。同样,如果您在评估字段中输入 40,则总值将更新为 90,GP 将更新为 4.00 并将更新为 A。同样的事情将发生在所有人身上该行。如果您单击“保存”按钮,将提交号码。
现在我正在描述我尝试过的代码和我的问题,这里 studentList
、examList
和 gradePointList
数组将从后端服务填充课程数据。我创建了 populateSomeDemoData
方法来生成此数据。 studentList 是包含学生 ID 和姓名的 Student 对象的数组。 examList 是 Exam 对象的数组,其中包含考试 ID、考试名称和本次考试的总分。 gradePointList 包含字母等级和等级点数的范围。假设总分在 90(最小值)和 100(最大值)之间,则成绩点将为 4.00,字母等级将为 A。我尝试创建一个表格 gradeForm
并在 examList 上为每行应用一个循环以创建输入字段。但是为此,我无法使用表单控件访问特定字段,这就是我无法计算一行的总分和成绩点的原因。同样出于同样的原因,我未能创建将发送到后端的成绩对象列表。 Grade
是每个字段的对象,将包含学生、考试和数字。如果一个字段被更新,这个成绩对象也会被更新。如果保存按钮触发,这个号码列表将被传递到后端。
HTML部分是:
<form [formGroup]="gradeForm" (ngSubmit)="saveGrade()">
<table>
<thead>
<tr>
<th> #</th>
<th>Student</th>
<th *ngFor="let exam of examList">{{exam.name}} ({{exam.marks}}%)</th>
<th>Total</th>
<th>GP</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let student of studentList; let i = index">
<th> {{ i + 1 }} </th>
<th> {{ student.id }} <br/> {{student.name}} </th>
<th *ngFor="let grade of gradeFields.controls; let j = index" formArrayName="gradeFields">
<input type="number" [formControlName]="j">
</th>
<th>??</th>
<th>??</th>
<th>??</th>
</tr>
</tbody>
</table>
<div class="wrapper">
<button class="button">Save Grade</button>
</div>
</form>
TypeScript 部分是:
export class AppComponent {
public gradeForm: FormGroup;
// This arrays will populate dynamically from the database
public examList: Exam[] = [];
public studentList: Student[] = [];
public gradePointList: GradePoint[] = [];
public gradeList: Grade[] = [];
constructor(private formBuilder: FormBuilder) {
this.populateSomeDemoData();
this.createGradeForm();
}
createGradeForm(): void {
this.gradeForm = this.formBuilder.group({
gradeFields: this.formBuilder.array([
this.formBuilder.control('')
])
});
for (let i = 0; i < this.examList.length - 1; i++) {
this.gradeFields.push(this.formBuilder.control(''));
}
}
get gradeFields() {
return this.gradeForm.get('gradeFields') as FormArray;
}
saveGrade(): void {
console.log(this.gradeForm.get('gradeFields').value);
console.log(this.gradeList);
console.log('Grade List Saved');
}
populateSomeDemoData(): void {
// Static value for populating arrays for easy explanation
const midTerm = new Exam();
midTerm.id = '1';
midTerm.name = 'Mid Exam';
midTerm.marks = 25;
const finalExam = new Exam();
finalExam.id = '2';
finalExam.name = 'Final Exam';
finalExam.marks = 30;
const assessmentExam = new Exam();
assessmentExam.id = '3';
assessmentExam.name = 'Assessment';
assessmentExam.marks = 40;
const attendance = new Exam();
attendance.id = '4';
attendance.name = 'Attendance';
attendance.marks = 5;
this.examList.push(midTerm);
this.examList.push(finalExam);
this.examList.push(assessmentExam);
this.examList.push(attendance);
// Static value for populating arrays for easy explanation
const student1 = new Student();
student1.id = '14101561';
student1.name = 'Petey Cruiser';
const student2 = new Student();
student2.id = '14112201';
student2.name = 'Bob Frapples';
const student3 = new Student();
student3.id = '14112202';
student3.name = 'Paul Molive';
const student4 = new Student();
student4.id = '16113004';
student4.name = 'Anna Mull';
const student5 = new Student();
student5.id = '16113005';
student5.name = 'Gail Forcewind';
this.studentList.push(student1);
this.studentList.push(student2);
this.studentList.push(student3);
this.studentList.push(student4);
this.studentList.push(student5);
// Static value for populating arrays for easy explanation
const aGradePoint = new GradePoint();
aGradePoint.minimumNumber = 90;
aGradePoint.maximumNumber = 100;
aGradePoint.gradePoint = 4;
aGradePoint.letterGrade = 'A';
const bGradePoint = new GradePoint();
bGradePoint.minimumNumber = 80;
bGradePoint.maximumNumber = 89;
bGradePoint.gradePoint = 3.5;
bGradePoint.letterGrade = 'B';
const cGradePoint = new GradePoint();
cGradePoint.minimumNumber = 70;
cGradePoint.maximumNumber = 79;
cGradePoint.gradePoint = 3;
cGradePoint.letterGrade = 'C';
const dGradePoint = new GradePoint();
dGradePoint.minimumNumber = 60;
dGradePoint.maximumNumber = 69;
dGradePoint.gradePoint = 2.5;
dGradePoint.letterGrade = 'D';
const fGradePoint = new GradePoint();
fGradePoint.minimumNumber = 0;
fGradePoint.maximumNumber = 59;
fGradePoint.gradePoint = 0;
fGradePoint.letterGrade = 'F';
this.gradePointList.push(aGradePoint);
this.gradePointList.push(bGradePoint);
this.gradePointList.push(cGradePoint);
this.gradePointList.push(dGradePoint);
this.gradePointList.push(fGradePoint);
}
}
export class Student {
public id: string;
public name: string;
constructor(student?) {
student = student || {};
this.id = student.id || null;
this.name = student.name || null;
}
}
export class Exam {
public id: string;
public name: string;
public marks: number;
constructor(exam?) {
exam = exam || {};
this.id = exam.id || null;
this.name = exam.name || null;
this.marks = exam.marks || 0;
}
}
export class GradePoint {
public minimumNumber: number;
public maximumNumber: number;
public gradePoint: number;
public letterGrade: string;
constructor(gradePoint?) {
gradePoint = gradePoint || {};
this.minimumNumber = gradePoint.minimumNumber || 0;
this.maximumNumber = gradePoint.maximumNumber || 59;
this.gradePoint = gradePoint.gradePoint || 0;
this.letterGrade = gradePoint.letterGrade || 'F';
}
}
export class Grade {
public student: Student;
public exam: Exam;
public number: number;
constructor(grade?) {
grade = grade || {};
this.student = grade.student || null;
this.exam = grade.exam || null;
this.number = grade.number || null;
}
}
为了更好地理解代码,我创建了一个 StackBlitz。请点击这个link查看代码。 Click here
我看到了很多与表单数组相关的 post 但我的情况却大不相同。在这里我没有任何添加按钮来推送数组或计算值。所有情况都有固定的表格。但在我的例子中,数组也将从列表中生成,因为 examList 是动态的。
我假设我在表单上创建时遇到问题或者我的方法是错误的。请帮助我完成我的 objective 生成总分和创建成绩列表。我们将不胜感激。提前致谢。
好的,我知道为了满足您的要求,您必须使用嵌套数组,您需要遵循这种结构,或者至少是一个二维数组, grades[][]: grades[studentId][examId],看了代码你会明白很多,你的代码里少了很多,比如marks的验证,total和GP字段的计算,我不会打扰这些部分(我添加了一个用于计算和验证的示例,但您需要对其进行修改),但我将向您展示如何正确填充表单组,以及如何使用它,具有动态视图。 正如我提到的,你需要一个二维数组,对我来说,我提供了更多信息,所以我有一个对象数组,该对象有 2 个属性,一个是学生控制(持有学生 ID),一个是成绩数组学生 这是我用来填充表单组的循环
createGradeForm(): void {
this.gradeForm = this.formBuilder.group({
gradeFields: this.formBuilder.array([])
});
//loop all students
for (let b = 0; b < this.studentList.length; b++) {
// create a form group to hold student data
const userGroup = this.formBuilder.group({
student: this.formBuilder.control(this.studentList[b].id),
grades: this.formBuilder.array([])
})
//populate grades form array
for (let i = 0; i < this.examList.length; i++) {
//Bonus, I added a validator for you, max value is the mark, please apply min and other validations on your own.
userGroup.get('grades').push(this.formBuilder.control('', [Validators.max(this.examList[i].marks)]));
}
// add the form group to your array
this.gradeFields.push(userGroup);
}
//Check result here
console.log(this.gradeFields);
}
对于您的视图,您将有嵌套的 ng-for,一个用于循环学生的行,一个用于循环您的成绩(列)
<tbody>
<!-- loop through the higher level array (students) -->
<ng-container *ngFor="let student of gradeFields.controls; let i = index" formArrayName='gradeFields'>
<ng-container [formGroupName]='i'>
<tr>
<th> {{ i + 1 }} </th>
<-- this is student control -->
<th> {{ student.get('student').value }} </th>
<-- loop through your grades array -->
<th *ngFor="let grade of student.get('grades').controls; let j = index" formArrayName="grades">
<-- bonus here, I added an example for validation, add style to your css for .inValid to see when invalid -->
<input [class.inValid]='!grade.valid' type="number" [formControlName]="j">
</th>
<-- one more bonus, an example of calculation getTotal -->
<th>{{getTotal(i)}}</th>
<th>??</th>
<th>??</th>
</tr>
</ng-container>
</ng-container>
</tbody>
获取总函数
//It takes the index of the student, get all grades, and accumlate them, the logic is wrong, please modify it, I just sum all grades, you have to add the logic
getTotal(ind) {
const studentGrades = this.gradeFields.value[ind].grades;
return studentGrades.reduce((a, b) => Number(a) + Number(b));
}
感谢 Munzer。他给了我想法并给我看了一个样品。我按照他的回答彻底解决了问题。由于我完全解决了它,如果它对某人有帮助,我会分享细节。为了解决这个问题,我动态创建了表单。对于每个学生,我做了一个小组,并为每次考试创建了一个表单控件。然后我遍历多个学生列表并再次遍历每个学生组的标记字段并添加表单控件。您将从 createGradeForm
方法中获得清晰的思路。这是 StackBlitz link:https://stackblitz.com/edit/grade-upload-9nnr8p
TS 文件:
export class AppComponent {
public gradeForm: FormGroup;
// This arrays will populate dynamically from the database
public examList: Exam[] = [];
public studentList: Student[] = [];
public gradePointList: GradePoint[] = [];
constructor(private formBuilder: FormBuilder) {
this.populateSomeDemoData();
this.createGradeForm();
}
private createGradeForm(): void {
this.gradeForm = this.formBuilder.group({});
this.studentList.forEach(student => {
this.gradeForm.addControl(student.id, this.formBuilder.array([]));
this.examList.forEach(exam => {
const grade: Grade = new Grade();
grade.student = student;
grade.exam = exam;
grade.number = 0; // Replace the number if previously saved
const studentGrades: FormArray = this.gradeForm.get(student.id) as FormArray;
studentGrades.push(this.createGradeFormGroup(grade));
});
});
}
createGradeFormGroup(grade: Grade): FormGroup {
return this.formBuilder.group({
student: [grade.student, [Validators.required]],
exam: [grade.exam, [Validators.required]],
number: [grade.number, [Validators.required, Validators.min(-1), Validators.max(grade.exam.marks)]]
});
}
getMaxNumberValue(index: number): number {
if (this.examList[index]) {
return this.examList[index].marks;
}
return 0;
}
getTotalNumber(studentId: string): number {
let totalNumber = 0;
const studentWiseGradeList: any[] = this.gradeForm.getRawValue();
if (studentWiseGradeList && studentWiseGradeList[studentId]) {
studentWiseGradeList[studentId].forEach(grade => {
if (grade.student.id === studentId) {
if (grade.number < 0 || grade.number > grade.exam.marks) {
// Ignoring the error number
} else {
totalNumber += grade.number;
}
}
});
}
return totalNumber;
}
getGradePoint(studentId: string): number {
let gradePoint = 0;
const totalMarks = this.getTotalNumber(studentId);
this.gradePointList.forEach(gp => {
if (gp.minimumNumber <= totalMarks && gp.maximumNumber >= totalMarks) {
gradePoint = gp.gradePoint;
}
});
return gradePoint;
}
getLetterGrade(studentId: string): string {
let letterGrade = 'F';
const totalMarks = this.getTotalNumber(studentId);
this.gradePointList.forEach(gp => {
if (gp.minimumNumber <= totalMarks && gp.maximumNumber >= totalMarks) {
letterGrade = gp.letterGrade;
}
});
return letterGrade;
}
saveGrade(): void {
if (this.gradeForm.status === 'INVALID') {
alert('Grade has invalid entry');
return;
}
const gradeList: Grade[] = [];
const studentWiseGradeList: any[] = this.gradeForm.getRawValue();
if (studentWiseGradeList) {
for (const studentId in studentWiseGradeList) {
if (studentId) {
studentWiseGradeList[studentId].forEach(gradeObj => {
if (gradeObj.student.id === studentId) {
if (gradeObj.number < 0 || gradeObj.number > gradeObj.exam.marks) {
alert('Grade has invalid entry');
return;
} else {
const grade: Grade = new Grade();
grade.student = gradeObj.student;
grade.exam = gradeObj.exam;
grade.number = gradeObj.number;
gradeList.push(grade);
}
}
});
}
}
} else {
alert('Grade is invalid');
return;
}
console.log(gradeList);
console.log('Grade list saved');
}
}
HTML:
<form [formGroup]="gradeForm" (ngSubmit)="saveGrade()">
<table>
<thead>
<tr>
<th> #</th>
<th>Student</th>
<th *ngFor="let exam of examList">{{exam.name}} ({{exam.marks}}%)</th>
<th>Total</th>
<th>GP</th>
<th>Grade</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let student of studentList; let i = index">
<th> {{ i + 1 }} </th>
<th> {{ student.id }} <br/> {{student.name}} </th>
<th *ngFor="let grade of gradeForm.get(student.id)?.controls; let j = index">
<input required min="0" [max]="getMaxNumberValue(j)" [class.inValid]="!gradeForm.get(student.id)?.controls[j].valid" type="number" [formControl]="gradeForm.get(student.id)?.controls[j].controls.number" />
</th>
<th>{{getTotalNumber(student.id)}}</th>
<th>{{getGradePoint(student.id)}}</th>
<th>{{getLetterGrade(student.id)}}</th>
</tr>
</tbody>
</table>
<div class="wrapper">
<button class="button">Save Grade</button>
</div>
</form>
<pre>{{this.gradeForm.getRawValue() | json}}</pre>
如果有人遇到类似情况,我希望这个例子对其他人有所帮助。