phhbr
phhbr

Reputation: 2727

md-table with datasource not updating on delete

I have an angular component with a material design table where objects are shown. This component gets its data via a datasource. The datasource gets its data from a http-service. The table itself can be filtered.

That's my code, simplified - Feel free to ask for further information on certain things if you need to..

customer.component.html

<md-input-container floatPlaceholder="never">
    <input mdInput #filter placeholder="Filter customers">
</md-input-container>
<md-table #table [dataSource]="dataSource">
    <ng-container cdkColumnDef="customerid">
        <md-header-cell *cdkHeaderCellDef> ID </md-header-cell>
        <md-cell *cdkCellDef="let row"> {{row.id}} </md-cell>
    </ng-container>
    <ng-container cdkColumnDef="remove">
        <md-header-cell *cdkHeaderCellDef></md-header-cell>
        <md-cell *cdkCellDef="let row"> 
            <button md-button (click)="deleteCustomer(row)">
                Remove Customer
            </button> 
        </md-cell>
    </ng-container>
    <md-header-row *cdkHeaderRowDef="displayedColumns">
    </md-header-row>
    <md-row *cdkRowDef="let row; columns: displayedColumns;">
    </md-row>
</md-table>

customer.component.ts

export class CustomerOverviewComponent implements OnInit {
    public displayedColumns: string[] = [
        "customerid", "remove"
    ];
    public dataSource: CustomerOverviewDataSource | null;
    @ViewChild("filter") filter: ElementRef;

     constructor(private dataService: CustomerOverviewService,
                 private cdRef: ChangeDetectorRef) {
        this.initDataSource(dataService);
    }

    ngOnInit(): void {
        Observable.fromEvent(this.filter.nativeElement, "keyup")
            .debounceTime(150)
            .subscribe(() => {
                if (!this.dataSource) { return; }
                this.dataSource.filter = this.filter.nativeElement.value;
            });
    }

    public deleteCustomer(customer: CustomerOverview): void {
        this.dataSource.disconnect();
        this.dataService
            .Delete(customer.id)
            .subscribe(data => {
                this.error.info(`Customer with Id: ${data._body} deleted!`);
            }, (err) => {
                this.error.error(`Unable to delete customer: ${err.message}`);
            });
        this.dataSource.connect();
    }

    private initDataSource(dataService: CustomerOverviewService): void {
        this.dataSource = new CustomerOverviewDataSource(this.dataService);
    }
}

customer.service.ts

@Injectable()
export class CustomerOverviewService {
    private actionUrl: string;
    private headers: Headers;

    dataChange: BehaviorSubject<CustomerOverview[]> = new BehaviorSubject<CustomerOverview[]>([]);
    get data(): CustomerOverview[] { return this.dataChange.value; }

    constructor(private http: Http, private authHttp: AuthHttpService, public snackBar: MdSnackBar) {

        this.actionUrl = Configuration.API_SERVER + "api/customeroverview/";

        this.headers = new Headers();
        this.headers.append("Content-Type", "application/json; charset=utf-8");
        this.headers.append("Accept", "application/json");
        this.GetAll().forEach(s => this.dataChange.next(s));
    }

    public GetAll = (): Observable<CustomerOverview[]> => {
        return this.authHttp
            .get(this.actionUrl)
            .map((response: Response) => <CustomerOverview[]>response.json())
            .publishReplay(1)
            .refCount();
    }

    public Delete = (id: number): Observable<any> => {
        return this.authHttp.delete(this.actionUrl + id)
            .catch(console.log("Delete Catch"));
    }
}

export class CustomerOverviewDataSource extends DataSource<any> {
    _filterChange = new BehaviorSubject("");
    get filter(): string { return this._filterChange.value; }
    set filter(filter: string) { this._filterChange.next(filter); }

    constructor(private dataService: CustomerOverviewService) {
        super();
    }

    /** Connect function called by the table to retrieve one stream containing the data to render. */
    connect(): Observable<CustomerOverview[]> {
        const displayDataChanges: any = [
            this.dataService.GetAll(),
            this._filterChange,];

        return Observable.merge(...displayDataChanges).map(() => {
            return this.dataService.data.slice().filter((item: CustomerOverview) => {
                let searchStr: string = (item.gender + item.lastname + item.firstname + item.personalnumber
                    + item.consultant + item.hasConsultingEntities).toLowerCase();
                return searchStr.indexOf(this.filter.toLowerCase()) !== -1;
            });
        });
    }

    disconnect(): void {
        console.log("disconnect");
    }
}

When I delete a entity from the table, it does not get updated. Where are the objects stored, so I can know what I have to update? I have the feeling that the objects, the HTML gets, are in the constant variable displayDataChanges and I cannot change this?

Thanks for any hints to the right directions..

Upvotes: 0

Views: 1645

Answers (1)

Will Howell
Will Howell

Reputation: 3715

You're close, but there are a few things slightly off here:

  1. I don't know of a valid use case for calling connect() and disconnect() manually. AFAIK connect() should only be called once by the table, and disconnect() is a callback when the table is destroyed in case you need to do any sort of unsubscribing or other cleanup. You can remove those lines from deleteCustomer().

  2. displayDataChanges includes dataService.GetAll(). That means that when connect() is called by the table and the result is subscribed to, GetAll() will run. I doubt this is what you want, since GetAll() is already called and cached in the constructor of CustomerOverviewService. You probably want dataService.dataChange, the BehaviorSubject, to be part of the displayDataChanges instead. That means that whenever you call dataChange.next(vals), your connect() Observable will emit a new value, and the table will be updated.

  3. The state of your service does not change when Delete() is called. Whenever the request completes, you should advance the BehaviorSubject with next() to update dataChange with the new value. You can either sort through the current data and remove the entry, or you can call GetAll() again. In either case, next() the new results into dataChange so that the table will re-render.

Upvotes: 2

Related Questions