Reputation: 63
(Angular 8)
I have two component addgame and home, In home I displayed all games stored in database with the help of REST API.
In home component, by using Mat-dialog I called game component in dialog view.
The problem is if I have add game in that mat-dialog, it is successfully added But Only after refreshing home component manually that'll updated in mat-table
I need that mat-table update without any manual refreshing
Note : I tried ChangeDetectorRef, triggered MatPaginator and all possible answers from here but no changes
home.component.html
<div class="search-div">
<button mat-raised-button (click)="onCreateGame()"><mat-icon >add</mat-icon>Create Guest</button>
<mat-form-field class="search-form-field" floatLabel="never">
<input matInput [(ngModel)]="searchKey" placeholder="Search" autocomplete="off" (keyup)="applyFilter()" >
<button mat-button matSuffix mat-icon-button aria-label="Clear" *ngIf="searchKey" (click)= "onSearchClear()">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
</div>
<div class="mat-elevation-z8">
<mat-table [dataSource]="listData">
<ng-container matColumnDef="gameName">
<mat-header-cell *matHeaderCellDef>Game Name</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.gameName}}</mat-cell>
</ng-container>
<ng-container matColumnDef="gameDate">
<mat-header-cell *matHeaderCellDef>Game Date</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.gameDate | date}}</mat-cell>
</ng-container>
<ng-container matColumnDef="gameVenue">
<mat-header-cell *matHeaderCellDef>Game Venue</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.gameVenue}}</mat-cell>
</ng-container>
<ng-container matColumnDef="homeTeam">
<mat-header-cell *matHeaderCellDef>Home Team</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.homeTeam}}</mat-cell>
</ng-container>
<ng-container matColumnDef="awayTeam">
<mat-header-cell *matHeaderCellDef>Away Team</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.awayTeam}}</mat-cell>
</ng-container>
<ng-container matColumnDef="numberOfGuest">
<mat-header-cell *matHeaderCellDef>No of Guest</mat-header-cell>
<mat-cell *matCellDef="let element">{{element.numberOfGuest}}</mat-cell>
</ng-container>
<ng-container matColumnDef="loading">
<mat-footer-cell *matFooterCellDef colspan="6">
Loding data...
</mat-footer-cell>
</ng-container>
<ng-container matColumnDef="noData">
<mat-footer-cell *matFooterCellDef colspan="6">
No data.
</mat-footer-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns:displayedColumns;"></mat-row>
<mat-footer-row *matFooterRowDef="['loading']" [ngClass]="{'hide': listData!=null}"></mat-footer-row>
<mat-footer-row *matFooterRowDef="['noData']" [ngClass]="{'hide': !(listData!=null && listData.data.length==0)}"></mat-footer-row>
</mat-table>
<mat-paginator [pageSizeOptions]="[5,10,25,100]" pageSize="5" showFirstLastButtons></mat-paginator>
</div>
home.component.ts
import { LoginService } from './../service/login.service';
import { AddgameComponent } from './../addgame/addgame.component';
import { GameService } from './../service/game.service';
import { MatTableDataSource, MatTable } from '@angular/material/table';
import { Component, OnInit, ViewChild, ChangeDetectorRef, AfterContentChecked, OnDestroy } from '@angular/core';
import { MatPaginator } from '@angular/material/paginator';
import { MatDialog, MatDialogConfig } from '@angular/material/dialog';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit{
constructor(public loginService : LoginService,public gameService :GameService, public dialog : MatDialog) { }
listData : MatTableDataSource<any>;
@ViewChild(MatPaginator) paginator : MatPaginator
displayedColumns : string[] = ['gameName','gameDate','gameVenue', 'homeTeam','awayTeam','numberOfGuest'];
ngOnInit(): void
{
this.starter();
}
starter()
{
this.gameService.getAllGamesFromRemote().subscribe(
data =>{
let array = data;
this.listData = new MatTableDataSource(array);
this.listData.paginator = this.paginator;
this.listData.filterPredicate = (data, filter) => (data.gameName.trim().toLowerCase().indexOf(filter.trim().toLowerCase()) !== -1 ||data.homeTeam.trim().toLowerCase().indexOf(filter.trim().toLowerCase()) !== -1 );
});
}
searchKey : string = "";
onSearchClear()
{
this.searchKey = "";
this.applyFilter();
}
applyFilter()
{
this.listData.filter = this.searchKey.trim().toLowerCase();
}
onCreateGame()
{
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = true;
dialogConfig.autoFocus=true;
dialogConfig.width="60%";
this.dialog.open(AddgameComponent, dialogConfig);
}
}
addgame.component.html
<mat-toolbar>
<span>Add new Game</span>
<span class="filler"></span>
<button class="btn-dialog-close" mat-stroked-button (click)="onClose()" tabindex="-1"><mat-icon>close</mat-icon></button>
</mat-toolbar>
<form class="normal-form" [formGroup]="gameService.gameForm" autocomplete="off" (submit)="onSubmit()">
<div class="controls-container">
<mat-form-field>
<input matInput formControlName="gameName" placeholder="Game Name*">
<mat-error *ngIf="gameService.gameForm.controls['gameName'].errors?.required">This is Mandatory Field</mat-error>
<mat-error *ngIf="gameService.gameForm.controls['gameName'].errors?.notUnique">Game name should be Unique</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput formControlName="gameVenue" placeholder="Game Venue*">
<mat-error>This is Mandatory Field</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput formControlName="homeTeam" placeholder="Home Team*">
<mat-error>This is Mandatory Field</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput formControlName="awayTeam" placeholder="Away Team*">
<mat-error>This is Mandatory Field</mat-error>
</mat-form-field>
<mat-form-field>
<input matInput formControlName="gameDate" [matDatepicker]="picker" placeholder="Game Date*">
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
<mat-error>This is Mandatory Field</mat-error>
</mat-form-field>
<input type="hidden" formControlName="gameName" placeholder="Game Name*">
<div style="text-align: center;">
<button mat-raised-button color="primary" type="submit" [disabled]="gameService.gameForm.invalid">Add Game</button>
</div>
</div>
</form>
addgame.component.ts
import { Router } from '@angular/router';
import { NotificationService } from './../service/notification.service';
import { GameService } from './../service/game.service';
import { Component, OnInit } from '@angular/core';
import { DatePipe } from '@angular/common';
import { MatDialogRef } from '@angular/material/dialog';
@Component({
selector: 'app-addgame',
templateUrl: './addgame.component.html',
styleUrls: ['./addgame.component.css']
})
export class AddgameComponent implements OnInit {
constructor(public gameService : GameService, public notificationService : NotificationService, private datePipe: DatePipe, public dialogRef : MatDialogRef<AddgameComponent>, public router : Router) { }
ngOnInit(): void {
}
onSubmit()
{
this.gameService.addGameToRemote(this.gameService.gameForm.value).subscribe(
data=>{
this.notificationService.success("Game Added Sucessfully");
this.gameService.initializeForm();
console.log("Game added");
this.gameService.gameForm.reset();
this.onClose();
},
error=>{
this.gameService.gameForm.controls['gameName'].setErrors({"notUnique": true});
this.notificationService.error("Game name already taken");
console.log("Error in adding Game")
}
)
}
onClose()
{
this.gameService.gameForm.reset();
this.gameService.initializeForm();
this.dialogRef.close();
}
}
Upvotes: 1
Views: 2933
Reputation: 41
Given:
Data source : listData
Data source type : MatTableDataSource
starter() : populates listData by invoking an API this.gameService.getAllGamesFromRemote()
dialogConfig, a MatDialog : used for entry of new Game details
Steps to follow:
Get newGame data as a JSON object of type matching your Game details in listData array
Push this new Game JSON object into DataSource Array object using;
this.listData.data.push(<new Game JSON object)
Add below line after .push()
this.listData.data._updateChangeSubscription();
This will ensure that the new entry is immediately reflected in your table.
Note:
Upvotes: 1
Reputation: 15098
Below would be my approach
Declare an observable
allGamesFromRemote$ = this.gameService.getAllGamesFromRemote().pipe(
tap( data =>{
let array = data;
this.listData = new MatTableDataSource(array);
this.listData.paginator = this.paginator;
this.listData.filterPredicate = (data, filter) =>
(data.gameName.trim().toLowerCase().indexOf(filter.trim().toLowerCase()) !== -1 || data.homeTeam.trim().toLowerCase().indexOf(filter.trim().toLowerCase()) !== -1 );
});
We now need a way to trigger the observable every time you make a change For this we can declare two variables
remoteChangedSubject$ = new BehaviorSubject<any>(true);
remoteChangedAction$ = this.remoteChangedSubject$.asObservable()
Next would be to combine the two observables
// Make sure combineLatest to import from 'rxjs'
allGames$ = combineLatest([this.allGamesFromRemote$, this.remoteChangedAction$]).pipe(
map(([allGames]) => allGames)
)
Finally after successful addition, you would call
this.remoteChangedSubject$.next(true);
In your html you then wrap the table in an *ngIf
using async pipe to handle subscriptions for us
<ng-container *ngIf='allGames$ | async'>
<!-- Your Table Code Here-->
</ng-container>
This way whenever you call this.remoteChangedSubject$.next(true);
, allGames$
observable is reevaluated and change detection kicks in
Upvotes: 1
Reputation: 1935
You can try to recall the REST API to fetch all games again after the dialog is closed. For that you can use the below code:
const dialogConfig = new MatDialogConfig();
dialogConfig.disableClose = true;
dialogConfig.autoFocus=true;
dialogConfig.width="60%";
dialogConfig.data = {somedata: 'data'};
const dialogId = this.dialog.open(AddgameComponent, dialogConfig);
const dialogSubmitSubscription = dialogId .componentInstance.onDialogueClose.subscribe(result => {
if(result) {
this.starter();
}
dialogSubmitSubscription.unsubscribe();
});
Also, if your table is not updating after data is changed dynamically then you might need to call Angular's ngOnChanges Hook.
ngOnChanges(changes: SimpleChange) {
if (changes["tableData"]) {
this.data = new MatTableDataSource(this.tableData);
if (this.data != undefined) {
this.data = new MatTableDataSource(this.tableData);
this.data.sort = this.sort;
}
setTimeout(() => {
this.data.paginator = this.paginator;
this.data.sort = this.sort;
});
}
}
Hope this answer helps to solve your issue.
Upvotes: 1