DetectChanges() не запускается в массиве объектов в Angular

#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]