0

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.

before

after

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437

0 Answers0