Reputation: 962
I'm using Angular 4. There is a search button on Customers page. If the user clicks this button, a list of customers will display in a table.
I'm trying to bind the list of customers to a table using *ngFor. But when clicking search button, the list of customers is appended to the current data in table.
My expectation is the table is clear and just display new data. Please take a look my code below and advise how to resolve this issue. Thank you very much.
customer-list.component.ts
import { Component } from '@angular/core';
import { Http } from '@angular/http';
import { Customer } from './customer';
import { CustomerService } from './customer.service';
@Component({
selector: 'customer-list',
templateUrl: './customer-list.component.html',
providers: [CustomerService]
})
export class CustomerListComponent {
public customers: Customer[] = [];
public searchTerm: string;
constructor(private customerService: CustomerService) {
}
onSearchClicked(): void {
this.customerService.searchSimilarCustomers(this.searchTerm);
this.customers = this.customerService.customers;
}
}
customer-list.component.html
<div class="row">
<div class="col-md-6">
<button class="btn btn-primary" data-toggle="modal" data-target="#customer-detail"><i class="fa fa-plus"></i> Create customer</button>
</div>
<div class="col-md-6">
<form class="form-inline pull-right">
<div class="form-group">
<div class="input-group">
<input type="text" class="form-control" name="searchTerm" [(ngModel)]="searchTerm" placeholder="Search customer">
</div>
</div>
<button type="submit" class="btn btn-primary" (click)="onSearchClicked()"><i class="fa fa-search"></i> Search</button>
</form>
</div>
</div>
<table class="table">
<thead>
<tr>
<th>Id</th>
<th>Reference</th>
<th>Last Name</th>
<th>Middle Name</th>
<th>First Name</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let customer of customers">
<td>{{ customer.id }}</td>
<td>{{ customer.reference }}</td>
<td>{{ customer.lastName }}</td>
<td>{{ customer.middleName }}</td>
<td>{{ customer.firstName }}</td>
</tr>
</tbody>
</table>
customer.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Customer } from './customer';
@Injectable()
export class CustomerService {
private customersUrl = "http://localhost:60001/api/v1/customers";
public customers: Customer[] = [];
constructor(private http: Http) { }
searchSimilarCustomers(searchTerm: string, page: number = 1, itemsPerPage: number = 10) {
var me = this;
if (!searchTerm) {
searchTerm = "";
}
var url = me.customersUrl + `?searchTerm=${searchTerm}&page=${page}&itemsPerPage=${itemsPerPage}`;
me.http.get(url).subscribe(result => {
for (var item of result.json().data) {
var customer = me.MapCustomerFromResource(item.data);
me.customers.push(customer);
}
}, error => console.error(error));
}
private MapCustomerFromResource(data: any): Customer {
return {
id: data.id,
reference: data.attributes.reference,
firstName: data.attributes.firstName,
middleName: data.attributes.middleName,
lastName: data.attributes.lastName,
gender: data.attributes.gender
};
}
}
Best regards,
Kevin
Upvotes: 2
Views: 2218
Reputation: 62238
me.customers.push
<= the service is adding to the same array and that array is reused. If you want to track all customers and customers you just returned you will have to differentiate by creating 2 second array.
Also I dislike your call structure, there is no clear separation of concerns which is causing you to introduce logic bugs that are hard to track down. Stick to the built in subscribe mechanisms so your service returns data but is stateless (ie. does not track customers).
See the changed code, this allows the component to subscribe
to the observable returned from the service and removes state from the service. This is a cleaner way to go about doing this. If you want to track all customers then add another array in your component (not service) where you push more customers as they are returned.
customer-list.component.ts
import { Component } from '@angular/core';
import { Http } from '@angular/http';
import { Customer } from './customer';
import { CustomerService } from './customer.service';
@Component({
selector: 'customer-list',
templateUrl: './customer-list.component.html',
providers: [CustomerService]
})
export class CustomerListComponent {
public customers: Customer[] = [];
public searchTerm: string;
constructor(private customerService: CustomerService) {
}
onSearchClicked(): void {
this.customerService.searchSimilarCustomers(this.searchTerm)
.subscribe(customers => {
this.customers = customers;
});
}
}
customer.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Customer } from './customer';
@Injectable()
export class CustomerService {
private customersUrl = "http://localhost:60001/api/v1/customers";
// No more customers state
constructor(private http: Http) { }
searchSimilarCustomers(searchTerm: string, page: number = 1, itemsPerPage: number = 10) : Observable<Customer[]> {
if (!searchTerm) {
searchTerm = "";
}
var url = this.customersUrl + `?searchTerm=${searchTerm}&page=${page}&itemsPerPage=${itemsPerPage}`;
return this.http.get(url).map(result => {
var customers: Customer[] = []
for (var item of result.json().data) {
var customer = this.MapCustomerFromResource(item.data);
customers.push(customer);
}
return customers;
}, error => console.error(error));
}
private MapCustomerFromResource(data: any): Customer {
return {
id: data.id,
reference: data.attributes.reference,
firstName: data.attributes.firstName,
middleName: data.attributes.middleName,
lastName: data.attributes.lastName,
gender: data.attributes.gender
};
}
}
Upvotes: 3
Reputation: 4013
Just set the me.customers = []
inside of the subscription.
searchSimilarCustomers(searchTerm: string, page: number = 1, itemsPerPage: number = 10) {
var me = this;
if (!searchTerm) {
searchTerm = "";
}
var url = me.customersUrl + `?searchTerm=${searchTerm}&page=${page}&itemsPerPage=${itemsPerPage}`;
me.http.get(url).subscribe(result => {
me.customers = []; // This line will fix that issue.
for (var item of result.json().data) {
var customer = me.MapCustomerFromResource(item.data);
me.customers.push(customer);
}
}, error => console.error(error));
}
Upvotes: 0
Reputation: 2277
You should clean customers
in service before you start pushing results in it.
Upvotes: 0
Reputation: 3460
You shouldn't use the push() method for what you are trying to achieve. This will add the data to the existing list.
me.http.get(url).subscribe(result => {
for (var item of result.json().data) {
var customer = me.MapCustomerFromResource(item.data);
me.customers.push(customer);
}
}
If you want the customers list to be updated, you should do something that looks like this :
me.http.get(url).subscribe(
(res) => me.onSuccess(res.json),
(res) => me.onError(res.json)
);
private onSuccess(data) {
me.customers = data;
}
private onError(error) {
this.errorMessage = error.toString();
}
Upvotes: 0