I've designed a generic detailing component for entities in my backend api. The service delivers a DTO with everything, so there's no problem with my api. I am using angular materials for the table display (and made a custom header swap to make the table vertical).
Since the details are generic, the routing only links the details route to one component, ex: /company/1 is a path for my details component, /user/3 is also a path for the details but it will display the user 3 fields
Some fields are entities and I want to let the user jump to the specific entity details by clicking it. The component constructor subscribes to the route paramMap and waits for a change from the routerLink and I notice the url change on my browser when I click. The problem for now is that the table doesn't change, some values disapear but it's structure doesn't change. Although if I manually refresh in this state, the page fixes itself.
Here's what I have:
app-routing.module.ts
const routes: Routes = [
{ path: ':entity', children: [
{ path: ':id', component: DetailsComponent },
]
}
];
@NgModule({
imports: [RouterModule.forRoot(routes, {
onSameUrlNavigation: 'reload'
})],
exports: [RouterModule]
})
export class AppRoutingModule { }
details.component.html
<div class="container-fluid" style="margin-top: 10px;">
<div class="row d-flex justify-content-center">
<div class="col-4">
<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">
<ng-container [matColumnDef]="column" *ngFor="let column of displayColumns">
<th mat-header-cell *matHeaderCellDef>{{ column }}</th>
<td mat-cell *matCellDef="let element">
<div *ngIf="column === 'id'">{{ element[column] }}</div>
<div *ngIf="column !== 'id'">
<a *ngIf="element['rLink']" routerLink="{{ element['rLink'] }}">{{ element[column] }}</a>
<div *ngIf="element['rLink'] === undefined">{{ element[column] }}</div>
</div>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayColumns"></tr>
<tr mat-row *matRowDef="let row; let even = even; columns: displayColumns" [ngClass]="{whiteGray: even}"></tr>
</table>
</div>
</div>
</div>
details.component.ts
@Component({
selector:'app-detail',
templateUrl: './details.component.html',
styleUrls: ['./details.component.css',
'../../../styles.css']
})
export class DetailsComponent implements OnInit {
dataSource: any[] = [];
displayColumns: string[] = [];
constructor(private service: ApiService, private route: ActivatedRoute, private router: Router, private knownNameService: KnownEntityNameService) {
route.paramMap.subscribe(() => this.ngOnInit()); //I am awaire this causes the table to load twice
}
ngOnInit(): void {
this.route.paramMap.subscribe((d: ParamMap) => {
this.setFlipedTableData(d);
});
}
private setFlipedTableData(d: ParamMap) {
//this is to make sure you can't just type anything in the browser url
if(!Object.values(DTO_TYPE).includes(d.get('entity') as DTO_TYPE))
this.router.navigateByUrl('/');
this.service.getById(d.get('entity'), d.get('id')).subscribe((dto: BasicDTO) => {
var currentKeys;
const keys = Object.keys(dto);
const key = keys[0];
const value = dto[keys[0]].toString();
this.displayColumns = [ key, value ];
var objData: { [key: string]: string } = {};
keys.forEach(k => {
if(k === 'id')
return;
objData[key] = k;
//if the fields are null I still want them to show 'null' (yes I can make this better)
//some fields may have a different name than the entity they represent
//the knownNameService corrects that
switch(dto[k]) {
case null: {
objData[value] = 'null';
break;
}
default: {
if(dto[k] instanceof Object) {
currentKeys = Object.keys(dto[k]);
objData['rLink'] = '/' + this.knownNameService.getKnownEntityName(k) + '/' + dto[k][currentKeys[0]];
objData[value] = dto[k][currentKeys[1]];
}
else {
objData[value] = dto[k];
}
break;
}
}
this.dataSource = this.dataSource.concat(objData);
objData = {};
});
});
}
}
Right now the display works fine and I can click an entity link with the route change on the browser. But the table is still messed up. I have to refesh the page to fix it.
Once the url changed I expect the component to be refreshed.