I have an Angular 8 application and I am using a API call like this:
getDossierEntry(patientUUID: string, type: String = '' ): Observable<DossierEntry[]> {
const entryType = type === '' ? 'all' : 'type/' + type;
return this.http.get<DossierEntry[]>(`${this.baseUrl}/${patientUUID}/DossierEntry/` + entryType);
}
And I have a parent component like this:
export class DossierCorrespondenceComponent implements OnInit {
correspondenceEntries$: Observable<DossierEntry[]>;
#Input() allCorrespondence: Array<DossierEntry>;
#Input() correspondenceEntries: Array<DossierEntry>;
#Input() attachmentEntries: Array<DossierEntry>;
message = '';
emptyMessageCorrespondentie = 'Geen correspondentie.';
errorMessageConnection = 'Er ging iets mis met de connectie. Probeer over enkele minuten nogmaals.';
correspondenceLoaded = false;
showingSingle = false;
single: DossierEntry;
constructor(
private documentCorrespondeceService: DocumentCorrespondenceService,
private authService: AuthService ) {}
ngOnInit() {
this.authService.loginStatus().subscribe(user => {
const UUID = user.profile.participant;
this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, () => (this.message = this.errorMessageConnection));
});
}
handleCorrespondenceLoad(result: any) {
if (result.length === 0) {
this.message = this.emptyMessageCorrespondentie;
return;
}
this.allCorrespondence = result;
this.attachmentEntries = [];
this.correspondenceEntries = [];
const groups = _.groupBy(result, 'type');
this.correspondenceEntries = groups.correspondence;
this.attachmentEntries = groups.attachments;
}
}
And the html template looks like this:
<app-vital10-page [noTopBar]="true">
<h2 class="dossier-page-header">Correspondentie</h2>
<p class="data-entry" *ngIf="!allCorrespondence">{{ message }}</p>
<app-is-loading *ngIf="!correspondenceLoaded" message="Correspondentie wordt geladen"></app-is-loading>
<app-dossier-correspondence-list [correspondenceEntries] = "correspondenceEntries$ | async" ></app-dossier-correspondence-list>
<app-dossier-correspondence-item
[item]="single"
(goBack)="goBack($event)"
*ngIf="showingSingle">
</app-dossier-correspondence-item>
</app-vital10-page>
Then I have an child component like this:
export class DossierCorrespondenceListComponent implements OnInit {
#Input()
correspondenceEntries: DossierEntry[];
#Input() showingSingle;
constructor() { }
ngOnInit() {
}
}
and the template looks like this:
<div *ngIf="!showingSingle && correspondenceEntries && correspondenceEntries.length > 0;">
<div class="main-row main-row-dossier">
<section class="data-entry">
<h3 class="dossier-header">Algemeen</h3>
<table class="dossier-table" *ngIf="correspondenceEntries else loadingCorrespondenceEntires ">
<thead class="dossier-tableheader">
<tr>
<th class="dossier-tablehead fixed-one-fifth">Datum</th>
<th class="dossier-tablehead fixed-four-fifths">Onderwerp</th>
</tr>
</thead>
<tbody class="dossier-tablebody">
<tr class="dossier-correspondencerow" *ngFor="let entry of correspondenceEntries; let i = index" (click)="gotoItem(i, entry.type)">
<td>{{ entry.date | date:"dd-MM-y" }}</td>
<td>{{ entry.name }}</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
<ng-template #loadingCorrespondenceEntires>
<div>..Loading </div>
</ng-template>
But now the data of the child component is not visible in the Parent component. But The correct data is loaded. Because If I do a console.log on this :
this.correspondenceEntries = groups.correspondence;
this.attachmentEntries = groups.attachments;
I see the correct arrays.But not in the view(htm file)
The problem is with this:
correspondenceEntries$: Observable<DossierEntry[]>;
So my question is , how to pass the observable with this:
this.authService.loginStatus().subscribe(user => {
const UUID = user.profile.participant;
this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, () => (this.message = this.errorMessageConnection));
});
}
So that you dont have to use the subscribe method:
.subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, () => (this.message = this.errorMessageConnection));
And that you will see the data in the view
Thank you.
So I want to do this:
ngOnInit() {
this.authService.loginStatus().subscribe(user => {
const UUID = user.profile.participant;
this.correspondenceEntries$ = this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, () => (this.message = this.errorMessageConnection));
});
}
But of course that doesn't work. But I don't know how to do it others?
So this is the output:
dossier-corresponden…ist.component.ts:21
{correspondenceEntries: SimpleChange}
correspondenceEntries: SimpleChange
currentValue: null
firstChange: true
previousValue: undefined
oke, if I do this:
<app-vital10-page [noTopBar]="true">
<h2 class="dossier-page-header">Correspondentie</h2>
<p class="data-entry" *ngIf="!allCorrespondence">{{ message }}</p>
<ng-container *ngIf="(correspondenceEntries$ | async) as correspondenceEntries">
<app-dossier-correspondence-list [correspondenceEntries]="correspondenceEntries" ></app-dossier-correspondence-list>
</ng-container>
<app-dossier-correspondence-item
[item]="single"
(goBack)="goBack($event)"
*ngIf="showingSingle">
</app-dossier-correspondence-item>
</app-vital10-page>
then still no output in view. But I get this in console:
(13) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
But I see this:
currentValue: null
But if I do this:
ngOnInit() {
console.log(this.correspondenceEntries$);
this.authService.loginStatus().subscribe(user => {
const UUID = user.profile.participant;
this.correspondenceEntries$ = this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence')
});
}
Then I see the items in the view.
But so I removed all this:
.subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, () => (this.message = this.errorMessageConnection));
So is this correct?
No, this is not correct. Because now it will be one big list. ANd not two seperated lists(arrays)
I think what you are looking for is the as keyword in the *ngIf directive that you can use as follows:
...
<ng-container *ngIf="(correspondenceEntries$ | async) as correspondenceEntries">
...
<app-dossier-correspondence-list [correspondenceEntries]="correspondenceEntries" ></app-dossier-correspondence-list>
...
</ng-container>
...
The as keyword stores the value of what is before it (in this case the value emitted by the Observable and extracted by the async pipe) in the variable that follows it. Unfortunately the as keyword is currently (i.e. Angular 8) only available within the *ngIf directive, so you have to use *ngIf even if you don't need the conditional template rendering aspect of it.
StackBlitz Example
Ok, I solved like this:
<ng-container *ngIf="correspondenceEntries">
<app-dossier-correspondence-list [correspondenceEntries]="correspondenceEntries"> </app-dossier-correspondence-list>
</ng-container>
<ng-container *ngIf="attachmentEntries">
<app-dossier-correspondence-attachments
[attachmentEntries]="attachmentEntries"
></app-dossier-correspondence-attachments>
</ng-container>
So thank you all for you effort
Related
I have a mat table with a filter that uses chips to filter multiple criterias.
When I go to filter the first time and save the criteria,I get the error ExpressionChangedAfterItHasBeenCheckedError. It says it went from undefined to a number.
I've seen in multiple treads saying that I need to change the way I initialize the table and data to be loaded to the table. I've added ngAfterContentChecked() with this.ref.detectChanges(); and it gets rid of the error, but not the wrong behavior.
I start with ngOnInit getting the data from a service.
What should I do to get the correct behavior?
See the code attached!
thanks!
DetentionValidation.component.ts
import { AfterViewInit, Component, ViewChild, OnInit } from '#angular/core';
import { MatSort, Sort } from '#angular/material/sort';
import { MatTableDataSource } from '#angular/material/table';
import { LiveAnnouncer } from '#angular/cdk/a11y';
import { COMMA, ENTER } from '#angular/cdk/keycodes';
import { MatChipInputEvent } from '#angular/material/chips';
import { SelectionModel } from '#angular/cdk/collections';
import { DetentionValidationService } from './detentionValidation.service';
import { Subscription } from 'rxjs';
import { centerInterface } from './centers-data.model';
import { StopsInterface } from './stops-data.model';
import { ChangeDetectorRef } from '#angular/core';
import { parse } from 'dotenv';
//data interfaces
interface operation {
value: string;
viewValue: string;
}
// export interface StopsInterface {
// StopNbr: number;
// Wgt: number;
// }
export interface SearchItem {
name: string;
}
#Component({
selector: 'app-detentionValidation',
templateUrl: './detentionValidation.component.html',
styleUrls: ['./detentionValidation.component.css'],
})
export class DetentionValidationComponent implements AfterViewInit, OnInit {
isLoading = true;
selectedCenter!: string;
selectedOperation!: string;
operations: operation[] = [
{ value: 'Pickup', viewValue: 'Pickup' },
{ value: 'Delivery', viewValue: 'Delivery' },
];
centers: centerInterface[] = [];
stops: StopsInterface[] = [];
private allDataSubs: Subscription;
//which rows are selected
selection = new SelectionModel<StopsInterface>(true, []);
public globalFilter = '';
public searchItems: SearchItem[] = [];
public reset_data = this.stops;
displayedColumns: string[] = [
'WAND_STOP_NBR',
'UNIT_NBR',
'AGGR_SHP_WGT',
'select',
];
dataSource = new MatTableDataSource(this.stops);
stopsDataSource = [];
selectable = true;
removable = true;
readonly separatorKeysCodes: number[] = [COMMA, ENTER];
public searchTerms: string[] = [];
constructor(
private _liveAnnouncer: LiveAnnouncer,
public detentionValidationService: DetentionValidationService,
private ref: ChangeDetectorRef
) {}
#ViewChild(MatSort, { static: false }) set content(sort: MatSort) {
this.dataSource.sort = sort;
}
ngAfterViewInit() {}
async ngOnInit() {
const response = await this.detentionValidationService.getCenters();
//loading Service centers for drop down
this.detentionValidationService
.getCentersUpdateListener()
.subscribe((centers: centerInterface[]) => {
this.centers = centers;
// this.isLoading = false;
});
//loading all data
this.detentionValidationService
.getStopsUpdateListener()
.subscribe((stops: StopsInterface[]) => {
stops.forEach((element) => {
this.stops.push({
WAND_STOP_NBR: element.WAND_STOP_NBR,
UNIT_NBR: element.UNIT_NBR,
AGGR_SHP_WGT: element.AGGR_SHP_WGT,
});
console.log(element.WAND_STOP_NBR);
});
this.stopsDataSource = this.stops;
this.dataSource.data = this.stopsDataSource;
// this.dataSource.filterPredicate = this.customFilterPredicate();
this.isLoading = false;
});
}
ngAfterContentChecked() {
this.dataSource.sort = this.dataSource.sort;
this.dataSource.filterPredicate = this.customFilterPredicate();
this.ref.detectChanges();
}
onStopToggled(stop: StopsInterface) {
this.selection.toggle(stop);
// console.log(this.selection.selected);
}
isAllSelected() {
return (
// console.log(this.dataSource.filteredData),
this.selection.selected?.length == this.dataSource.filteredData.length
);
}
toggleAll() {
if (this.isAllSelected()) {
this.selection.clear();
} else {
this.selection.select(...this.dataSource.filteredData);
}
}
add(event: MatChipInputEvent): void {
const input = event.chipInput.inputElement;
const value = event.value;
// Add new search term
if ((value || '').trim()) {
this.searchItems.push({ name: value.trim() });
}
// Reset the input value
if (input) {
input.value = '';
}
this.searchTerms = this.searchItems.map(function (searchItem) {
return searchItem.name;
});
this.globalFilter = '';
// console.log('search terms', this.searchTerms);
}
remove(item: SearchItem): void {
const index = this.searchItems.indexOf(item);
if (index >= 0) {
this.searchItems.splice(index, 1);
this.searchTerms = this.searchItems.map(function (searchItem) {
return searchItem.name;
});
this.dataSource.data = [...this.reset_data];
this.dataSource.filter = JSON.stringify(this.searchTerms);
}
}
applyFilter(filterValue: string) {
console.log(filterValue);
this.globalFilter = filterValue;
this.dataSource.filter = JSON.stringify(this.searchTerms);
}
customFilterPredicate() {
const myFilterPredicate = (
data: StopsInterface,
filter: string
): boolean => {
var globalMatch = !this.globalFilter;
if (this.globalFilter) {
// search all text fields
globalMatch =
data.WAND_STOP_NBR.toString()
.trim()
.toLowerCase()
.indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data.UNIT_NBR.toString()
.trim()
.toLowerCase()
.indexOf(this.globalFilter.toLowerCase()) !== -1 ||
data.AGGR_SHP_WGT.toString()
.trim()
.toLowerCase()
.indexOf(this.globalFilter.toLowerCase()) !== -1;
}
if (!globalMatch) {
return false;
}
let parsedSearchTerms = JSON.parse(filter);
let isMatchToAllTerms = true;
for (const term of parsedSearchTerms) {
isMatchToAllTerms =
isMatchToAllTerms &&
(data.WAND_STOP_NBR.toString()
.toLowerCase()
.trim()
.indexOf(term.toLowerCase()) !== -1 ||
data.UNIT_NBR.toString()
.toLowerCase()
.trim()
.indexOf(term.toLowerCase()) !== -1 ||
data.AGGR_SHP_WGT.toString()
.toLowerCase()
.trim()
.indexOf(term.toLowerCase()) !== -1);
}
return isMatchToAllTerms;
};
return myFilterPredicate;
}
announceSortChange(sortState: Sort) {
if (sortState.direction) {
this._liveAnnouncer.announce(`Sorted ${sortState.direction}ending`);
console.log('sorting');
} else {
this._liveAnnouncer.announce('Sorting cleared');
}
}
}
DetentionValidation.component.html
<body>
<div
style="
position: absolute;
height: 80%;
width: 80%;
align-items: center;
display: flex;
justify-content: space-around;
align-content: space-around;
"
>
<mat-spinner *ngIf="isLoading">
<span class="sr-only">Loading...</span>
</mat-spinner>
</div>
<div *ngIf="!isLoading">
<div>
<form>
<div>
<h2>Detention Processing Validation</h2>
<div style="display: flex">
<div style="padding-right: 2%">
<mat-form-field appearance="fill">
<mat-label>Select Service Center</mat-label>
<mat-select [(ngModel)]="selectedCenter" name="car">
<mat-option
*ngFor="let center of centers"
[value]="center.ORIG_LOC_CD"
>
{{ center.ORIG_LOC_CD }}
</mat-option>
</mat-select>
</mat-form-field>
<p>Selected SC: {{ selectedCenter }}</p>
</div>
<div>
<mat-form-field appearance="fill">
<mat-label>Pickup/Delivery</mat-label>
<mat-select [(ngModel)]="selectedOperation" name="operation">
<mat-option
*ngFor="let operation of operations"
[value]="operation.value"
>
{{ operation.viewValue }}
</mat-option>
</mat-select>
</mat-form-field>
<p>Selected Operation: {{ selectedOperation }}</p>
</div>
<div class="fedex-button-row">
<button mat-raised-button color="primary">Submit</button>
</div>
</div>
</div>
</form>
</div>
<!-- table div -->
<div class="stops-table-div">
<mat-chip-list #chipList>
<mat-chip
*ngFor="let item of searchItems"
[selectable]="selectable"
[removable]="removable"
(removed)="remove(item)"
>
{{ item.name }}
<mat-icon matChipRemove *ngIf="removable"> cancel </mat-icon>
</mat-chip>
</mat-chip-list>
<mat-form-field>
<input
matInput
[ngModel]="globalFilter"
(ngModelChange)="applyFilter($event)"
[matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
(matChipInputTokenEnd)="add($event)"
placeholder="Search (press enter for multiple)"
class="form-control input-md"
appearance="fill"
/>
</mat-form-field>
<table
mat-table
class="stops-table mat-elevation-z8"
[dataSource]="dataSource"
matSort
(matSortChange)="announceSortChange($event)"
>
<ng-container matColumnDef="WAND_STOP_NBR">
<th
mat-header-cell
*matHeaderCellDef
mat-sort-header
sortActionDescription="Sort by WAND_STOP_NBR"
>
Stop Nbr
</th>
<td mat-cell *matCellDef="let stop">{{ stop.WAND_STOP_NBR }}</td>
</ng-container>
<ng-container matColumnDef="UNIT_NBR">
<th
mat-header-cell
*matHeaderCellDef
mat-sort-header
sortActionDescription="Sort by UNIT_NBR"
>
Unit Nbr
</th>
<td mat-cell *matCellDef="let stop">{{ stop.UNIT_NBR }}</td>
</ng-container>
<ng-container matColumnDef="AGGR_SHP_WGT">
<th
mat-header-cell
*matHeaderCellDef
mat-sort-header
sortActionDescription="Sort by AGGR_SHP_WGT"
>
Weight
</th>
<td mat-cell *matCellDef="let stop">{{ stop.AGGR_SHP_WGT }}</td>
</ng-container>
<ng-container matColumnDef="select">
<th mat-header-cell *matHeaderCellDef>
<mat-checkbox
color="primary"
[checked]="selection.hasValue() && isAllSelected()"
(change)="toggleAll()"
[indeterminate]="selection.hasValue() && !isAllSelected()"
>
</mat-checkbox>
</th>
<td mat-cell *matCellDef="let stop">
<mat-checkbox
color="primary"
(change)="onStopToggled(stop)"
[checked]="selection.isSelected(stop)"
>
</mat-checkbox>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let stop; columns: displayedColumns"></tr>
</table>
</div>
</div>
</body>
From https://angular.io/errors/NG0100
If the issue exists within ngAfterViewInit, the recommended solution is to use a constructor or ngOnInit to set initial values, or use ngAfterContentInit for other value bindings.
So, you can try to
[...]
export class DetentionValidationComponent implements AfterContentInit {
[...]
ngAfterContentInit () {
//insert content of your ngOnInit
}
In every case, this is just a dev error, in production there is no additional check and no errors throws
Problem is: When I start this component, my ngFor div always updates and my RAM becomes empty. As I know, ngFor updates when array is updated, but my array(announcements) update only once, in constructor.
I have two ngFor divs:
<mat-tab label="Classroom">
<div *ngFor="let announcement of announcements">
<mat-card class="example-card">
<mat-card-header>
<mat-card-subtitle>{{"Announcement: " + announcement.text}}</mat-card-subtitle>
</mat-card-header>
<mat-card-footer>
<div *ngFor="let comment of loadComments(announcement)">
<mat-card class="example-card comment">
<mat-card-header>
<mat-card-subtitle>{{"Comment: " + comment.text}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
</mat-card>
</div>
</mat-card-footer>
</mat-card>
</div>
</mat-tab>
ts file:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup } from '#angular/forms';
import { environment } from 'src/environments/environment';
import { Announcement } from '../model/announcement';
import { Classroom } from '../model/classroom';
import { User } from '../model/user';
import { Comment } from '../model/comment';
import { ClassroomService } from '../service/classroom.service';
import { CommentService } from '../service/comment.service';
import { AnnouncementService } from '../service/announcement.service';
#Component({
selector: 'app-view-classroom',
templateUrl: './view-classroom.component.html',
styleUrls: ['./view-classroom.component.css']
})
export class ViewClassroomComponent implements OnInit {
announcements: Announcement[] | undefined;
comments: Comment[] | undefined;
constructor(private classroomService: ClassroomService,
private commentService: CommentService,
private announcementService: AnnouncementService,
private formBuilder: FormBuilder)
{
this.classroomService.getClassroomUsers(JSON.parse(localStorage.getItem(environment.classroom) || ''), 'teachers').subscribe(
(response: User[]) => this.teachers = response);
this.classroomService.getClassroomUsers(JSON.parse(localStorage.getItem(environment.classroom) || ''), 'students').subscribe(
(response: User[]) => this.students = response);
this.classroomService.getClassroomOwner(JSON.parse(localStorage.getItem(environment.classroom) || '')).subscribe(
(response: User) => this.owner = response);
this.classroom = JSON.parse(localStorage.getItem(environment.classroom) || '');
this.announcementService.getAnnouncementsByClassroom(JSON.parse(localStorage.getItem(environment.classroom) || '')).subscribe(
(response: Announcement[]) => this.announcements = response);
}
ngOnInit(): void {
}
loadComments(announcement: Announcement){
let an = announcement;
this.commentService.getCommentsByAnnouncement(an).subscribe(
(response: Comment[]) => this.comments = response);
return this.comments;
}
}
But when i remove inner ngFor, problem is gone.
What should i do?
What you are doing is wrong.
loadComments(announcement: Announcement){
let an = announcement;
this.commentService.getCommentsByAnnouncement(an).subscribe(
(response: Comment[]) => this.comments = response);
return this.comments; // <-- old values!!
}
As it is right now this metod will return an old version of this.comments, not the one from the response.
Change the metode like this:
loadComments(announcement: Announcement):Observable<Comment[]>{
let an = announcement;
return this.commentService.getCommentsByAnnouncement(an);
}
And in the html file:
<ng-container *ngIg="loadComments(announcement) | async as comments">
<div *ngFor="let comment of comments">
...
</div>
</ng-container>
You're seeing this issue as the data is populating asynchronously. To resolve this, one of solution is to apply reactive programming strategy using RxJs.
Step 1: Replace static array definition to a Subject (import from 'rxjs')
announcements: Announcement[] | undefined;
comments: Comment[] | undefined;
// above two line needs to be changed to
announcements$: Subject<Announcement[] | undefined>;
comments$: Subject<Comment[] | undefined>;
Step 2: Update assignments
this.announcementService.getAnnouncementsByClassroom(
JSON.parse(localStorage.getItem(environment.classroom) || '')
).subscribe(
// (response: Announcement[]) => this.announcements = response <- update this to:
(response: Announcement[]) => this.announcements$.next(response)
);
this.commentService.getCommentsByAnnouncement(an).subscribe(
// (response: Comment[]) => this.comments = response <- update this to:
(response: Comment[]) => this.comments$.next(response)
);
// return this.comments; <- this is not required any more
Step 3: Update HTML
<!-- old -->
<div *ngFor="let announcement of announcements">
<!-- new -->
<div *ngFor="announcements$ | async as announcement">
<!-- old -->
<div *ngFor="let comment of loadComments(announcement)">
<!-- new -->
<div *ngFor="comments$ | async as comment">
just change
*ngFor="let comment of loadComments(announcement)"
to
*ngFor="let comment of comments"
and
loadComments(announcement: Announcement) {
this.commentService.getCommentsByAnnouncement(announcement).subscribe((response: Comment[]) => {
this.comments = response
})
}
I want to add a condition where the table should only show the table if table !== undefined and table has data but hiding the table causes the error.
The condition I am trying to implement is if table !== undefined and table.datasource.data is not empty or null and tableOptions.notifications is falsed show table
else if table !== undefined and table.datasource.data is empty and tableOptions.notifications is true then hide table else show
Any idea guys ? Thanks.
enter image description here
#error
core.js:6162 ERROR TypeError: Cannot read property 'actives' of undefined
at MatMultiSortTableDataSource.orderData (ngx-mat-multi-sort.js:795)
at TableData._clientSideSort (ngx-mat-multi-sort.js:428)
at TableData.set data [as data] (ngx-mat-multi-sort.js:479)
at TableMultiSortComponent.getData (table-multi-sort.component.ts:62)
at table-multi-sort.component.ts:51
at timer (zone-evergreen.js:2561)
at ZoneDelegate.invokeTask (zone-evergreen.js:406)
at Object.onInvokeTask (core.js:28497)
at ZoneDelegate.invokeTask (zone-evergreen.js:405)
at Zone.runTask (zone-evergreen.js:178)
#html code
<mat-card *ngIf="table !== undefined">
<div style="padding-top: 12px;" *ngIf="tableOptions.notifications">
<div class="alertInfo" >
<mat-icon>{{tableOptions.notifications[0].type}}</mat-icon>{{tableOptions.notifications[0].message}}
</div>
</div>
<mat-table mat-table [dataSource]="table.dataSource" matMultiSort (matSortChange)="table.onSortEvent()">
<ng-container *ngFor="let column of table.columns" [matColumnDef]="column.id">
<mat-header-cell class="table-multi-sort-header" *matHeaderCellDef [mat-multi-sort-header]="column.id">
<div>{{column.name}}</div>
<div class="sub-text">{{getColumnSubtitle(column.id)}}</div>
</mat-header-cell>
<mat-cell *matCellDef="let row">
<ng-container *ngIf="column.id !== 'action'; then col; else actionCol"></ng-container>
<ng-template #col>
<app-table-multi-sort-cell-default [cellData]="row" [id]="column.id" [subId]="getColumnSubId(column.id)"></app-table-multi-sort-cell-default>
</ng-template>
<ng-template #actionCol>
<app-table-multi-sort-cell-action [rowData]="row" [actions]="getActions(column.id)" (actionClickEvent)="clickTableAction($event,row)"></app-table-multi-sort-cell-action>
</ng-template>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="table.displayedColumns; sticky:true"></mat-header-row>
<mat-row *matRowDef="let item; columns: table.displayedColumns;"></mat-row>
</mat-table>
<mat-progress-bar *ngIf="isLoading" mode="indeterminate"></mat-progress-bar>
</mat-card>
#ts code
export class TableMultiSortComponent implements OnInit, OnChanges {
#Input() tableOptions:any;
#Input() tableMessage:any;
#Input() tableData:any = [];
test = 0;
#Input() isClientSide:boolean = false;
#Input() isLoading: boolean = false;
#Output() tableActionsEvent = new EventEmitter<any>();
#Output() dataServiceEvent = new EventEmitter<any>() ;
#ViewChild(MatMultiSort, { static: false }) sort: MatMultiSort;
hasInfoMessage: boolean;
tableConfig: any = TABLE_MULTI_SORT_OPTIONS.DEFAULT;
table:TableData<any>;
displayedColumns: any;
constructor() { }
ngOnChanges(changes: SimpleChanges): void {
}
ngOnInit(): void {
this.initTableMultiSort();
}
initTableMultiSort() {
this.tableConfig = {
...this.tableConfig,
...this.tableOptions
}
this.table = new TableData<any>(this.tableConfig.columns,this.tableConfig.sortParams);
this.table.pageSize = this.tableConfig.pageSize;
this.table.pageIndex = this.tableConfig.pageIndex;
this.table.nextObservable.subscribe(() => { this.getData(); });
this.table.sortObservable.subscribe(() => { this.getData(); });
this.table.previousObservable.subscribe(() => { this.getData(); });
this.table.sizeObservable.subscribe(() => { this.getData(); });
setTimeout(()=>{
this.table.dataSource = new MatMultiSortTableDataSource(this.sort, this.isClientSide);
this.getData();
console.log("this.tabl1e", this.table)
},0);
}
getData(){
this.table.totalElements = 1;
this.table.pageIndex = 0;
this.table.totalElements = 0;
this.table.pageSize = 10;
this.table.data = this.tableData;
if(this.dataServiceEvent) {
this.dataServiceEvent.emit(this.table);
}
}
getColumnSubtitle(id) :string{
return this.tableOptions.columns.filter(c => c.id === id)[0].subtitle;
}
getColumnSubId(id) :string{
return this.tableOptions.columns.filter(c => c.id === id)[0].subId;
}
getActions(id):any{
return this.tableOptions.columns.filter(c => c.id === id)[0].actions;
}
enter image description here
If you want to hide the table you shouldn't even instantiate the TableMultiSortComponent component.
Assuming the selector for that component is app-table-multi-sort you should add a *ngIf to it like:
<app-table-multi-sort *ngIf="tableData"></app-table-multi-sort>
I have an Angular 8 application. And I am using the asynccommand for passing data from child to parent component. But the data is returning by the server but is not been displayed by the HTML template.
This is my typescript:
correspondenceEntries$: Observable<DossierEntry[]>;
attachmentEntries$: Observable<DossierEntry[]>;
#Input() allCorrespondence: Array<DossierEntry>;
#Input() correspondenceEntries: Array<DossierEntry>;
#Input() attachmentEntries: Array<DossierEntry>;
message = '';
emptyMessageCorrespondentie = 'Geen correspondentie.';
errorMessageConnection = 'Er ging iets mis met de connectie. Probeer over enkele minuten nogmaals.';
correspondenceLoaded = false;
showingSingle = false;
single: DossierEntry;
constructor(private documentCorrespondeceService: DocumentCorrespondenceService, private authService: AuthService) {}
ngOnInit() {
this.authService.loginStatus().subscribe(user => {
const UUID = user.profile.participant;
this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, msg => (this.message = this.emptyMessageCorrespondentie));
});
}
handleCorrespondenceLoad(result: any) {
if (result.length === 0) {
this.message = this.emptyMessageCorrespondentie;
return;
}
this.allCorrespondence = result;
this.attachmentEntries = [];
this.correspondenceEntries = [];
const groups = _.groupBy(result, 'type');
console.log(this.correspondenceEntries = groups.correspondence);
console.log(this.attachmentEntries = groups.attachments);
this.correspondenceEntries = groups.correspondence;
this.attachmentEntries = groups.attachments;
}
And here is the HTML template:
<h2 class="dossier-page-header">Correspondentie</h2>
<p class="data-entry" *ngIf="!allCorrespondence">{{ message }}</p>
<ng-container *ngIf="(correspondenceEntries$ | async) as correspondenceEntries">
<app-dossier-correspondence-list [correspondenceEntries]="correspondenceEntries" ></app-dossier-correspondence-list>
</ng-container>
<ng-container *ngIf="(attachmentEntries$ | async) as attachmentEntries">
<app-dossier-correspondence-attachments [attachmentEntries] = "attachmentEntries"></app-dossier-correspondence-attachments>
</ng-container>
<app-dossier-correspondence-item
[item]="single"
(goBack)="goBack($event)"
*ngIf="showingSingle">
</app-dossier-correspondence-item>
So when I do this:
console.log(this.correspondenceEntries = groups.correspondence);
console.log(this.attachmentEntries = groups.attachments);
I see the data:
Array(13)
:4200/dossier-dossier-module-ngfactory.js:17533 Array(3)
But it's not displayed in the view.
So how to improve this, that the data will also be showing in the view.
Thank you.
I understand.
But How to do this then:
this.attachmentEntries$ = this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, msg => (this.message = this.emptyMessageCorrespondentie));
});
Of course this doenst work.So how to use the Observables:
correspondenceEntries$: Observable<DossierEntry[]>;
attachmentEntries$: Observable<DossierEntry[]>;
?
Because there are two arrays.
So you mean like this:
<app-dossier-correspondence-list *ngFor="let item of correspondenceEntries" ></app-dossier-correspondence-list>
and this:
ngOnInit() {
this.authService.loginStatus().subscribe(user => {
const UUID = user.profile.participant;
this.documentCorrespondeceService.getDossierEntry(UUID, 'correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, msg => (this.message = this.emptyMessageCorrespondentie));
});
}
But the
child component looks already like this:
<div *ngIf="!showingSingle && correspondenceEntries && correspondenceEntries.length > 0;">
<div class="main-row main-row-dossier">
<section class="data-entry">
<h3 class="dossier-header">Algemeen</h3>
<table class="dossier-table" *ngIf="correspondenceEntries else loadingCorrespondenceEntires ">
<thead class="dossier-tableheader">
<tr>
<th class="dossier-tablehead fixed-one-fifth">Datum</th>
<th class="dossier-tablehead fixed-four-fifths">Onderwerp</th>
</tr>
</thead>
<tbody class="dossier-tablebody">
<tr class="dossier-correspondencerow" *ngFor="let entry of correspondenceEntries; let i = index" (click)="gotoItem(i, entry.type)">
<td>{{ entry.date | date:"dd-MM-y" }}</td>
<td>{{ entry.name }}</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
<ng-template #loadingCorrespondenceEntires>
<div>..Loading </div>
</ng-template>
You are mixing 2 ways to subscribe to an Observable. Choose one, don't do both.
Solution 1:
// component
this.myService.getDataFromServer().subscribe(
result => this.data = result
);
<!-- HTML -->
<div *ngFor="let item of data">
<span>{{item.label}}</span>
</div>
Solution 2:
// component
this.data$ = this.myService.getDataFromServer()
<!-- HTML -->
<div *ngIf="(data$ | async) as data">
<span>{{data.label}}</span>
</div>
SO I have a angular 8 application.
And I have this in the component DossierCorrespondenceComponent:
#Input() showingSingle = false;
So that I can use it in a other component(DossierCorrespondenceListComponent). But if I do this:
<div *ngIf="!showingSingle && correspondenceEntries && correspondenceEntries.length > 0;">
Then I will get this error:
Property 'showingSingle' does not exist on type 'DossierCorrespondenceListComponent'.
So how to fix this?
Thank you
html:
<div *ngIf="!showingSingle && correspondenceEntries && correspondenceEntries.length > 0;">
<div class="main-row main-row-dossier">
<section class="data-entry">
<h3 class="dossier-header">Algemeen</h3>
<table class="dossier-table">
<thead class="dossier-tableheader">
<tr>
<th class="dossier-tablehead fixed-one-fifth">Datum</th>
<th class="dossier-tablehead fixed-four-fifths">Onderwerp</th>
</tr>
</thead>
<tbody class="dossier-tablebody">
<tr class="dossier-correspondencerow" *ngFor="let entry of correspondenceEntries; let i = index" (click)="gotoItem(i, entry.type)">
<td>{{ entry.date | date:"dd-MM-y" }}</td>
<td>{{ entry.name }}</td>
</tr>
</tbody>
</table>
</section>
</div>
</div>
js:
export class DossierCorrespondenceListComponent implements OnInit {
correspondenceEntries: DossierEntry[];
constructor() { }
ngOnInit() {
}
js:
export class DossierCorrespondenceComponent implements OnInit {
#Input()
allCorrespondence: Array<DossierEntry>;
#Input()
correspondenceEntries: Array<DossierEntry>;
#Input()
attachmentEntries: Array<DossierEntry>;
/* allCorrespondence$: Observable<DossierEntry>;
correspondenceEntries$: Observable<DossierEntry>;
attachmentEntries$: Observable<DossierEntry>; */
message = '';
emptyMessageCorrespondentie = 'Geen correspondentie.';
errorMessageConnection = 'Er ging iets mis met de connectie. Probeer over enkele minuten nogmaals.';
correspondenceLoaded = false;
#Input() showingSingle = false;
single: DossierEntry;
constructor(
private healthAPIService: HealthAPIService,
private prevRouterService: PreviousRouteService,
private dossierService: DossierService
) {}
ngOnInit() {
this.healthAPIService.getDossierEntry('correspondence').subscribe(result => {
this.handleCorrespondenceLoad(result), (this.correspondenceLoaded = true);
}, msg => (this.message = this.errorMessageConnection));
}
handleCorrespondenceLoad(result) {
if (result.length === 0) {
this.message = this.emptyMessageCorrespondentie;
return;
}
this.allCorrespondence = result;
this.attachmentEntries = [];
this.correspondenceEntries = [];
const groups = _.groupBy(result, 'type');
groups.correspondence.push(result);
groups.attachments.push(result);
}
gotoItem(index, type: string) {
this.showingSingle = true;
/* _.mapValues(type, function(group, key) {
return type === 'correspondence' ? _.groupBy(group, 'attachments') : group;
}); */
const groupData = _.groupBy(type, item => {
return _.get(item, 'correspondence', 'attachments');
});
switch (type) {
case 'correspondence': {
this.single = this.correspondenceEntries[index];
break;
}
case 'attachments': {
this.single = this.attachmentEntries[index];
break;
}
default: {
break;
}
}
this.showingSingle = true;
}
goBack(event) {
this.showingSingle = false;
}
}
YOu mean in here:
<tbody class="dossier-tablebody">
<tr class="dossier-correspondencerow" *ngFor="let entry of correspondenceEntries; let i = index" (click)="gotoItem(i, entry.type)">
<td>{{ entry.date | date:"dd-MM-y" }}</td>
<td>{{ entry.name }}</td>
</tr>
</tbody>
So this is the html of DossierCorospondenceComponent:
<app [noTopBar]="true">
<h2 class="dossier-page-header">Correspondentie</h2>
<p class="data-entry" *ngIf="!allCorrespondence">{{ message }}</p>
<app-is-loading *ngIf="!correspondenceLoaded" message="Correspondentie wordt geladen"></app-is-loading>
<app-dossier-correspondence-list ></app-dossier-correspondence-list>
<app-dossier-correspondence-item
[item]="single"
(goBack)="goBack($event)"
*ngIf="showingSingle">
</app-dossier-correspondence-item>
</app>
So I have it like this:
export class DossierCorrespondenceListComponent implements OnInit {
correspondenceEntries: DossierEntry[];
#Input() showingSingle = false;
constructor() { }
ngOnInit() {
}
But what I have to put in here:
export class DossierCorrespondenceComponent implements OnInit {
#Input()
allCorrespondence: Array<DossierEntry>;
#Input()
correspondenceEntries: Array<DossierEntry>;
#Input()
attachmentEntries: Array<DossierEntry>;
/* allCorrespondence$: Observable<DossierEntry>;
correspondenceEntries$: Observable<DossierEntry>;
attachmentEntries$: Observable<DossierEntry>; */
message = '';
emptyMessageCorrespondentie = 'Geen correspondentie.';
errorMessageConnection = 'Er ging iets mis met de connectie. Probeer over enkele minuten nogmaals.';
correspondenceLoaded = false;
#Input() showingSingle = false;
single: DossierEntry;
constructor(
private healthAPIService: HealthAPIService,
private prevRouterService: PreviousRouteService,
private dossierService: DossierService
) {}
Because now it is a double property
You can see it said clear that showingSingle not existed in DossierCorrespondenceListComponent, you need to declare it inside DossierCorrespondenceListComponent to use it.
If you can, show us your code for DossierCorrespondenceListComponent and DossierCorrespondenceComponent to see what you want to achieve is better.
You may see document of Angular Input for more infos how to do it Angular Input
UPDATE 1
To be short: DCLC => DossierCorrespondenceListComponent and DCC for DossierCorrespondenceComponent
I see you want to display a list of DCC component, the property showingSingle need to bypass a value from DCLC.
In your ngFor loop, add showingSingle into the loop like this
<div *ngFor="let c of list">
<DCC [showingSingle]="c"></DCC>
</div>
A side note: You should use English instead of French for the name of component, and you are working with Germans