【问题标题】:Angular observable: Why is the parameter undefinedAngular observable:为什么参数未定义
【发布时间】:2018-01-15 18:03:24
【问题描述】:

我对@9​​87654321@ 参数有疑问,就我而言,我有一个具有consult() 功能和参数响应的consultService。在使用参数响应之前调用sendConsultRequest() 函数。 sendConsultRequest 工作得很好,因为我可以在页面中看到结果,但是当我想使用参数响应导出内容时,我遇到了问题:

ERROR TypeError: Cannot read property 'dataList' of undefined

在我的选择中,响应参数在导出操作之前已经初始化,因为consultPage 工作得很好。 这是服务代码,你能告诉我为什么它不起作用吗?

@Injectable()
export class ConsultService {
  private url = 'api/consultApplication';
  response: Application;

sendConsultRequest(id: string) {
  console.log('sendConsultRequest');
  this.http.get<Application[]>(this.url)
    .subscribe(response => {
      response.map(res => {
        this.response = res;
        this.updateApplication(res);
      }),
        pipe(catchError(this.handleError('sendConsultRequest', [])));
    });
}

这里是导出页面ts:

ngOnInit() {
  console.log('ExportComponent init');
  console.log(this.consultService.response);
  this.application = this.consultService.response;

  this.appID= this.consultService.response.dataList.find(pair => 'PROCEDURE' === pair.field)
      ? this.consultService.response.dataList.find(pair => 'PROCEDURE' === pair.field).value : null;
}

谢谢。

【问题讨论】:

  • 但是等等...如果您发送异步请求,您希望如何立即获得响应?
  • 我不想马上得到响应,其实我有2个步骤,第1步:consult,会调用函数request并初始化属性Response,然后第2步,导出,这将使用属性响应。第一步总是好的,我认为属性 Response 应该被初始化,但是没有。

标签: angular typescript observable


【解决方案1】:

每当您收到Cannot read property 'foo' of undefined 错误时,这意味着您尝试从中读取数据的对象从未被赋值。

在 JavaScript 中,如果您不设置某些内容,它将始终默认为 undefined

let foo; // foo is undefined

此外,如果您希望函数有一个参数,但该参数从未被赋值,那么它也将是未定义的。

function foo(arg) {
   console.log(foo);
}

foo(); // the console logs 'undefined'

因此,一个很好的经验法则是,当您遇到此类错误时,这意味着该变量从未在您的代码中分配过值,您应该查看您认为您正在分配的位置它是一个值,然后重新阅读代码以确保它实际上按照您的要求工作。

这一行:

this.appID= this.consultService.response.dataList.find(pair => 'PROCEDURE' === pair.field)
  ? this.consultService.response.dataList.find(pair => 'PROCEDURE' === pair.field).value : null;

在这种情况下将不起作用,因为 this.consultService.response 未定义。您只能在为 this.consultService.response 赋值后调用该逻辑。

未定义的原因是您进行了异步 HTTP 调用,然后您尝试立即访问该值。但 HTTP 调用尚未返回,因此您的值将是未定义的。

如果你想从异步 HTTP 调用中接收值,你应该从你的服务返回 observable,然后在你的组件中订阅它。

服务:

sendConsultRequest(id: string): Observable<any> {
  return this.http.get<Application[]>(this.url).pipe(
    tap(response => {
      return response.map(res => {
        this.response = res;
        this.updateApplication(res);
      })
    ),
    catchError(this.handleError('sendConsultRequest', []))
  );
}

现在在你的组件中:

ngOnInit() {
    this.consuleService
        .sendConsuleRequestId(id)
        .subscribe(response => {
            this.appID = response.dataList.find(pair => 'PROCEDURE' === pair.field) ? response.dataList.find(pair => 'PROCEDURE' === pair.field).value : null;
        });
}

更新(显示ReplaySubject 示例)- 未测试:

服务:

consultStream: ReplaySubject = new ReplaySubject();
consult$: Observable<Consult> = this.consultStream.asObservable();

constructor() {
     this.sendConsultRequest(1);
}

sendConsultRequest(id: string): Observable<any> {
  return this.http.get<Application[]>(this.url)
      .subscribe((consult: Consult) => this.consultStream.next(consult));
}

组件:

ngOnInit() {
    this.consult$
        .subscribe((consult: Consult) => {
            // ...
        });
}

【讨论】:

  • 非常感谢。函数 sendConsuleRequestId() 已经被另一个页面调用了,不想第二次调用,可以只订阅参数响应吗?
  • 在这种情况下,您应该做的是在您的服务上创建一个可观察的流。当您调用sendConsuleRequestId 时,将响应结果添加到可观察流中,然后您可以从任何组件订阅该流。确保它是ReplaySubject,这样即使其他组件已经订阅了它,您也可以一遍又一遍地获取它。如果您需要这方面的帮助,请告诉我,但请先尝试一下。以下是从主题创建可观察流的示例:stackoverflow.com/questions/45423102/…
  • 我试过这个,但我总是得到未定义的值,我将主题设置为新的 replaySubject(1) 我应该再次调用 next() 函数吗?
  • 在consultService中,我在第一步咨询期间调用this.applicationReplaySubject.next(application),然后在第二步导出期间,我在构造函数中订阅了这个主题,但我总是遇到未定义的参数错误。
  • @pierre 确保在尝试订阅组件中的可观察对象之前给 ReplaySubject 一个值。我会在我的回答中举出上面的例子。
【解决方案2】:

这不是它的工作方式。

这是它的工作原理。

@Injectable()
export class ConsultService {
  private url = 'api/consultApplication';
  response: Application;

sendConsultRequest() {
  console.log('sendConsultRequest');
  return this.http.get<Application[]>(this.url)
}

在你的组件中

ngOnInit() {
  console.log('ExportComponent init');
  console.log(this.consultService.response);

  this.consultService.sendConsultRequest().subscribe(
    response => {
      response.map(res => {
        this.consultService.updateApplication(res);
        this.appID = response.dataList.find(pair => 'PROCEDURE' === pair.field); 
      });
    }, error => this.consultService.handleError('sendConsultRequest', [])
  );
}

按照您执行此操作的方式,您永远不会调用您的服务(或者您没有发布调用的代码)。而且由于您的服务进行了异步调用,并且您想要等待它,因此您应该让您的组件等待它,而不是您的服务。

按照您执行此操作的方式,您的组件不会等待您的服务结束调用。它只是突然取了一个值,这个值很可能等于 null 或 undefined。

【讨论】:

  • 非常感谢,所以我无法在本地保存回复?我只想调用一次sendConsultRequest,并将结果保存为响应,然后我想使用参数而不是调用http。
  • 您应该为此使用BehaviorSubject。这样,您调用端点一次,保存响应,并将其存储在您的主题中。其他组件将订阅此主题,并获得响应。这就是你想要的吗?
  • 所以我可以将响应存储在主题中,然后订阅主题以获得现有/旧值?
  • 没错。首先创建一个具有空值的主题,然后在您的组件中订阅它。如果值为 null,则不执行任何操作,否则执行某些操作。每次拨打mySubject.next(value),主题都会触发你的订阅
  • 但是在我打开这个页面之前已经调用了next()函数,我可以得到旧值吗?
猜你喜欢
  • 1970-01-01
  • 2019-12-20
  • 1970-01-01
  • 2018-04-02
  • 2017-04-06
  • 2020-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多