【问题标题】:Service called only occasionally in ngOnInit仅在 ngOnInit 中偶尔调用的服务
【发布时间】:2018-08-23 01:39:39
【问题描述】:

以下组件使用服务通过 HttpClient 模块从服务器获取数据。该服务基本有效。选择单个BankAccount时,BankAccountListComponent已被销毁,并显示BankAccountEditComponent。单击保存时,会执行 BankaccountEditComponent 中的 saveBankaccount 方法(见下文)。数据被发送到服务器并(始终)存储。保存数据后,将显示 BankaccountListComponent。在 ngOnInit 中,它应该再次获取数据。

问题是 BankaccountListComponent 的 ngOnInit (总是)被执行,但数据并不总是被获取。它不是一直有效,我不知道为什么。

来自 BankaccountEditComponent:

  saveBankaccount() {
    this.subscription = this.route.params
      .subscribe(params => {
        const id = (params['id'] || '');
        if (id) {
          this.bankaccountService.update(id, this.bankaccount).subscribe(bankaccount => {
            this.bankaccount = bankaccount;
          });
        } else {
          this.bankaccountService.add(this.bankaccount).subscribe(bankaccount => {
            this.bankaccount = bankaccount;
          });
        }
        const relUrl = this.router.url.includes('edit') ? '../..' : '..';
        this.router.navigate([relUrl], { relativeTo: this.route });
      });
  }

BankaccountListComponent 的全部内容:

import { Component, OnInit, OnDestroy } from '@angular/core';

import { BankaccountService } from '../../services/bankaccount.service';
import { Bankaccount } from '../../domain/bankaccount';

@Component({
  selector: 'ac-bankaccount-list',
  templateUrl: './bankaccount-list.component.html',
  styleUrls: ['./bankaccount-list.component.css']
})
export class BankaccountListComponent implements OnInit, OnDestroy {

  bankaccounts: Bankaccount[];
  bankaccountSelectedId: number;

  constructor(private bankaccountService: BankaccountService) { }

  ngOnInit() {
    console.log('init BankaccountListComponent');
    this.getBankaccounts();
  }

  ngOnDestroy() {
    console.log('destroying BankaccountListComponent');
  }

  getBankaccounts() {
    this.bankaccountService.getBankaccounts().subscribe(bankaccounts => {
      this.bankaccounts = bankaccounts;
      console.log('this.bankaccount: ' + Array.prototype.map.call(this.bankaccounts, function(bankaccount) { return bankaccount.name; }).join(", "));
    });
  }

  selectBankaccount(bankaccountId: number) {
    this.bankaccountSelectedId = bankaccountId;
    console.log('id of bankaccount selected: ' + this.bankaccountSelectedId);
  }

  deleteBankaccount(bankaccountId: number) {
    console.log('id of bankaccount to delete: ' + bankaccountId);
    this.bankaccountService.delete(bankaccountId).subscribe(_ => {
      this.getBankaccounts();
    });
  }

}

所有 BankaccountService:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import 'rxjs/add/operator/map';

import { Bankaccount } from '../domain/bankaccount';

@Injectable()
export class BankaccountService {

  private headers = new HttpHeaders();
  private bankaccountsUrl = 'http://localhost:8090/account/accounts/';

  constructor(private httpClient: HttpClient) {
    this.headers = this.headers.set('Content-Type', 'application/json');
    this.headers = this.headers.set('Accept', 'application/json');
  }

  getBankaccounts(): Observable<Bankaccount[]> {
    return this.httpClient.get<Bankaccount[]>(this.bankaccountsUrl).map((result: any) => {
      console.log('fetched ' + result._embedded.accounts.length + ' bankaccounts from server');
      return result._embedded.accounts;
    });
  }

  getBankaccount(id: number): Observable<Bankaccount> {
    return this.httpClient.get<Bankaccount>(this.bankaccountsUrl + id).map((result: any) => {
      console.log('fetched bankaccount with id ' + result.id + ' from server');
      return result;
    });
  }

  update(id: number, bankaccount: any): Observable<Bankaccount> {
    return this.httpClient.put<Bankaccount>(this.bankaccountsUrl + id, bankaccount);
  }

  add(bankaccount: Bankaccount): Observable<Bankaccount> {
    return this.httpClient.post<Bankaccount>(this.bankaccountsUrl, bankaccount);
  }

  delete(id: number): Observable<Bankaccount> {
    console.log('will delete bankaccount ' + this.bankaccountsUrl + id);
    return this.httpClient.delete<Bankaccount>(this.bankaccountsUrl + id);
  }

}

【问题讨论】:

  • 我可以看到您正在嵌套可观察订阅,这不是一个好习惯。不过,不确定它是否与您的问题有关
  • 您是否使用 *ngFor 来显示数据?您是否已经尝试在this.bankaccounts = bankaccounts; 下放置console.log() 以可视化this.bankaccounts 中的数据?
  • @Lorenzo 是的,我正在使用 *ngFor。 getBankaccounts()方法中已经有console.log语句了!?
  • @Pac0 不知道我该如何改变它仍然可以工作!?
  • @Mfried 有时会记录一些东西,有时会不正确?

标签: angular typescript rxjs


【解决方案1】:

(在cmets之后重写)

链式 Observables 而不是嵌套订阅

首先,嵌套订阅是不好的做法。

还有其他原因,因为可读性。很难判断哪些动作会按什么顺序执行。

第一个修复:使用flatMap 运算符链接Observables。

this.subscription = this.route.params
  .flatMap(params => {  // use 'flatMap' instead of 'subscribe' here
    const id = (params['id'] || '');
    if (id) {
      // here return the Observable that goes next instead of subscribing
      return this.bankaccountService.update(id, this.bankaccount);
    } else {
      // here also
      return this.bankaccountService.add(this.bankaccount);
    }
  })
  // here you  action following the 'update' or 'add' 
  // (what you wanted to do nested in the previous block)
  .subscribe(bankaccount => {
     this.bankaccount = bankaccount;
  });
  const relUrl = this.router.url.includes('edit') ? '../..' : '..';
  this.router.navigate([relUrl], { relativeTo: this.route });
});

其次,执行你的操作顺序

根据您上次的 cmets,这是您问题的实际原因:您有异步操作顺序问题。

您只想在创建/编辑操作返回后导航。目前,编辑和获取之间存在竞争条件。

您在选择之前调用更新/创建(这是有道理的),但由于它们是异步的,有时选择操作在更新/创建执行之前返回,因此您的症状见证。 (有时会收到旧版本,即使总是执行更新)

解决方案:仅在“编辑/创建”完成之后调用“读取”操作。

希望这里的修复非常简单:只需在订阅回调中移动 router.navigate 部分,因为这部分是在操作返回后执行的:

  .subscribe(bankaccount => {
     this.bankaccount = bankaccount;
     const relUrl = this.router.url.includes('edit') ? '../..' : '..';
     this.router.navigate([relUrl], { relativeTo: this.route });
  });

【讨论】:

  • 另外,如果flatMap 不起作用,请尝试switchMap
  • 两者都无法编译:“'Observable' 类型上不存在属性 'flatMap'”。
  • @Mfried 如果是这样,则您缺少这些运算符的导入语句。你应该能够使用类似的东西:import 'rxjs/add/operator/flatMap';(与switchMap相同)
  • 对此感到抱歉。但导入相同的结果。
  • 我完全删除了变量订阅并用const id = this.route.snapshot.params['id']; 替换它以避免另一个 Observable 中的 Observable。然而,同样的结果。奇怪的。我也认为可能是 BankaccountListComponent 的选择比 BankaccountEditComponent 中的更新更快。但是,根据服务器上的日志,选择是在更新之后。
猜你喜欢
  • 1970-01-01
  • 2017-12-07
  • 2018-11-14
  • 1970-01-01
  • 1970-01-01
  • 2018-07-21
  • 2014-10-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多