#angular #ngfor
#угловатый #нгфор
Вопрос:
* ngFor делает мое приложение чрезвычайно медленным из-за повторения тысяч строк. Он работает так, как и ожидалось, за исключением низкой производительности. Если выбранное или введенное значение будет изменено, его значение будет выделено красным цветом. Если _main.rolls повторяется, я могу извлечь все измененные значения и отправить их в API для обновления.
<tr *ngFor="let student of _main.students"> <!-- up to 250 students (rows). -->
<ng-container *ngFor="let date of _main.attendance_dates"> <!-- up to 5 school days (rows) in the same week. -->
<ng-container *ngFor="let roll of _main.rolls"> <!-- up to 5 days (rows) times 250 students per two columns (5*250*2) in the same week. -->
<ng-container *ngIf="student.id == roll.student_id amp;amp; date.date == roll.date">
<td [ngClass] = "roll.email_sent_datetime?'table-cell-attendance-code-emailed' : 'table-cell-attendance-code'">
<select [(ngModel)]="roll.attendance_type_seq_new" [ngClass]="roll.attendance_type_seq == roll.attendance_type_seq_new ? 'select-attendance-type' : 'select-attendance-type-changed'">
<option [value]="0" >Not selected</option>
<option *ngFor="let answer of _main.attendance_tyes" [value]="answer.seq">
{{answer.description}}
</option>
</select>
</td>
<td [ngClass] = "roll.email_sent_datetime?'table-cell-attendance-note-emailed' : 'table-cell-attendance-note'">
<input [(ngModel)]="roll.attendance_note_new" placeholder="" title="{{roll.attendance_note_new}}" [ngClass]="roll.attendance_note == roll.attendance_note_new ? 'input-attendance-note' : 'input-attendance-note-changed'"/>
</td>
</ng-container>
</ng-container>
</ng-container>
</tr>
* Я внес некоторые изменения. Сначала найдите индекс и отобразите элемент. Это достаточно быстро, но изменения не сохраняются. Я могу изменить только один элемент через массив _main.rolls. Это не detectChages(), запущенный для всех значений. Есть и еще одна проблема. Если значение выбора или ввода изменено, значение по-прежнему будет черным, а не красным.
<tr *ngFor="let student of _main.students"> <!-- up to 250 students (rows). -->
<ng-container *ngFor="let date of _main.attendance_dates"> <!-- up to 5 school days (rows) in the same week. -->
{{getRollSelectedIndex(student.id, date.date)}} <!-- fetch the index of the row in the rolls. -->
<!-- <ng-container *ngFor="let roll of _main.rolls"> up to 5 days (rows) times 250 students per two columns (5*250*2) in the same week. -->
<ng-container *ngIf="_index">
<td [ngClass] = "_main.rolls[_index].email_sent_datetime?'table-cell-attendance-code-emailed' : 'table-cell-attendance-code'">
<select [(ngModel)]="_main.rolls[_index].attendance_type_seq_new" [ngClass]="_main.rolls[_index].attendance_type_seq == _main.rolls[_index].attendance_type_seq_new ? 'select-attendance-type' : 'select-attendance-type-changed'">
<option [value]="0" >Not selected</option>
<option *ngFor="let answer of _main.attendance_tyes" [value]="answer.seq">
{{answer.description}}
</option>
</select>
</td>
<td [ngClass] = "_main.rolls[_index].email_sent_datetime?'table-cell-attendance-note-emailed' : 'table-cell-attendance-note'">
<input [(ngModel)]="_main.rolls[_index].attendance_note_new" placeholder="" title="{{_main.rolls[_index].attendance_note_new}}" [ngClass]="_main.rolls[_index].attendance_note == _main.rolls[_index].attendance_note_new ? 'input-attendance-note' : 'input-attendance-note-changed'"/>
</td>
</ng-container>
<!-- </ng-container> -->
</ng-container>
</tr>
В файле .ts.
getRollSelectedIndex(student_id: number, date: Date){
if (this._main.rolls != null )
{
this._index = this._main.rolls.findIndex(x=>x.student_id === student_id amp;amp; x.attendance_date === date)
}
}
Я не являюсь опытным программистом Angular. Мой подход может быть неправильным, и есть ли лучший способ решить проблему производительности в моем коде?
Ответ №1:
Да, есть лучший способ. Оба ваших решения требовали вложенных итераций, которых вы можете избежать, выполнив итерацию rolls
и создав объект, в котором ключами являются идентификаторы учащихся, а значением — рулон. Объекты предоставляют вам мгновенный доступ, поэтому вам не нужно перебирать его в шаблоне. Вы могли бы сделать что-то подобное, чтобы построить эту «карту»:
studentRollsMap = {};
this._main.rolls.forEach(roll => {
studentRollsMap[roll.student_id] = roll;
})
Затем в вашем шаблоне вы можете выполнить повторную итерацию students
и в то же время иметь возможность привязываться к studentRollsMap[student.id]
Это должно повысить производительность в 5-10 раз в соответствии с вашими комментариями
Комментарии:
1. При сопоставлении с понедельника по пятницу на каждого учащегося будет до пяти бросков. Могу ли я добавить пять рулонов в studentRollsMap[Student.id ]? detectChages() будет по-прежнему работать в этом случае? Я хотел бы отобразить измененное значение красным цветом в списке в стиле электронной таблицы. Кроме того, измененный список будет отправлен в API для обновления таблицы базы данных.
2. Я обнаружил очень интересный факт angular.
3. С помощью *ngFor=»roll of _main.rolls» и roll.note он запускает DetectChanges() для отдельных [входных] заметок правильно. С помощью _selectedRoll = _main.rolls[_index] и _selecteRoll.note запускается функция DetectChanges(), но ее целью будет последний добавленный [ввод]. Любое значение, измененное в любом [input] s, обновит последний [input] .
4. Я не уверен, что содержится в каждом
roll
из них, но если rolls — это массив всех посещений учащихся за день, и вам нужен массив или списки для каждого учащегося, то вы можете обновить эту строку выше доstudentRollsMap[roll.student_id] ? studentRollsMap[roll.student_id].push(roll) : studentRollsMap[roll.student_id] = [roll]
. Это инициализирует массив для каждого ключа на карте, если его еще нет, и добавит к нему, если он есть5. Спасибо. Это работает. Теперь от 40 секунд до менее чем 3 секунд поначалу. Обновление списка занимает менее одной секунды. Теперь довольно быстро. Однако все еще существует вложенный цикл. у ученика будет до 5 булочек. Чтобы удалить 2-й цикл, я попробовал другое ключевое значение массива. Но ни малейшего представления или понятия не имею, как это сделать.
Ответ №2:
Я чувствую ваше напряжение там, где вы находитесь, и я надеюсь, что мои слова могут быть информативными и полезными, чтобы найти наилучший выход из этой ситуации.
Что я считаю хорошим решением, так это выполнять все классы стиля кондиционирования в файле класса, а не в файле шаблона. Используется .map()
для перебора массива данных и добавления двух новых свойств к каждому объекту в массиве, одно для <td>
класса roll.email_sent_datetime?'table-cell-attendance-code-emailed' : 'table-cell-attendance-code'"
, а другое для <input>
класса стиля _main.rolls[_index].email_sent_datetime?'table-cell-attendance-note-emailed' : 'table-cell-attendance-note'"
.
Я считаю, что использование этого способа сократит время, затраченное на приведение стиля к нулю, и будет осязаемым.
Более того, вы можете перебирать все надежные массивы данных _main.students
, _main.attendance_dates
, и _main.rolls
так, что вы получите только один массив данных для использования внутри *ngFor=""
[также в файле class .ts]