fix: display CodeLookup values instead of IDs in datatable view (#2896)

Added empty check: parts[1] && parts[1].trim() to ensure the part after _cd_ exists and is not empty (line)
This commit is contained in:
Shubham Kumar
2025-12-13 16:25:49 -06:00
committed by GitHub
parent 4c00e5824d
commit e6bb867424
8 changed files with 95 additions and 13 deletions
+56 -5
View File
@@ -35,6 +35,7 @@ export class Datatables {
public getFormfields(columns: any, dateTransformColumns: string[], dataTableEntryObject: any) {
return columns.map((column: any) => {
const displayLabel = this.toDisplayLabel(column.columnName);
switch (column.columnDisplayType) {
case 'INTEGER':
case 'STRING':
@@ -42,7 +43,7 @@ export class Datatables {
case 'TEXT':
return new InputBase({
controlName: column.columnName,
label: column.columnName,
label: displayLabel,
value: '',
type: column.columnDisplayType === 'INTEGER' || column.columnDisplayType === 'DECIMAL' ? 'number' : 'text',
required: column.isColumnNullable ? false : true
@@ -50,7 +51,7 @@ export class Datatables {
case 'BOOLEAN':
return new CheckboxBase({
controlName: column.columnName,
label: column.columnName,
label: displayLabel,
value: '',
type: 'checkbox',
required: column.isColumnNullable ? false : true
@@ -58,7 +59,7 @@ export class Datatables {
case 'CODELOOKUP':
return new SelectBase({
controlName: column.columnName,
label: column.columnName,
label: displayLabel,
value: '',
options: { label: 'value', value: 'id', data: column.columnValues },
required: column.isColumnNullable ? false : true
@@ -70,7 +71,7 @@ export class Datatables {
}
return new DatepickerBase({
controlName: column.columnName,
label: column.columnName,
label: displayLabel,
value: '',
maxDate: this.settingsService.maxAllowedDate,
required: column.isColumnNullable ? false : true
@@ -81,7 +82,7 @@ export class Datatables {
dataTableEntryObject.dateFormat = Dates.DEFAULT_DATETIMEFORMAT;
return new DateTimepickerBase({
controlName: column.columnName,
label: column.columnName,
label: displayLabel,
value: '',
maxDate: this.settingsService.maxAllowedDate,
required: column.isColumnNullable ? false : true
@@ -174,6 +175,56 @@ export class Datatables {
return columnName;
}
public toDisplayLabel(columnName: string): string {
if (!columnName) {
return '';
}
// Handle CODELOOKUP pattern: extract the part after _cd_
if (columnName.includes('_cd_')) {
const parts = columnName.split('_cd_');
// Ensure parts[1] exists and is not empty before processing
if (parts.length > 1 && parts[1] && parts[1].trim()) {
// Return the part after _cd_ converted to Title Case
// Filter out standalone "cd" or "CD" words that are artifacts from naming convention
// This only affects display, not database column names
const displayWords = parts[1]
.split('_')
.filter((word) => word.trim() && word.toLowerCase() !== 'cd') // Remove empty strings and standalone "cd" artifacts
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ');
// Return empty string if all words were filtered out, otherwise return formatted label
return displayWords || parts[1].trim();
}
}
// Convert snake_case to Title Case (e.g., "created_at" -> "Created At")
// For non-CODELOOKUP fields, only filter standalone "cd" if it appears as an artifact
return (
columnName
.split('_')
.filter((word) => word.trim() && word.toLowerCase() !== 'cd') // Remove empty strings and standalone "cd" artifacts
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(' ') || columnName
); // Fallback to original if all filtered out
}
public getCodeLookupValue(columnHeader: any, id: number): string {
if (!columnHeader?.columnValues || id === null || id === undefined) {
return '';
}
const codeValue = columnHeader.columnValues.find((cv: any) => cv.id === id);
return codeValue ? codeValue.value : id.toString();
}
public getCodeName(columnName: string): string {
if (columnName && columnName.includes('_cd_')) {
return columnName.split('_cd_')[0];
}
return '';
}
public isValidUrl(urlString: string): boolean {
try {
const url = new URL(urlString);
@@ -235,6 +235,11 @@ export class DatatableMultiRowComponent implements OnInit, OnDestroy, OnChanges
if (typeof value === 'number') {
value = this.numberFormat.transform(value);
}
} else if (columnDisplayType === 'CODELOOKUP') {
if (columnHeader.columnValues && value !== null && value !== undefined) {
const codeValue = columnHeader.columnValues.find((cv: any) => cv.id === value);
value = codeValue ? codeValue.value : value;
}
}
return true;
}
@@ -25,8 +25,7 @@
@for (columnHeader of dataObject.columnHeaders; track columnHeader; let i = $index) {
<div class="flex-fill" [ngClass]="setAttributeClass(columnHeader.columnName)">
<div class="mat-body-strong left flex-40">
{{ columnHeader.columnDisplayName }}
{{ getInputName(columnHeader.columnName) }}
{{ datatables.toDisplayLabel(columnHeader.columnName) }}
</div>
<div class="right flex-60">
@switch (getColumnType(columnHeader.columnDisplayType, columnHeader.columnType)) {
@@ -50,6 +49,11 @@
{{ dataObject.data[0].row[i] | formatNumber }}
</span>
}
@case ('CODELOOKUP') {
<span>
{{ datatables.getCodeLookupValue(columnHeader, dataObject.data[0].row[i]) }}
</span>
}
@case ('TEXT') {
<span class="long-text">
{{ dataObject.data[0].row[i] }}
@@ -20,8 +20,6 @@
.system-defined {
margin-top: 3px;
color: $status-approved !important;
font-weight: 100;
}
.long-text {
@@ -43,7 +43,7 @@ export class DatatableSingleRowComponent implements OnInit {
private dateUtils = inject(Dates);
private dialog = inject(MatDialog);
private settingsService = inject(SettingsService);
private datatables = inject(Datatables);
public datatables = inject(Datatables);
private systemService = inject(SystemService);
@Input() dataObject: any;
@@ -123,7 +123,8 @@ export class DatatableSingleRowComponent implements OnInit {
const data = {
title: 'Edit ' + this.datatableName + ' for ' + this.entityType,
formfields: formfields,
layout: { addButtonText: 'Save' }
layout: { addButtonText: 'Submit' },
pristine: false
};
const editDialogRef = this.dialog.open(FormDialogComponent, { data, width: '50rem' });
editDialogRef.afterClosed().subscribe((response: any) => {
@@ -182,6 +183,9 @@ export class DatatableSingleRowComponent implements OnInit {
case 'DECIMAL': {
return columnDisplayType;
}
case 'CODELOOKUP': {
return columnDisplayType;
}
case 'TEXT': {
if (columnType === 'JSON') {
return 'JSON';
@@ -31,7 +31,12 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header class="center">
{{ 'labels.inputs.Field Name' | translate }}
</th>
<td mat-cell *matCellDef="let dataTableColumn">{{ dataTableColumn.columnName }}</td>
<td mat-cell *matCellDef="let dataTableColumn">
{{ getFieldDisplayName(dataTableColumn.columnName) }}
@if (datatables.getCodeName(dataTableColumn.columnName)) {
<span class="code-name">({{ datatables.getCodeName(dataTableColumn.columnName) }})</span>
}
</td>
</ng-container>
<ng-container matColumnDef="columnDisplayType">
@@ -1,3 +1,9 @@
table {
width: 100%;
}
.code-name {
opacity: 0.6;
font-size: 0.9em;
margin-left: 4px;
}
@@ -19,7 +19,8 @@ import {
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
/** Custom Services */
import { SystemService } from '../../system.service';
import { SystemService } from 'app/system/system.service';
import { Datatables } from 'app/core/utils/datatables';
/** Custom Components */
import { TranslateService } from '@ngx-translate/core';
@@ -60,6 +61,7 @@ export class ViewDataTableComponent implements OnInit {
private router = inject(Router);
private dialog = inject(MatDialog);
private translateService = inject(TranslateService);
public datatables = inject(Datatables);
/** Data Table Data */
dataTableData: any;
@@ -105,6 +107,13 @@ export class ViewDataTableComponent implements OnInit {
this.setColumnsTable();
}
/**
* Get display name for field (e.g., "Actividad" from "ADDRESS_TYPE_cd_ACTIVIDAD")
*/
getFieldDisplayName(columnName: string): string {
return this.datatables.toDisplayLabel(columnName);
}
/**
* Initializes the data source, paginator and sorter for columns table.
*/