【问题标题】:Error in console when trying to route to 404 page in Angular尝试在 Angular 中路由到 404 页面时控制台出错
【发布时间】:2021-10-26 16:28:51
【问题描述】:

当 URL 地址不正确时,我正在尝试在 Angular 上创建一个页面的路由。

控制台出错

在我的 IDE 中没有错误消息。 我在控制台上收到的唯一错误消息是:

ERROR TypeError: Cannot read property 'name' of undefined
     at SingleAppareilComponent.ngOnInit (single-appareil.component.ts:19)
     at callHook (core.js:2526)
     at callHooks (core.js:2495)
     at executeInitAndCheckHooks (core.js:2446)
     at refreshView (core.js:9480)
     at refreshEmbeddedViews (core.js:10590)
     at refreshView (core.js:9489)
     at refreshComponent (core.js:10636)
     at refreshChildComponents (core.js:9261)
     at refreshView (core.js:9515)

以下是我的文件:

  • SingleAppareilComponent
  • AppModule
  • 404 页面的 HTML 模板
  • SingleAppareilComponent 的 HTML 模板
  • AppareilService

single.appareil.component.ts

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { AppareilService } from 'services/appareil.service';

@Component({
  selector: 'app-single-appareil',
  templateUrl: './single-appareil.component.html',
  styleUrls: ['./single-appareil.component.scss']
})
export class SingleAppareilComponent implements OnInit {

  name: string = 'Appareil';
  status: string = 'Statut';
  constructor(private appareilService: AppareilService,
              private route: ActivatedRoute) { }

  ngOnInit(): void {
    const id = this.route.snapshot.params['id'];
    this.name = this.appareilService.getApparreilById(+id).name;
    this.status = this.appareilService.getApparreilById(+id).status;
  }
}

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AppareilComponent } from './appareil/appareil.component';
import { FormsModule } from '@angular/forms';
import { AppareilService } from 'services/appareil.service';
import { AuthComponent } from './auth/auth.component';
import { AppareilViewComponent } from './appareil-view/appareil-view.component';
import { RouterModule, Routes } from '@angular/router';
import { AuthService } from 'services/auth.service';
import { SingleAppareilComponent } from './single-appareil/single- 
appareil.component';
import { FourOhFourComponent } from './four-oh-four/four-oh-four.component';
import { HashLocationStrategy, LocationStrategy } from '@angular/common';

const appRoutes: Routes = [
  { path: 'appareils', component: AppareilViewComponent },
  { path: 'appareils/:id', component: SingleAppareilComponent },
  { path: 'auth', component: AuthComponent },
  { path: '', component: AppareilViewComponent },
  { path: 'not-found', component: FourOhFourComponent },
  { path:'**', redirectTo: '/notfound' }
]

@NgModule({
  declarations: [
    AppComponent,
    AppareilComponent,
    AuthComponent,
    AppareilViewComponent,
    SingleAppareilComponent,
    FourOhFourComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    RouterModule.forRoot(appRoutes)
  ],
  providers: [
    AppareilService,
    AuthService,
    {provide: LocationStrategy, useClass: HashLocationStrategy}
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

它应该显示这个模板:

four-oh-four.component.html

<h2>Erreur 404</h2>
<p>La page que vous cherchez n'existe pas.</p>

但它会显示这个

single-appareil.component.hmtl

<h2>{{ name }}</h2>
<p>Statut : {{ status }}</p>
<a routerLink="/appareils">Retourner à la liste</a>

appareil-service.ts

export class AppareilService {
    appareils = [
        {
          id: 1,
          name: 'Machine à laver',
          status: 'éteint'
        },
        {
          id:2,
          name: 'Frigo',
          status: 'allumé'
        },
        {
          id:3,
          name: 'Ordinateur',
          status: 'éteint'
        }
      ];


    getApparreilById(id: number) {
      const appareil = this.appareils.find(
        (appareilObject) =>{
          return appareilObject.id === id;
        }
      );
      return appareil
    }

    switchOnAll() {
        for (let appareil of this.appareils) {
            appareil.status = 'allumé'
        }
    }

    switchOffAll() {
        for (let appareil of this.appareils) {
            appareil.status = 'éteint'
        }
    }
    
    switchOnOne(index: number) {
        this.appareils[index].status = 'allumé';
    }

    switchOffOne(index: number) {
        this.appareils[index].status = 'éteint';
    }   

【问题讨论】:

  • 可能this.appareilService.getApparreilById(+id) 返回undefined。打开浏览器的开发工具,进入调试器,在ngOnInit设置断点,调试服务方法this.appareilService.getApparreilById
  • 将有助于查看您的AppareilService。显然,当通过 appareils/:id 路由到 SingleAppareilComponent 时,它会为给定的 id 参数返回 undefined

标签: javascript angular typescript routes


【解决方案1】:

为什么控制台会显示这个错误?

错误类型错误:无法读取未定义的属性“名称” 在 SingleAppareilComponent.ngOnInit (single-appareil.component.ts:19)

作为commented by jabaa,您的组件内的函数返回undefined。然后,在同一错误的 第 19 行 中,分配尝试访问此不存在对象的属性: this.name = this.appareilService.getApparreilById(+id).name

如何调试这个?

  • 打开浏览器的开发工具,进入调试器,在错误行设置断点
  • 或在您的源代码中添加调试日志以显示更多详细信息

在您的SingleAppareilComponent 中尝试记录从当前路由路径中提取的id 参数以及getApparreilById(+id) 的返回值:

    const id = this.route.snapshot.params['id'];
    console.log('Routed to SingleAppareilComponent to display appareil with id: ', id)
    let appareil = this.appareilService.getApparreilById(+id);
    console.log('AppareilService returned appareil: ', appareil):
    this.name = appareil.name; // if appareil is undefined then you will get an error here

可能发生了什么?

要进一步分析您的问题,我们必须查看您的AppareilService 组件,尤其是方法getApparreilById

测试您的服务

appareils = [{
    id: 1,
    name: 'Machine à laver',
    status: 'éteint'
  },
  {
    id: 2,
    name: 'Frigo',
    status: 'allumé'
  },
  {
    id: 3,
    name: 'Ordinateur',
    status: 'éteint'
  }
];

function getApparreilById(id) {
  const appareil = this.appareils.find(
    (appareilObject) => {
      return appareilObject.id === id;
    }
  );
  return appareil
}

/*  Tested with vairous inputs: if not found, then undefined!  */
console.log("getApparreilById(1) returns: ", getApparreilById(1));
console.log("getApparreilById(2) returns: ", getApparreilById(2));
console.log("getApparreilById(3) returns: ", getApparreilById(3));
console.log("getApparreilById(4) returns: ", getApparreilById(4));
console.log("getApparreilById('') returns: ", getApparreilById(''));
console.log("getApparreilById('1') returns: ", getApparreilById('1'));

所以find 方法总是返回undefined 如果appareil 与指定id 没有找到。

undefined 只是一个标识符(或者你可以说一个占位符,它表示“Uups,对不起:没有,nada,null”)。它没有可访问的属性(如 name)。

所以错误信息是:

无法读取属性“名称”属于未定义

有一个对象undefined,但是它没有属性,所以你不能读写任何属性。

另一种返回方式

您也可以返回 已定义默认值对象,但具有值,例如:

getApparreilById(id: number) {
  const appareil = this.appareils.find(a => a.id === id);
  if (appareil === undefined) {
    return {id: 0, name: '', status: ''};  // defined but empty object
  }
  return appareil;
}

后端 HTTP GET 调用的假设

假设服务尝试从您的后端获取具有指定id 的服装资源,例如:GET /appareils/:id 并期望 HTTP 状态为 200 的响应和正文中的 appareil。

或者显然在这里(在不愉快的情况下),如果在后端找不到具有指定 id 的 appareil,则返回 HTTP 状态 404。

那么主体可能是空的,所以getApparreilById 返回一个undefined appareil 对象。如果没有对象,则没有属性name

以编程方式重定向到 404 页面

如果undefined 被返回(表示“appareil not found”),你可以(如你所愿)重定向到另一个组件,例如你的 404 页面。

您的 404 页面的路径是在您应用的路由定义中定义的:

{ path: 'not-found', component: FourOhFourComponent },

然后你可以在你的组件SingleAppareilComponent中使用Angular的Router

import { Router } from '@angular/router'; // import the router module here

export class SingleAppareilComponent implements OnInit {

  name: string = 'Appareil';
  status: string = 'Statut';

  // inject router in constructor as parameter 
  constructor(private appareilService: AppareilService,
              private route: ActivatedRoute,
              private _router: Router) {
  }

  ngOnInit(): void {
    const id = this.route.snapshot.params['id'];
    let appareil = this.appareilService.getApparreilById(+id);

    if (typeof appareil === 'undefined') {
      // use router to redirect to 404 page as defined in your routes
      this._router.navigateByUrl('/not-found');
    }

    // else continue
    this.name = appareil.name;
    this.status = appareil.status;
  }
}

注意:get 方法只调用一次(性能更好)。 未定义的测试也可以简化为myVar === undefined(在大多数情况下应该具有相同的效果)。

替代错误处理

另一种方法是从您的服务方法中返回一个 Promise。 这将允许您为两种情况定义回调(快乐的 200 和所有不快乐的 404)。

见: Angular: Http vs fetch api

进一步阅读

【讨论】:

  • 谢谢您,我已经仔细阅读了您的回答,对于您的建议,我会尝试阅读更多内容。但我仍然不明白为什么属性“名称”是未定义的?你能帮我吗?我在最初的问题AppareilService 中添加了您所要求的内容。非常感谢!!
  • 感谢您的评论,以及您问题中的AppareilService 代码。我更新了我的答案并解释了为什么未定义是“未找到”的错误信号,而不是具有“名称”等属性的对象。单击“测试您的服务”部分中的演示,并阅读下面我的建议,您可以如何返回 empty-object 而不是 undefined。然后有一个属性“名称”。
【解决方案2】:

它实际上在 ngOnInit 的这一行中提到了确切的行问题:

    this.name = this.appareilService.getApparreilById(+id).name;

我怀疑你应该在那里返回一个 observable 而不是一个值并订阅它,因为当调用 ngOnInit 时你怎么知道你的数据被加载了。

【讨论】:

  • 我同意你的观点,我很确定 this.appareilService.getAparrailById() 是一个 Observable,所以 GooshFen 应该订阅它以获得响应
  • 我认为它甚至不是可观察的,因为它返回了undefined,但可以修复为返回一个可观察的,可以在未来的任何时间给他数据(当它们被加载时)关于修复服务以支持异步数据加载,而不是依赖于仅在调用 ngOnInit 时存在的任何值。
  • 只有一种情况,如果没有异步调用。然后实际上在内存集合中的本地没有找到东西,但我们无法从上面的代码中看出
猜你喜欢
  • 2018-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-07
  • 1970-01-01
  • 1970-01-01
  • 2023-01-26
相关资源
最近更新 更多