2

It looks like this problem is popular, I tried many times to update my view after a new element pushed in my array, but nothing happened.

I will explain my functions and what I'm trying to do, and I will show you my tries.

My first component is car.component.ts that I used to show my cars list calling API using carsService.

carsList() {
        this._carsListService.carsService(this.user)
            .subscribe(
                response => {
                        this.result = response;
                        if (this.result.code != 200) {
                             //error response message
                        }
                        else if (this.result.code == 200) {
                            this.data = [] = this.data;
                            this.result.cars.forEach(element => {
                                this.data.push(element);
                            });
                        }
                },
                error => console.log(error)
            );
    }

Second component is insert.component.ts that I can add a new car with details and the carsList should detect the changes which is what I'm looking for.

insertNew() {
        this._insertService.insertNewService(this.user)
            .toPromise()
            .then(
                response => {
                    this.result = response;
                    if (this.result.status != 200) {
                       //error message
                    } else {
                       // new element inserted and now it is in cars list API
                    }
                },
                error => console.log(error)
            );

    }

Now in my car.component.html

 <div *ngFor="let element of data" id="{{element.car_id}}">
    <mat-card>
        <mat-card-content>
          //some details about cars
        </mat-card-content>
    </mat-card>
 </div>

Now everything is fine for first reading, but when insertNew() called and inserted any new element, nothing change in carsList view.

1 - I tried to run my function using ngZone

carsList() {
            this._carsListService.carsService(this.user)
                .subscribe(
                    response => {
                    this.zone.run(() => { // <==
                            this.result = response;
                            if (this.result.code != 200) {
                                 //error response message
                            }
                            else if (this.result.code == 200) {
                                this.data = [] = this.data;
                                this.result.cars.forEach(element => {
                                    this.data.push(element);
                                });
                            }
                             console.log("using ngzone"); //this console appeared just one time even when I insert anything new
                             });
                    },
                    error => console.log(error)
                );
        }

2 - I tried to go with DoCheck algorithm included in Angular, it looks like replacing this line <div *ngFor="let element of data" id="{{element.car_id}}"> with this <div *ngFor="#element of data" id="{{element.car_id}}"> but Angular said (Unexpected token #).

#EDIT

My service

carsService(value: Object) {
        return this._appService.LoadAPI(value, this.carsAPI);
    }

LoadAPI

public loadScript(src) {
        let script = document.createElement('script');
        script.type = 'text/javascript';
        document.getElementsByTagName('body')[0].appendChild(script);
        script.src = src;
    }

EDIT 2

I have tried to call carList() function inside insertNew()

constructor( public cars_list: CarComponent)

insertNew() {
            this._insertService.insertNewService(this.user)
                .toPromise()
                .then(
                    response => {
                        this.result = response;
                        if (this.result.status != 200) {
                           //error message
                        } else {
                    this.cars_list.carsList();
                           // new element inserted and now it is in cars list API
                        }
                    },
                    error => console.log(error)
                );

        }
16
  • When the car is added to the list, is the carsListService.carsService observable / subject notified? That will need to happen for the subscribe function to be called, thereby updating your data variable Commented Dec 31, 2017 at 10:22
  • # instead of let was in angular before the final release, it's deprecated. Did you check the data after insertNew was called? Are you sure they are correct? And why do you have this in your code please? this.data = [] = this.data; Commented Dec 31, 2017 at 10:23
  • 1
    1. It has nothing to do with zones, DoCheck, or whatever other angular internals. 2. Your service is doing it all wrong. It shouldn't subscribe and modify its state. It shouldn't even have any state. It should only return an observable, and the component should be the one subscribing. 3. Why do you think adding an element to some database on a server should auomatically refresh a list in the browser? You of course need to reload the list from the server after you've inserted the car. Commented Dec 31, 2017 at 10:23
  • @Laker Yes I checked my data and nothing wrong, and this.data = [] = this.data to load more button, to append the list separately. Commented Dec 31, 2017 at 10:37
  • @JBNizet My component is the one subscribing isn't it? Nothing to do with subscribe in my service, maybe I miss something, please check my edit. Commented Dec 31, 2017 at 10:42

1 Answer 1

3

One way to do this is to create an Observable that emits the data you're interested in whenever it changes. It would be managed from within a service, and any necessary components can subscribe to that Observable.

When you then call insertNew, if that API call returns the item that has been added, you can simply add that to your existing data, and notify the observable, without needing to make another API call.

By having your components subscribe to an Observable, they don't need to know when to get updated data, it's simply pushed to them. It also means that no matter which component calls the service, all components will receive the updated data via the observable

Here is an example to indicate what I mean:

@Injectable()
export class CarListService {

  const root = 'https://jsonplaceholder.typicode.com';

  // This is your data.
  private data = [];
  // This subject will be used to update the observable
  private _carList = new Subject();

  // This observable is public so that your components can subscribe
  carList$ = this._carList.asObservable();

  constructor(private http: HttpClient) {
    // The notify function emits the data variable out of the observable
    // In it's initial state, it is simply an empty array
    this.notify();
  }

  loadList(): void {
    // Here, we can get our data from the API. Note that this function
    // does not return anything
    this.http.get(this.root + '/users').subscribe((res) => {
      // We update data with what comes back, and call notify again 
      // so that the observable emits the latest data
      this.data = res;
      this.notify();
    })
  }

  insertNew():void {
    // Here we are updating the API
    this.http.post(this.root + "/users", {
      name: "This is my new one"
    }).subscribe((res) => {
      // The API returns our newly created item, so append it to data, and 
      // call notify again to update the observable
      this.data.push(res);
      this.notify();
    })
  }

  private notify() {
    // Call next on the subject with the latest data
    this._carList.next(this.data);
  }
}

If you want to see this in action, I've created a plunker to show what I mean https://plnkr.co/edit/lLfFcqYqawKcyi3tLtQe?p=preview

Please note that the service and component are in the same file, but that's just for the example

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

4 Comments

So I should subscribe in my service not in component? this is huge for my project, if I will do that I will change a lot of things
The components subscribe to the observable (in this example it's carList$), but the service will subscribe to your HTTP calls so that it can update the carList$ observable, and allow all of the components that are subscribed to carList$ to get the updated data
It's easier than all that. When you insert an element, you must reload the entire list or manually push the array
@Eliseo but how does the car.component know when insert.component has added an element?

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.