0

*ngFor makes my app extremely slow due to thousand rows iterated. It works as expected except for slow performance. If the select or input value is changed, its value will be in red. If the _main.rolls is iterated, I am able to fetch all values modified and send them to API to update.

<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 && 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>         

*I made some changes. Find an index first and display the item. It is faster enough but the changes are not stored. I can get only one item changed through _main.rolls array. It is not detectChages() fired on all values. There is another problem too. If the value of the select or input is changed, the value is still in black not in red.

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

In .ts file.

getRollSelectedIndex(student_id: number, date: Date){
    if (this._main.rolls != null )
    {
      this._index = this._main.rolls.findIndex(x=>x.student_id === student_id && x.attendance_date === date)
    }
  }

I am not an experienced Angular programmer. My approach could be wrong and is there a better way to tackle the performance issue in my code?

2 Answers 2

1

Yes, there's a better way. Both of your solutions required nested iterations, which you can avoid by iterating over rolls and creating an object where the keys are the student id's and the value is the roll. Objects give you instant access, so you don't have to iterate over it in the template. You could do something like this to build this 'map':

studentRollsMap = {};
this._main.rolls.forEach(roll => {
    studentRollsMap[roll.student_id] = roll;
})

Then in your template you can iterate over students and at the same time be able to bind to studentRollsMap[student.id]

This should improve performance by 5 to 10 times according to your comments

Sign up to request clarification or add additional context in comments.

7 Comments

With the mapping, there will be up to five rolls per student due to Monday to Friday. Can I add five rolls into studentRollsMap[Student.id]? detectChages() will still work in that case? I'd like to display a modified value in red in the spreadsheet-style list. Also, the modified list will be sent to the API for updating a database table.
I found a very interesting fact of angular.
With *ngFor="roll of _main.rolls" and roll.note, it fires detectChanges() on individual [input] of the notes correctly. With _selectedRoll = _main.rolls[_index] and _selecteRoll.note, it fires detectChanges() but its target will be the last [input] added. Any value modified in any [input]s will update the last [input].
I'm not sure what's in each roll, but if rolls is an array of all the students attendance per day and you want an array or rolls for each student then you can update that line above to studentRollsMap[roll.student_id] ? studentRollsMap[roll.student_id].push(roll) : studentRollsMap[roll.student_id] = [roll]. That will initialize an array for each key in the map if there isn't one already and add to it if there is
Thanks. It works. Now 40 seconds to less than 3 seconds at first. Refreshing the list is less than one second. Pretty fast now. There is still a nested loop though. a student will have up to 5 rolls. To remove the 2nd loop, I tried another key value of the array. But no idea or no clue how to.
|
0

I can feel your tense where you are and I hope that my words might be informative and helpful to find the best way out of this.

What I think as a good solution, is to do all the conditioning style classes in the class file not in the template file. Using .map() to iterate over the array of data and adding two new properties to each object in the array one for the <td> class roll.email_sent_datetime?'table-cell-attendance-code-emailed' : 'table-cell-attendance-code'" and another one for the <input> style class _main.rolls[_index].email_sent_datetime?'table-cell-attendance-note-emailed' : 'table-cell-attendance-note'".

I believe that using this way will shrink the time elapsed in conditioning the style to zero and will be touchable.

Even more, you can iterate over the whole dependable data arrays _main.students, _main.attendance_dates, and _main.rolls so that you only get one data array to use inside of *ngFor="" [in the class .ts file as well]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.