仅在ngOnInit

时间:2018-03-14 13:37:05

标签: angular typescript rxjs

以下组件使用服务通过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);
  }

}

1 个答案:

答案 0 :(得分:1)

(评论后重写)

Chain Observables而不是嵌套订阅

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

除其他原因外,由于可读性。很难判断哪些动作将以哪种顺序执行。

首先修复:使用flatMap运算符链接Observable s。

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 });
});

其次,强制执行您的操作顺序

根据您上次的评论,这是您的问题的实际原因:您有异步操作订单问题。

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

你在选择之前调用更新/创建(这是有意义的),但由于它们是异步的,有时候select操作会在执行更新/创建之前返回,因此症状是你见证。 (有时接收旧版本,即使始终执行更新)

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

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

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