Reputation: 253
I've spent about 3 whole days trying to figure this out.
Behavior expected: Material Table always renders with most current data from backend. When a new item is added to table from a separate component, a router redirect to the page shows updated data.
Actual behavior: Upon first navigation to page, table is empty. Table is rendered, column headers rendered, but no rows populated. Navigating away then back populates the table properly. When adding a new item to the table, a service sends the item to the backend and then makes a call for all items. The service then updates the store (another service). On redirect to the table component the table seems to flash the old data quickly and then again appears blank. Once I navigate away and back the table is properly loaded.
I am using the async pipe below with services. This doesn't seem right to me... further, ngOnChanges logs nothing.
appStore.service.ts
private _pets: ReplaySubject<Pet[]> = new ReplaySubject(1);
public readonly pets: Observable<Pet[]> = this._pets.asObservable();
getPets(): Observable<Pet[]> {
return this.pets;
}
appDispatch.service.ts
public getPets(): Observable<Pet[]> {
return this.appStore.getPets();
}
private setPets(pets: Pet[]) {
this.appStore.setPets(pets);
}
petTableComponent.component.ts
...
changeDetection: ChangeDetectionStrategy.OnPush
constructor(private appDispatch: AppDispatchService,
private router: Router) {
}
ngOnChanges(changes: SimpleChanges): void {
console.log(changes.value.currentValue);
}
petTableComponent.component.html
<table
mat-table
[dataSource]="appDispatch.getPets() | async" multiTemplateDataRows
class="mat-elevation-z8">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef scope="col" [attr.role]="null"> <h4>Name</h4></th>
<td mat-cell *matCellDef="let element" scope="row" [attr.role]="null">
<p>{{element.pet.name}}</p>
</td>
</ng-container>
...
EDIT I tried changing the following per request, exact same outcome as before.
//changeDetection: ChangeDetectionStrategy.OnPush
pets-table.component.ts
constructor(private appDispatch: AppDispatchService,
private router: Router,
private cdr: ChangeDetectorRef) {
}
ngOnChanges(changes: SimpleChanges): void {
console.log(changes.value.currentValue());
}
ngOnInit(): void {
this.subscription = this.appDispatch.getReleaseItems()
.subscribe(value => {
this.data = value;
this.cdr.detectChanges();
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
And I changed the datasource to [dataSource]="data"
Not sure...
Upvotes: 5
Views: 10546
Reputation: 117
You are using a ReplaySubject for your data, which will 'replay' all the nexted data but seemingly not the initialized data new ReplaySubject(1)
. If you want to initialize the subject with the data, and only pass the last best set to the subscribers (instead of multiple results like a replay subject would put out), use a BehaviorSubject
https://www.learnrxjs.io/learn-rxjs/subjects/replaysubject
Upvotes: 0
Reputation: 9124
Try to store the observable in the component
pets$: Observable<Pet[]> = this.appDispatch.getPets();
<table
mat-table
[dataSource]="pets$ | async" multiTemplateDataRows
class="mat-elevation-z8">
Upvotes: -1
Reputation: 374
this is because you have set ChangeDetectionStrategy.OnPush
this means your component thinks you only relying on your @input
attributes so change detection only takes place when those values are changed or a DOM object event is raised from the Component or its children.
have a look at this article that has a really good explanation with examples: https://netbasal.com/a-comprehensive-guide-to-angular-onpush-change-detection-strategy-5bac493074a4
you could also run change detection manually:
constructor(private cdr: ChangeDetectorRef, private appDispatch: AppDispatchService) {
......
this.appDispatch.Pets.subscribe(() => {
this.cdr.detectChanges();
})
}
Upvotes: 0