我正在开发一个 Angular 项目,我当前的任务是将所有组件变成独立的。然而,有一个组件运行不佳,并在控制台中抛出此错误:
TypeError: _form_form_component__WEBPACK_IMPORTED_MODULE_57__ is undefined
请注意,此错误仅在启动应用程序后出现在浏览器的控制台中,捆绑包的构建没有任何问题。所有其他组件都已经是独立的并且工作正常,该组件是唯一(也是最后一个)运行效果不佳的组件。
组件的代码如下所示:
import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { Validators } from '@angular/forms';
import { DownloadService, DynatraceService } from '@enercity/ap-core';
import { CellValue, Column, Workbook } from 'exceljs';
import { cloneDeep } from 'lodash-es';
import {
ApUiButtonComponent,
ApUiButtonsComponent,
ApUiFormComponent,
ApUiModalComponent,
FormConfig,
} from '../../components';
import { ToastService } from '../../services/toast.service';
import { ApUiDatatableComponent } from '../datatable/datatable.component';
interface FormValue {
fileType: 'excel' | 'csv';
rowCount: number;
columnIds: string[];
}
@Component({
selector: 'ui-datatable-export-modal',
templateUrl: './datatable-export-modal.component.html',
styleUrls: ['./datatable-export-modal.component.scss'],
standalone: true,
imports: [ApUiButtonComponent, ApUiButtonsComponent, ApUiFormComponent, ApUiModalComponent],
})
export class ApUiDatatableExportModalComponent<Row, Filter, SubtableRow> implements OnInit {
@Input() public datatable!: ApUiDatatableComponent<Row, Filter, SubtableRow>;
@ViewChild('form') public form?: ApUiFormComponent<FormValue>;
@ViewChild('modal') public modal?: ApUiModalComponent;
public initialValue: Partial<FormValue> = {};
public loading = false;
public formConfig?: FormConfig<FormValue>;
constructor(
private downloadService: DownloadService,
private toast: ToastService,
private dynatrace: DynatraceService,
) {}
public ngOnInit(): void {
if (!this.datatable) {
throw new Error('DatatableExportModalComponent: invalid input - datatable is missing');
}
const visibility = this.datatable.columnVisibility.state;
this.initialValue = {
columnIds:
this.datatable.config?.columns
.map((column, index) => {
if (visibility.includes(column.property)) {
return index.toString();
}
return 'invisible';
})
.filter((item) => item !== 'invisible') ?? [],
fileType: 'excel',
rowCount: 1000,
};
this.formConfig = {
sections: [
{
description: this.datatable.i18n.texts.exportingModalFormDescription,
rows: [
{
controls: [
{
type: 'RADIOS',
property: 'fileType',
trackId: `${this.datatable.config?.uniqueId}-file-type`,
texts: {
label: this.datatable.i18n.texts.exportingModalFiletypeLabel,
},
validators: [Validators.required],
orientation: 'VERTICAL',
options: [
{
value: 'excel',
text: this.datatable.i18n.texts.exportingModalExcelOptionText,
},
{
value: 'csv',
text: this.datatable.i18n.texts.exportingModalCSVOptionText,
},
],
},
],
},
{
controls: [
{
type: 'NUMBERFIELD',
property: 'rowCount',
trackId: `${this.datatable.config?.uniqueId}-row-count`,
texts: {
label: this.datatable.i18n.texts.exportingModalRowCountLabel,
},
validators: [Validators.required, Validators.min(0), Validators.max(10000)],
min: 0,
max: 10000,
step: 1000,
},
],
},
{
controls: [
{
type: 'CHECKBOXES',
viewType: 'SWITCH',
property: 'columnIds',
trackId: `${this.datatable.config?.uniqueId}-column-ids`,
orientation: 'VERTICAL',
allOptionEnabled: true,
texts: {
label: this.datatable.i18n.texts.exportingModalVisibleColumnsLabel,
},
options: this.datatable.viewParser.headings,
},
],
},
],
},
],
};
}
public async export(value: Partial<FormValue>): Promise<void> {
if (this.datatable.config) {
try {
let rows: (string | number)[][];
if (
this.datatable.settings.features.serverSideProcessing &&
value.rowCount &&
value.fileType
) {
this.loading = true;
const params = cloneDeep(this.datatable.serverSide.params);
if (params.pagination) {
params.pagination.limit = value.rowCount;
params.pagination.offset = 0;
}
const result = await this.datatable.serverSide.performFetch(
params,
this.datatable.filter.value,
);
rows = this.datatable.dataitemService.transformMultiConfigsAndValues(
result.data,
this.datatable.config.columns,
);
} else {
rows = this.datatable.dataitemService.transformMultiConfigsAndValues(
this.datatable.data,
this.datatable.config.columns,
);
}
if (value.rowCount && rows.length > value.rowCount) {
rows.length = value.rowCount;
}
this.processViewAppearances(rows);
rows = rows.map((row) =>
row.filter((item, index) => value.columnIds?.includes(index.toString())),
);
const headings = this.datatable.viewParser.headings
.map((item) => item.text)
.filter((item, index) => value.columnIds?.includes(index.toString()));
if (value.fileType === 'csv') {
this.exportCSV(rows, headings);
} else if (value.fileType === 'excel') {
await this.exportExcel(rows, headings);
}
} catch {
this.dynatrace.reportError(
`ap-ui: Datatable ${this.datatable.config?.uniqueId} - error during ${value.fileType} export`,
);
this.toast.showError({
message: this.datatable.i18n.texts.exportingModalErrorToastMessage,
});
}
this.modal?.close();
this.loading = false;
}
}
private processViewAppearances(rows: (string | number)[][]): void {
if (this.datatable.config) {
const columns = this.datatable.config.columns;
for (const row of rows) {
for (let i = 0; i < row.length; i++) {
const viewConfig = columns[i].view;
if (viewConfig?.type === 'COUNT') {
const count = this.sanitizedCount(row[i]);
row[i] = `${count} ${
count === 1
? this.datatable.translateOrNotPipe.transform(
viewConfig.texts.singularKey,
viewConfig.texts.singular,
)
: this.datatable.translateOrNotPipe.transform(
viewConfig.texts.pluralKey,
viewConfig.texts.plural,
)
}`;
}
}
}
}
}
private sanitizedCount(value: string | number): number {
if (typeof value === 'string') {
const parsed = parseInt(value, 10);
if (!isNaN(parsed)) return parsed;
}
if (typeof value === 'number') {
if (!isNaN(value)) return value;
}
return 0;
}
private exportCSV(rows: (string | number)[][], headings: string[]): void {
const content = `"${headings.join('","')}"\n`.concat(
rows.map((row) => `"${row.join('","')}"`).join('\n'),
);
this.downloadService.download(content, `download.csv`);
}
private async exportExcel(rows: (string | number)[][], headings: string[]): Promise<void> {
const workbook = this.createWorkbook(rows, headings);
// Write workbook data to buffer
const buffer = await workbook.xlsx.writeBuffer();
// Trigger download of the Excel file
this.downloadService.downloadBlob(new Blob([buffer]), 'download.xlsx');
}
private createWorkbook(rows: (string | number)[][], headings: string[]): Workbook {
const workbook = new Workbook();
const sheet = workbook.addWorksheet('Download');
sheet.columns = headings.map((item) => ({ header: item }));
for (const row of rows) {
sheet.addRow(row);
}
// Iterate over each column to set data type for numeric cells (exceljs sees numbers as strings)
sheet.columns.forEach((column: Partial<Column>, index: number) => {
const columnValues = column.values;
if (columnValues) {
columnValues.forEach((value: CellValue, rowIndex: number): void => {
const cell = sheet.getCell(rowIndex + 1, index + 1);
if (cell.value != null && cell.value !== '') {
const cellValue = cell.value.toString();
// Check if the value has leading zeros
const hasLeadingZeros = /^0\d+/.test(cellValue);
// Check if the value is large enough to be written in scientific notation
const needsScientificNotation = !isNaN(Number(cellValue)) && Number(cellValue) >= 1e15;
if (hasLeadingZeros || needsScientificNotation) {
cell.value = cellValue;
} else if (!isNaN(Number(cellValue))) {
cell.value = Number(cellValue);
if (typeof cell.value === 'number') {
cell.numFmt = '0';
}
}
}
});
}
});
// Dynamically adjust column widths based on content length
sheet.columns.forEach((column: Partial<Column>) => {
if (column.values) {
const lengths =
column.values.map((v: CellValue) => {
const typedV = v as string | number;
return typedV?.toString().length ?? 0;
}) ?? [];
const maxLength = Math.max(...lengths.filter((v: number) => typeof v === 'number'));
column.width = maxLength;
}
});
return workbook;
}
}
控制台中的完整错误是
Uncaught TypeError: _form_form_component__WEBPACK_IMPORTED_MODULE_57__ is undefined
ApUiFormComponent main.js:10528
31619 datatable-export-modal.component.ts:27
31619 datatable-export-modal.component.ts:240
Webpack 17
__webpack_require__
57317
__webpack_require__
14874
__webpack_require__
98918
__webpack_require__
50635
__webpack_require__
84429
__webpack_require__
__webpack_exec__
<anonymous>
O
<anonymous>
webpackJsonpCallback
<anonymous>
main.js:10528:70
ApUiFormComponent main.js:10528
31619 datatable-export-modal.component.ts:27
31619 datatable-export-modal.component.ts:240
Webpack 17
__webpack_require__
57317
__webpack_require__
14874
__webpack_require__
98918
__webpack_require__
50635
__webpack_require__
84429
__webpack_require__
__webpack_exec__
<anonym>
O
<anonym>
webpackJsonpCallback
<anonym>
第 27 行是导入数组,具体为
ApUiFormComponent
,第 240 行是 private createWorkbook(rows: (string | number)[][], headings: string[]): Workbook {
此类问题大多是由于循环依赖而产生的。
我建议使用 madge 来检测您的循环导入。然后就由你来解决它们了。