Home > Net >  Update List with data from array
Update List with data from array

Time:07-28

Hello Everyone, Iam trying to create a small Application which manage tasks for employees. In one part of the Application the user can add an failed call attempt. So if he clicks on a button a function get called which should insert a new call object in the calls array of the CallService. After that the new call should be shown in the view. But it doesn't.

This is the View

<mat-list>
<div mat-header style="color:#043755; font-weight: 700; font-size: 1.8rem; text-align: center;">Anrufe:</div>
<mat-list-item *ngFor="let call of calls;">
    <mat-icon mat-list-icon>phone</mat-icon>
    <div mat-line style="font-size: 1.2rem;">{{call.number}}</div>
    <div mat-line style="font-size: 1.2rem;"> {{call.date | date}} </div>
    <div mat-line style="font-size: 1.2rem; color:chocolate;">{{call.status}}</div>
</mat-list-item>

*it should load the calls with the ngFor derective.

Here is my Component

import {Component, OnInit} from '@angular/core';
import { Call, CallService } from '../call.service';


/**
 * @title List with sections
 */
@Component({
  selector: 'app-missed-calls',
  styleUrls: ['./missed-calls.component.css'],
  templateUrl: './missed-calls.component.html',
})
export class MissedCallsComponent implements OnInit {

  public calls: Call[] = [];

  constructor(private _callService: CallService) { }

  ngOnInit() {
    this.calls = this._callService.getCalls();
  }

}

I injected the service here and in the ngInit I "fill" my array.

And here is my Service

import { Injectable } from '@angular/core';

export interface Call {
  number: string;
  date: Date;
  status: string;
}

@Injectable({
  providedIn: 'root'
})
export class CallService {

  constructor() { }

  calls: Call[] = [
    {number: '0677/61792248', 
    date: new Date('1/1/14'), 
    status: 'Verpasst'},
  ];

  getCalls() {
    return this.calls;
  }

  addCall(call: Call) {
    this.calls = [...this.calls, call];
  }

}

It would be nice if someone could help me to solve the problem! :D

CodePudding user response:

Quickfix

A quick fix for you would be to make the Injectable public

public _callService: CallService

and updating your ngFor like this

<mat-list-item *ngFor="let call of _callService.calls;">

Another solution for you would be to change addCall method to do this:

this.calls.push(call)

I'll explain why

Explanation

In js, objects are saved by reference. This means they have an internal ID that points to the memory. This is what you share when you do return this.calls

You create the calls object on the service. Let's say it's internal id is js_id_123 and you return this.calls meaning that in js you return memory:js_id_123 if that makes sense.

so now calls on the component and calls on the service are pointing to the same memory object. So any modifications to that object would result in an update for the ngFor.

Component.calls ->> js_id_123
Service.calls ->> js_id_123

BUT:

in your addCalls you override the js_id_123 by creating a new object and assigning it to Service.calls

[...this.calls, call]

(this creates a new object, with values from the old object). So, let's say this new object is js_id_777, now your service has calls js_id_777, and your component js_id_123.

Component.calls ->> js_id_123
Service.calls ->> js_id_777

Your component will not get the updates, because you are updating the object js_id_777 referenced in the service only, so the calls in the component does not get those updates, so the ngFor does not have anything new to show.

This brings me to both of my solutions. You have multiple sources of truth.

So, if you use push, you are not changing the object reference, so it works. If you use the service object calls directly, you can change it's reference, but ngFor will pick it because it is reading that particular property, so if it changes, ngFor updates accordingly (not for the one in the component, that has not changed, therefore no need to update).

Maybe this makes little sense.

Performance suggestion

Also, destructuring ([...this.calls, calls]) has to go through the whole array. So the bigger your array gets, the slower your app will be at adding a new one, because it has to recreate a full array. In this case, I suggest you use push, as it doesn't need to iterate the array again, it just adds a new one.

If you have any questions let me know

What I would do:

I would change the service to use push instead of destructuring, for performance reasons. I then would make the property of the service the only source of truth (meaning, I don't need the calls property on the component).

Finally, there's no need for this service to be a service. It has no Dependency Injection or need to be a Service. So just make it a plain Object, call it CallsManager, initialize it on your component: callsManager = new CallsManager();, and then use it inside your ngFor

<mat-list-item *ngFor="let call of callsManager.calls;">

this is cleaner, keeps only one source of truth, and removes the cumbersomeness that comes from creating services (try to make them services when you need to inject something.. like httpClient or another service. If no Dependency Injection is needed, a plain object is preferable)

CodePudding user response:

Instead of pushing new values,you were just assigning it to array ,so issue occurred.

Just change your adding function

   // From 

addCall(call: Call) {
    this.calls = [...this.calls, call];
  }

// To

addCall(call: Call) {
    this.calls.push(call)
  }

  • Related