类型错误:_form_form_component__WEBPACK_IMPORTED_MODULE_57__ 未定义

问题描述 投票:0回答:1

我正在开发一个 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 {

angular typescript webpack
1个回答
0
投票

此类问题大多是由于循环依赖而产生的。

我建议使用 madge 来检测您的循环导入。然后就由你来解决它们了。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.