MEAN-Stack-无法读取未定义的属性“ map”

时间:2018-11-21 15:59:23

标签: sql mean-stack

我正在使用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 &nbsp; </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 &nbsp; </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;

我该如何解决? 非常感谢 根纳罗

1 个答案:

答案 0 :(得分:0)

您正在从服务器返回一个具有“ retrievedTables”属性的对象,但是在客户端上,您正在尝试访问不存在的“表”。

您可以在Backend\controllers\tables.jsTables.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 {