Angular订阅行为异常-数组为空

时间:2020-04-30 21:53:12

标签: angular

我正在跟踪Angular英雄之旅示例,并以相同的方式构造(我认为)代码版本,但是没有收到我期望的行为。

我的服务

import { Injectable } from '@angular/core';
import { PORTS } from './mock-ports'
import { Port } from './port'
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})

export class UpdateportsService {

  private controller_url = '/gelante/getports/150013889525632'
  private controller_ip = 'http://localhost:8080'

  getPorts(): Observable<Port[]> {
    return this.http.get<Port[]>(this.controller_ip + this.controller_url)
  }

  constructor(private http: HttpClient) { }
}

myObserver(用于调试)

const myObserver = {
  next: x => console.log('Observer got a next value: ' + x),
  error: err => console.error('Observer got an error: ' + err),
  complete: () => console.log('Observer got a complete notification'),
};

getPorts(订阅可观察的服务)

// This is part of the same class as getPorts
ports: Port[] = [];

getPorts(): void {
    // To subscribe to an observable, you take the declared observable, which in
    // this case is getPorts (which returns an array of ports) and then you
    // subscribe to it. Anytime the observable is called it will emit values
    // which are then sent to all subscribers.
    console.log(this.ports)
    this.updateportsService.getPorts().subscribe(ports => this.ports = ports);

    // This prints all of my port data as expected
    this.updateportsService.getPorts().subscribe(myObserver);

    console.log(this.ports)
  }

调试控制台的完整输出

Array(0) []
switch.component.ts:76
Array(0) []
switch.component.ts:82
Angular is running in the development mode. Call enableProdMode() to enable the production mode.
core.js:40917
Observer got a next value: [object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
switch.component.ts:13
Observer got a complete notification
switch.component.ts:15
[WDS] Live Reloading enabled.

目标

目标是列出我从REST API(与Angular分开)中接收到的交换机接口的列表,并将它们分配给称为端口的字典列表。这应该在以下行中完成:

this.updateportsService.getPorts().subscribe(ports => this.ports = ports);

问题

在英雄之旅中,应填充函数getPorts中的示例端口。我已经从Wireshark和一些调试输出中确认HTTP get请求正在按预期运行。具体来说,您可以看到以下行:

this.updateportsService.getPorts().subscribe(myObserver);

它接收大量对象(如预期)。但是,无论出于何种原因,ports => this.ports = ports中的分配似乎都不起作用。 ports的值始终是一个零元素的空数组。但是,我还不能弄清楚为什么。

3 个答案:

答案 0 :(得分:2)

这是在分配值之前尝试访问异步数据的简单情况。在这种情况下,this.ports是异步分配的。因此,当您执行console.log(this.ports)时,尚未为其分配任何值。但是,当您使用myObserver时,它可以工作,因为您正在按预期那样在订阅内部进行打印。使用ports的确切等价词如下

this.updateportsService.getPorts().subscribe(
  ports => { 
    this.ports = ports;
    console.log(this.ports);
  },
  error => {
     // it is always good practice to handle error when subscribing to HTTP observables
  }
);

请参见here,以了解有关异步请求的更多信息。

async管道与控制器中的订阅

在大多数情况下,建议使用

async管道,因为它可以避免订阅可观察对象,从而避免出现内存泄漏问题。手动订阅可观察对象时,最好在OnDestroy挂钩中取消订阅。

import { Subscription } from 'rxjs';

export class AppComponent implements OnInit, OnDestroy {
  obsSubscription: Subscription;

  ngOnInit() {
    this.obsSubscription = this.service.observable.subscribe(value => { // handle value });
  }

  ngOnDestroy() {
    if (this.obsSubscription) {
      this.obsSubscription.unsubscribe();
    }
  }
}

通常,在使用unsubscribe时会忽略HttpClient,因为它处理取消订阅并避免内存泄漏。但是也有例外。例如,如果用户离开进行HTTP调用的链接,则它可能仍处于打开状态。因此,始终建议您手动关闭订阅。

还有一种使用takeUntil处理取消订阅的优雅方法。

import { Subject, pipe } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

export class AppComponent implements OnInit, OnDestroy {
  closeActive = new Subject<void>();

  ngOnInit() {
    this.obsSubscription = this.service.observable
      .pipe(takeUntil(this.closeActive))
      .subscribe(value => { // handle value });
  }

  ngOnDestroy() {
    this.closeActive.next();
    this.closeActive.complete();
  }
}

答案 1 :(得分:0)

我不了解您的观察者调试,但是我认为您应该这样做(您应该避免手动订阅并使用异步管道,但这不是您的问题):

丢失调试观察器并在您的getPorts方法中执行以下操作:

:id

希望这有助于调试

答案 2 :(得分:0)

它接收大量对象(如预期)。但是,对于 无论出于什么原因,端口分配=> this.ports = ports都不 似乎有效。 ports的值始终是零的空数组 元素。但是,我还不能弄清楚为什么。

好吧,您用subscribe(myObserver);消耗了观测值

您可以按照@ user3791775的建议使用pipetap

或扩展您的观察者并在那里分配值。

const myObserver = {
  next: ports => {
    this.ports = ports;
    console.log('Observer got a next value: ' + ports)
  },
  error: err => console.error('Observer got an error: ' + err),
  complete: () => console.log('Observer got a complete notification'),
};

-编辑

实际上还有另一种解决方法

您可以创建Subject来处理多个订阅。

getPorts(): Subject<Port[]> {
    const subject = new Subject<Port[]>();
    return this.http.get<Port[]>(this.controller_ip + this.controller_url).subscribe(ports => subject.next(ports));
    return subject;
  }