Search code examples
angularprimeng-datatable

Angular4 PrimeNG DataTable Not Updating Despite Model Changes


I get an observable back from a data service to web api that returns an array and populates a data table in a parent component correctly. I pass the array used to populate the datatable to a child component via @Input. When a row on the parent component is clicked, I show a modal for editing a single row using a Form. When the save button is clicked on the modal, the row information is sent to the child component which calls to the server and updates the information correctly. The server returns the updated object which is then spliced into the @Input array. The datatable never updates although I can see that the changes to the model array do occur and are detected by Angular by using Augury. I've tried using NgZone, NgOnChanges and setters/getters to have the array forced to update, to no avail. I have this exact same code in another project, and it works as expected. I'm not sure what I'm missing here.

Parent Template

<h2>Website Aliases</h2>

<p-dataTable #dt [value]="websiteAliases" selectionMode="single" [(selection)]="selectedWebsiteAlias"
             (onRowSelect)="onRowSelect($event);wad.setEditValue(selectedWebsiteAlias)"
             [paginator]="true" [rows]="100"
             [sortMode]="multiple">
    <p-header>
        Website Aliases
        <div style="float:right;">
            <button pButton type="button" (click)="wad.setCreateValue();" icon="fa-plus"></button>
            <button pButton type="button" (click)="reloadDataTable();" icon="fa-refresh"></button>
        </div>
    </p-header>
    <p-column field="aliasID" header="Alias ID" sortable="true"></p-column>
    <p-column field="webID" header="Web ID" [filter]="true" filterMatchMode="contains"></p-column>
    <p-column field="csHost" header="CsHost" [filter]="true" filterMatchMode="contains"></p-column>
</p-dataTable>

    <website-alias-detail #wad [websiteAlias]="selectedWebsiteAlias" [websiteAliases]="websiteAliases"></website-alias-detail>

Parent Component

import { Observable } from 'rxjs/Rx';
import { DataTableModule, DialogModule } from 'primeng/primeng';
import { WebsiteAliasDetailComponent } from './websitealiasdetails.component';

@Component({
    selector: 'website-alias',
    templateUrl: 'websitealias.component.html'
})

export class WebsiteAliasComponent {
    constructor(private dataService: DataService) { }
    private websiteAliases: WebsiteAlias[];
    private selectedWebsiteAlias: WebsiteAlias;


    ngOnInit(): void {
        this.dataService.get('websitealias').subscribe(apiObjects => this.websiteAliases = apiObjects);
    }

    onRowSelect(row): void {
        this.selectedWebsiteAlias = this.websiteAliases.find(websiteAlias => websiteAlias.aliasID === row.data.aliasID);
    }

    createWebsiteAlias(): void {
        this.selectedWebsiteAlias = null;
    }

    reloadDataTable(): void {
        this.dataService.get('websitealias').subscribe(apiObjects => this.websiteAliases = apiObjects);
    }
}

Child Template

<p-dialog #dialog modal="true" header="Website Alias Details" [(visible)]="display">
    <form #form [formGroup]="alias">
        <div class="alert alert-danger" [hidden]="alias.controls.webID.valid || (alias.controls.webID.pristine && !submitted)">
            Web ID is required and only accepts numbers
        </div>
        <div class="alert alert-danger" [hidden]="alias.controls.csHost.valid || (alias.controls.csHost.pristine && !submitted)">
            CS Host is required
        </div>
        <div class="divTable">
            <div class="divTableBody">
                <div class="divTableRow" *ngIf="alias">
                    <div class="divTableCell"><label>Alias ID:  </label></div>
                    <div class="divTableCell"><label><input placeholder="Alias ID" formControlName="aliasID" readonly="readonly" /></label></div>
                </div>
                <div class="divTableRow">
                    <div class="divTableCell"><label>Web ID:  </label></div>
                    <div class="divTableCell"><input placeholder="Web ID" formControlName="webID" /></div>
                </div>
                <div class="divTableRow">
                    <div class="divTableCell"><label>CS Host:   </label></div>
                    <div class="divTableCell"><input placeholder="CS Host" formControlName="csHost" /></div>
                </div>
                <div class="divTableRow">
                    <div class="divTableCell">
                        <button pButton type="submit" label="Save" (click)="saveWebsiteAlias(alias.value)" [disabled]="alias.invalid" class="ui-button ui-button-primary"></button>
                        <button pButton type="button" label="Close" (click)="hideDialog();" class="ui-button ui-button-secondary"></button>
                        <button *ngIf="websiteAlias" pButton type="submit" label="Delete" (click)="deleteWebsiteAlias(alias.value)" class="ui-button ui-button-danger"></button>
                    </div>
                </div>
            </div>
        </div>
    </form>
</p-dialog>

Child Component

import { Component, Input, Output, OnInit, ViewEncapsulation } from '@angular/core';
import { DataService } from '../../services/data.service';
import { WebsiteAlias } from './WebsiteAlias';
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';

@Component({
    selector: 'website-alias-detail',
    templateUrl: 'websitealiasdetails.component.html',
    encapsulation: ViewEncapsulation.None,

})

export class WebsiteAliasDetailComponent implements OnInit {
    @Input() websiteAliases: WebsiteAlias[];
    @Input() websiteAlias: WebsiteAlias;

    public alias: FormGroup;
    private api = 'websitealias';
    private display: boolean = false;
    private isEdited: boolean = false;

    constructor(private dataService: DataService, private formBuilder: FormBuilder) { }

    ngOnInit(): void {
        this.alias = this.formBuilder.group({
            aliasID: [''],
            webID: ['', [Validators.required, Validators.pattern('[0-9]*')]],
            csHost: ['',Validators.required]
        });

    }


    setEditValue(websiteAlias: WebsiteAlias)
    {
        this.alias.patchValue({ aliasID: websiteAlias.aliasID, webID: websiteAlias.webID, csHost: websiteAlias.csHost});
        this.isEdited = true;
        this.showDialog();
    }

    setCreateValue()
    {
        this.alias.patchValue({ aliasID: '', webID: '', csHost: '' });
        this.isEdited = false;
        this.websiteAlias = null;
        this.showDialog();
    }


    showDialog(): void {

        this.display = true;
    }

    hideDialog(): void {
        this.display = false;
    }

    saveWebsiteAlias(savedAlias: WebsiteAlias): void {

        if (!this.isEdited)
            this.createWebsiteAlias(savedAlias);            
        else {
            this.editWebsiteAlias(savedAlias);
        }

        this.hideDialog();
    }

    deleteWebsiteAlias(deletedAlias: WebsiteAlias): void {

        var idToDelete = 0;
        this.dataService.delete(this.api, deletedAlias.aliasID).subscribe(deletedId => idToDelete = deletedId);

        let websiteAliasToRemove = this.websiteAliases.find(web => web.aliasID === idToDelete);
        let index: number = this.websiteAliases.indexOf(websiteAliasToRemove);
        this.websiteAliases.splice(index, 1);

        this.hideDialog();

    }

    createWebsiteAlias(createdAlias: WebsiteAlias): void {

        var newAlias = new WebsiteAlias(0, createdAlias.webID, createdAlias.csHost);

        this.dataService.post(this.api, newAlias).subscribe(apiObject => newAlias.aliasID = apiObject.aliasID);

        this.websiteAliases.push(newAlias);
        this.websiteAlias = newAlias;

    }

    editWebsiteAlias(updatedAlias: WebsiteAlias): void {
        var idToUpdate = 0;
        var aliases = this.websiteAliases;
        this.dataService.put(this.api, updatedAlias).subscribe(apiObject => this.websiteAlias = apiObject);
        let websiteAliasToEdit = aliases.find(web => web.aliasID === updatedAlias.aliasID);
        let index: number = aliases.indexOf(websiteAliasToEdit);
        this.websiteAliases.splice(index, 1, updatedAlias);
    }

}

Solution

  • Look at the answer given in [How to programmaticaly trigger refresh primeNG datatable when a button is clicked question: 1

    I don't have enough rep to add this as a comment, but the answer involves creating a "visible" variable with *ngIf to trigger recreating the DOm.