Как суммировать значение из поля вложенного массива Dynami c и создать объект для каждого поля ввода - PullRequest
0 голосов
/ 28 мая 2020

Я пытаюсь разработать числовую систему оценок для курса, используя Angular 8. Реальный сценарий довольно сложен, поэтому я разделил часть и создал новый проект, чтобы объяснить, в чем проблема. Если вы посмотрите на изображение, вы поймете, чего я пытаюсь достичь sh. Предположим, это классная форма одного курса. Для курса могут быть разные экзамены. Студенты, которые зачислены на этот курс, будут посещать указанные c экзамены для этого курса, а затем учитель отправит номер, используя эту форму. Предположим, что для первого столбца первой строки (Промежуточный экзамен) форма не должна позволять вам ставить больше 25 или меньше нуля. Если вы поместите 20 в поле «Промежуточный экзамен», тогда Total покажет 20, GP покажет 0,00, оценка покажет F. Затем, если вы поставите 30 в поле итогового экзамена, общее значение будет обновлено до 50, GP будет обновлен до 0,00 и будет обновлено до F. Опять же, если вы поместите 40 в поле Оценка, общее значение будет обновлено до 90, GP будет обновлено до 4,00 и будет обновлено до A. То же самое произойдет для всей строки . Если вы нажмете кнопку «Сохранить», числа будут отправлены.

enter image description here

Теперь я описываю код, который я пробовал, и свою проблему, здесь studentList, examList и gradePointList массивы заполнят данные для курса из серверной службы. Я создал метод populateSomeDemoData для генерации этих данных. studentList - это массив объекта Student, который содержит идентификатор и имя ученика. ExamList - это массив объекта Exam, который содержит идентификатор экзамена, название экзамена и общие оценки за этот экзамен. GradePointList содержит диапазон количества буквенных оценок и оценок. Предположим, что если общая оценка находится между 90 (minimumNumber) и 100 (maximumNumber), то балл будет 4,00, а буквенная оценка будет A. Я попытался создать форму gradeForm и применил al oop в списке examList для каждой строки, чтобы создать поле ввода. Но для этого я не смог получить доступ к определенному полю c с помощью элемента управления формой, поэтому мне не удалось рассчитать общие оценки и балл для строки. Также по той же причине мне не удалось создать список объектов Grade, который будет отправлен на бэкэнд. 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;
  }
}

I создали StackBlitz для лучшего понимания кода. Нажмите на эту ссылку, чтобы просмотреть код. Щелкните здесь

Я видел много сообщений, связанных с массивами форм, но мой сценарий совсем другой. Здесь у меня нет кнопки Добавить в массив pu sh или вычислить значения. И есть фиксированные формы на все случаи жизни. Но в моем случае массив также будет сгенерирован из списка, потому что excList - это Dynami c.

Я полагаю, что у меня проблемы с созданием в форме, или мои подходы неверны. Пожалуйста, помогите мне достичь sh моей цели: получить общие оценки и составить список оценок. Было бы признательно. Заранее спасибо.

Ответы [ 2 ]

1 голос
/ 29 мая 2020

Хорошо, я вижу, что для удовлетворения ваших требований вы должны использовать вложенные массивы, вам необходимо следовать этой структуре или, по крайней мере, 2-мерный массив, grades [] []: grades [studentId] [excId], вы будете когда вы читаете код, понимаете больше, в вашем коде много чего не хватает, например, проверки для оценок и расчет для полей total и GP, я не буду беспокоиться об этих частях (я добавил образец для расчета и проверки, но вам нужно изменить его), но я покажу вам, как правильно заполнить группу форм и как ее использовать, имея представление Dynami c. Как я уже упоминал, вам нужен 2d-массив, для меня я пошел с дополнительной информацией, поэтому у меня есть массив объектов, у объекта есть 2 свойства, одно - это элемент управления студентом (удерживайте идентификатор студента), а один - массив оценок для этого студент здесь l oop, который я использовал для заполнения моей группы форм

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, от одной до l oop строк студентов и от одной до l oop через ваши оценки (столбцы)

<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));
}

рабочий stackblitz здесь

Подробнее о массивы форм и реактивные формы

0 голосов
/ 30 мая 2020

Спасибо Munzer. Он дал мне идею и показал образец. По его ответу я решил проблему полностью. Поскольку я решил это полностью, я делюсь подробностями, если это кому-то поможет. Чтобы решить проблему, я создал форму динамически. Для каждого студента я создал группу и создал элемент управления формой для каждого экзамена. Затем я oop просматриваю несколько списков студентов и снова oop просматриваю поля оценок каждой группы студентов и добавляю элемент управления формой. Вы получите ясное представление о методе createGradeForm. Вот ссылка на StackBlitz: https://stackblitz.com/edit/grade-upload-9nnr8p

TS File:

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:

<code><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}}

Надеюсь, этот пример поможет другим, если у кого-то будет похожий сценарий.

...