angular html update after changing data in an interface

Viewed 774

What I would like to do is update my interface, which has some user information, and then when that changes I would like the results to reflect on the HTML output.

On my HTML page, I have an input, that allows the user to add an additional comment. This is all working fine, however, the user input does not actually show up on the page, but does make the necessary changes to my interface.

In the following HTML file, the user would type something into the input. That then would get saved to my interface which is the data for each student, and then would be visible here <p>{{the students.grades}}<p>

<div class='studentCard' *ngFor='let student of students | studentSearch: StudentSearch'>
            <img class='studentImg' src={{student.pic}}/>
            <div class='studentInfo'>
                <h1>{{student.name | uppercase}}</h1>
                <div class='loca'>
                    <p>Email: <span>{{student.email}}</span></p>
                    <p>Company: <span>{{student.company}}</span></p>
                    <p>Skill: <span>{{student.skills}}</span></p>
                    <p>Average: <span>{{student.average}}</span></p>
                    <mat-expansion-panel [hideToggle]='true' [expanded]='true'>
                            <p>{{student.grades}}</p>
                    </mat-expansion-panel>
                    <p>{{student.tags}}</p>
                    <mat-form-field>
                    <input  matInput (keyup.enter)='add(student.id)' placeholder="Add a tag"></mat-form-field>
                </div>
            </div>
            <div class='expand'><span class='material-icons'>add</span></div>
        </div>

This code is from my component.ts file. Right now I am just trying to pass in some dummy information to see it would actually work. I can see that it updates the interface correctly as it should.

import { Component, OnInit } from '@angular/core';
import { Student } from '../student'
import { StudentsService } from '../students.service';

@Component({
  selector: 'app-student-profiles',
  templateUrl: './student-profiles.component.html',
  styleUrls: ['./student-profiles.component.scss']
})
export class StudentProfilesComponent implements OnInit {
  //implementing an interface to control the data and make it easy to access
  students: Student[] = [];
  StudentSearch=''
  TagSearch=''

  constructor(
    private service: StudentsService
  ) { }

  ngOnInit(): void {
    this.service.getStudents().subscribe(
      result=>result['students'].map(
        x=>{this.students.push(
          {
            name:x.firstName+' '+x.lastName,
            grades:x.grades,
            average:this.computeAvg(x.grades),
            company: x.company,
            email: x.email,
            pic: x.pic,
            id: x.id,
            skills: x.skill,
            tags:[]
          }); 
          }
        )
    )
  }
  //find the average of all the scores of each student.  Calculates the total, then devides by the length of scores array.
  computeAvg(x){
    let total=0
    x.map(score=>{total=total+Number(score)})
    return(total/x.length)

  }
  add(id){
    this.students[id-1]['tags'].push(String('100'))
    console.log(this.students[id-1])
  }

}

This is the interface that I am using for the data:

    name: string;
    grades: number[];
    average: number;
    company: string;
    pic: string;
    email: string;
    id: number;
    skills: string;
    tags: string[];
    

}

What I am not understanding is that if I use the same code, but I change it from 'tags' to 'email', then it does appear updated in the HTML.

2 Answers

Angular Change Detection works by comparing the new and old values of the properties that exist in the template.

If you look closer to your interface, the email property is a string but the tags property is an Array.

When you change the value of the email, Angular is able to know that the old and new values of this primitive type are different and then updates the view.

When you add a new tag to the tags array, the reference to the array does not change it only changes its content, so Angular does not detect it and therefore does not update the view.

In this cases a very simple solution is creating a new array after adding a new tag, so that the reference changes, you can do it with splice or spread operator:

array.slice()
or
[...array]

Take into account that you are making a copy of the array.

This same behaviour of Angular Change Detection works for Object types when reference is not changed and it is required a copy.

Some other web frameworks (i.e. React) use the inmutable paradigm to make this things easier to understand and avoid pitfalls.

More info on Angular Change Detection:

https://dzone.com/articles/how-to-use-change-detection-in-angular#:~:text=Change%20Detection%20means%20updating%20the,detector%20to%20update%20the%20DOM.

Just add a model class. Get the response from the API into the model. When there is change just call the API and update the model with new results. Angular change detection happens when you update the model not when you append into it.

Related