我正在使用MEANStack开发WebApp,并使用Sequelize访问SQL数据库。不幸的是,我在客户端得到了以下错误:core.js:1673错误TypeError:无法读取未定义的属性“ map” 在MapSubscriber.project(tables.service.ts:39)
错误的“第39行”为applicationsTables: tableData.applicationsTables.map(table => {
这是服务器端DT的样子: Data Table on the Browser - Server Side
这是客户端的错误的样子: Error Messages on the Chrome developers' tools view
这是我的代码
tables-list.component.html
<mat-spinner *ngIf="isLoading"></mat-spinner>
<h1 class="mat-body-2">Process List </h1>
<mat-accordion multi="true" *ngIf="userIsAuthenticated && !isLoading">
<mat-expansion-panel>
<mat-expansion-panel-header>
Process List
</mat-expansion-panel-header>
<table mat-table [dataSource]="processTables" matSort class="mat-elevation-z8" *ngIf="userIsAuthenticated">
<!-- ProcessName Column -->
<ng-container matColumnDef="ProcessName">
<th mat-header-cell *matHeaderCellDef mat-sort-header> ProcessName </th>
<td mat-cell *matCellDef="let element"> {{element.ProcessName}} </td>
</ng-container>
<!-- PackageVersion Column -->
<ng-container matColumnDef="PackageVersion">
<th mat-header-cell *matHeaderCellDef mat-sort-header> PackageVersion </th>
<td mat-cell *matCellDef="let element"> {{element.PackageVersion}} </td>
</ng-container>
<!-- RobotType Column -->
<ng-container matColumnDef="RobotType">
<th mat-header-cell *matHeaderCellDef mat-sort-header> RobotType </th>
<td mat-cell *matCellDef="let element"> {{element.RobotType}} </td>
</ng-container>
<!-- PackagePath Column -->
<ng-container matColumnDef="PackagePath">
<th mat-header-cell *matHeaderCellDef mat-sort-header> PackagePath </th>
<td mat-cell *matCellDef="let element"> {{element.PackagePath}} </td>
</ng-container>
<!-- CreationTime Column -->
<ng-container matColumnDef="CreationTime">
<th mat-header-cell *matHeaderCellDef mat-sort-header> CreationTime </th>
<td mat-cell *matCellDef="let element"> {{element.CreationTime}} </td>
</ng-container>
<!-- Status Column -->
<ng-container matColumnDef="Status">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
<td mat-cell *matCellDef="let element"> {{element.Status}} </td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedprocessTablesColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedprocessTablesColumns;"></tr>
</table>
</mat-expansion-panel>
</mat-accordion>
<br> <h1 class="mat-body-2">Applications List </h1>
tables-list.component.ts:
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Table, ApplicationsTable } from "./tables.model";
import { PageEvent } from "@angular/material";
import { Subscription } from "rxjs";
import { TablesService } from "./tables.service";
import { AuthService } from "../auth/auth.service";
@Component({
// We load the component via routing and therefore we do not need a selector
selector: "app-tables",
templateUrl: "./tables-list.component.html",
styleUrls: ["./tables-list.component.css"]
}) // Turn class into component by adding @Component Decorator
export class TableListComponent implements OnInit, OnDestroy {
processTables: Table[] = [];
applicationsTables: ApplicationsTable[] = [];
isLoading = false;
totalTables = 0;
tablesPerPage = 5;
currentPage = 1;
pageSizeOptions = [1, 2, 5, 10];
displayedprocessTablesColumns: string[] = ["ProcessName", "PackageVersion", "RobotType", "PackagePath", "CreationTime", "Status" ];
userIsAuthenticated = false;
userId: string;
isAdmin: boolean;
private tablesSub: Subscription;
private authStatusSub: Subscription;
constructor(
public tablesService: TablesService,
private authService: AuthService
) {}
ngOnInit() {
this.isLoading = true;
this.tablesService.getTables(this.tablesPerPage, this.currentPage);
this.userId = this.authService.getUserId();
this.tablesSub = this.tablesService
.getTableUpdateListener()
.subscribe((tableData: { processTables: Table[]; applicationsTables: ApplicationsTable[]; tableCount: number }) => {
this.isLoading = false;
this.totalTables = tableData.tableCount;
this.processTables = tableData.processTables;
this.applicationsTables = tableData.applicationsTables;
console.log(tableData.applicationsTables);
});
this.userIsAuthenticated = this.authService.getIsAuth();
// console.log("Is authenticated: " + this.userIsAuthenticated);
this.authStatusSub = this.authService
.getAuthStatusListener()
.subscribe(isAuthenticated => {
this.userIsAuthenticated = isAuthenticated;
});
}
onChangedPage(pageData: PageEvent) {
this.isLoading = true;
this.currentPage = pageData.pageIndex + 1;
this.tablesPerPage = pageData.pageSize;
this.tablesService.getTables(this.tablesPerPage, this.currentPage);
}
onLogout() {
this.authService.logout();
}
ngOnDestroy() {
this.tablesSub.unsubscribe();
this.authStatusSub.unsubscribe();
}
}
Tables.service.ts:
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Subject } from "rxjs";
import { map } from "rxjs/operators";
import { Router } from "@angular/router";
import { environment } from "../../environments/environment";
import { Table, ApplicationsTable } from "./tables.model";
const BACKEND_URL = environment.apiUrl + "/tables/";
@Injectable({ providedIn: "root" })
export class TablesService {
private processTables: Table[] = [];
private applicationsTables: ApplicationsTable[] = [];
private tablesUpdated = new Subject<{ processTables: Table[]; applicationsTables: ApplicationsTable[]; tableCount: number }>();
constructor(private http: HttpClient, private router: Router) {}
getTables(tablesPerPage: number, currentPage: number) {
const queryParams = `?pagesize=${tablesPerPage}&page=${currentPage}`;
this.http
.get<{ processTables: Table[]; applicationsTables: ApplicationsTable[]; maxTables: number }>(
BACKEND_URL + queryParams
)
.pipe(
map((tableData: { processTables: Table[]; applicationsTables: ApplicationsTable[]; maxTables: number }) => {
return {
processTables: tableData.processTables.map(table => {
return {
ProcessName: table.ProcessName,
PackageVersion: table.PackageVersion,
RobotType: table.RobotType,
PackagePath: table.PackagePath,
CreationTime: table.CreationTime,
Status: table.Status
};
}),
applicationsTables: tableData.applicationsTables.map(table => {
return {
ProcessName: table.ProcessName,
PackageVersion: table.PackageVersion,
WorkflowsBelongingToProcess: table.WorkflowsBelongingToProcess,
ApplicationsBelongingToWorkflow: table.ApplicationsBelongingToWorkflow
};
}),
maxTables: tableData.maxTables
};
})
)
.subscribe(transformedTablesData => {
this.processTables = transformedTablesData.processTables;
this.tablesUpdated.next({
processTables: [...this.processTables],
applicationsTables: [...this.applicationsTables],
tableCount: transformedTablesData.maxTables
});
});
}
getTableUpdateListener() {
return this.tablesUpdated.asObservable();
}
getTable(id: string) {
return this.http.get<{
ProcessName: string;
PackageVersion: string;
RobotType: string;
PackagePath: string;
CreationTime: string;
Status: string;
}>(BACKEND_URL + id);
}
}
Tables \ model.ts:
export interface Table {
ProcessName: string;
PackageVersion: string;
RobotType: string;
PackagePath: string;
CreationTime: string;
Status: string;
}
export interface ApplicationsTable {
ProcessName: string;
PackageVersion: string;
WorkflowsBelongingToProcess: string;
ApplicationsBelongingToWorkflow: string;
}
后端\模型\ tables.js:
后端\控制器\ tables.js:
const sequelize = require("../sequelize");
exports.getProcessTables = (req, res) => {
sequelize.query("SELECT * FROM dbo.Process", { type: sequelize.QueryTypes.SELECT})
.then(fetchedtables => {
res.status(200).json({
message: "Process table fetched from the server",
processTables: fetchedtables,
maxProcessTables: fetchedtables.length
});
});
};
exports.getApplicationsTables = (req, res) => {
sequelize.query("SELECT * FROM dbo.Applications", { type: sequelize.QueryTypes.SELECT})
.then(fetchedtables => {
res.status(200).json({
message: "Applications Table fetched from the server",
applicationTables: fetchedtables,
maxApplicationsTables: fetchedtables.length
});
});
};
后端\ routes \ tables.js:
const express = require("express");
const TableController = require("../controllers/tables")
const router = express.Router({ mergeParams: true });
router.get("", TableController.getProcessTables);
router.get("", TableController.getApplicationsTables);
module.exports = router;
我该如何解决? 非常感谢 根纳罗
答案 0 :(得分:0)
您正在从服务器返回一个具有“ retrievedTables”属性的对象,但是在客户端上,您正在尝试访问不存在的“表”。
您可以在Backend\controllers\tables.js
或Tables.service.ts
中解决此问题。要将其修复在服务器上,只需将retrievedTables: tables
更改为tables: tables
,以便客户端获取所需的字段。如果要在客户端上修复它,则需要引用retrieveedTables而不是表,并相应地更新您的类型。您也不会从服务器发送maxTables,因此您需要添加它。也许是maxTables: tables.length
。
您还需要确保正确引用属性名称。在服务器上,您正在发送带有大写首字母的表属性,而在客户机上,您正在阅读带有小写首字母的属性,这导致其未定义。
也要小心类型定义。在这种情况下,您是说this.http.get
的返回对象的表属性为any类型,然后尝试调用其map方法。仅某些类型具有map方法,因此请更详细地说明您的期望。甚至将类型指定为任何类型的数组都更好,因为这样可以保证map方法:
this.http
.get<{ tables: any[]; maxTables: number }>(
BACKEND_URL + queryParams
)
通过指定特定类型而不是任何类型,仍可以进一步改进,特别是在继续使用其属性时。更好的类型与用于getTable的返回类型的类型相同,后者可以定义为可重用的接口。
interface TableInstance {
processName: string;
packageVersion: string;
robotType: string;
packagePath: string;
creationTime: string;
status: string;
}
然后,您将如下定义从get返回的类型。
.get<{ tables: TableInstance[]; maxTables: number }>(
您也可以设置map函数的类型,而不是执行上面的操作,而直接从错误消息中解决有问题的行。
.pipe(
map((tableData: { tables: TableInstance[]; maxTables: number }) => {
return {