Reputation: 268
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;
}
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)
);
}
Upvotes: 2
Views: 4831
Reputation: 18281
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
Upvotes: 3