【问题标题】:Return observable of object from subscribe of an http request angular从 http 请求角度的订阅中返回对象的 observable
【发布时间】:2019-07-11 07:48:42
【问题描述】:

我正在我的角度服务层上创建一种商店。

private attributes: Observable<Attribute[]>;
private attributes$: BehaviorSubject<Attribute[]>;

当用户要求allAttributes() 时填写。然后对所有属性或单个属性 (getAttribute(id)) 的后续请求从同一存储返回数据。

这是我的getAttribute()

getAttribute(id: number): Promise<Observable<Attribute>> {
    return new Promise((resolve) => {
        let _attributeObservable;
        const _attributes: Attribute[] = this.getAttributesState();
        let _attributeFound = false;
        for (const _attribute of _attributes) {
            if (_attribute.id === id) {
                _attributeFound = true;
                break;
            }
        }
        if (_attributeFound) {
            _attributeObservable = this.attributes.pipe(map((_attributeList: Attribute[]) => {
                return _attributeList.find(_attribute => _attribute.id === id);
            }));
            resolve(_attributeObservable);
        } else {
            return this.http.get(`${this.context}/attributeService/getAttribute/${id}`)
                .subscribe((_attributeInfo: Attribute) => {
                    const _allAttributes = this.getAttributesState();
                    _allAttributes.push(_attributeInfo);
                    // push object to store that was not found
                    this.attributes$.next(Object.assign([], _allAttributes));
                    _attributeObservable = this.attributes.pipe(map((_attributeList: Attribute[]) => {
                        return _attributeList.find(_attribute => _attribute.id === id);
                    }));
                    resolve(_attributeObservable);
                });
        }
    });
}

getAttributesState(): Attribute[] {
   return this.attributes$.getValue();
}

现在有些情况下其他用户可以添加属性,这样该属性就不会出现在商店中。因此,如果未找到请求的 属性,则会发出 http 请求并将其保存到存储中。

但问题是如果找到属性然后它工作,但其他部分不工作。可能是什么问题?这段代码是否可以简化,更好的方法?

【问题讨论】:

  • 为什么要在Promise 中返回Observable?看起来很复杂。我认为您应该看看RxJS 的地图运算符(switchMapconcatMapmergeMap 等),您可以将它们链接起来。你可以查看这个marble diagrams
  • 是的,我可以返回Observable&lt;Observable&lt;Attribute&gt;&gt;,因为我需要连接属性来监听变化。
  • 只返回一个 Observable 的属性。你不应该在 Observable 中有一个 Observable。
  • @DenniJensen,我需要连接属性,以便可以监听属性中的更新等进一步更改。这就是为什么它会这样。能否请您给出解决问题的答案?
  • 一个热门的 observable 可以让你听到变化...

标签: angular promise rxjs observable


【解决方案1】:

在重构代码一段时间后,我想我理解了这段代码的用途。

据我理解正确 如果已经存储了属性,您希望避免服务器调用。通过在 BehaviourSubject 中查找给定 id 的属性来指示存储的属性。 如果您没有找到属性,代码将触发 http 客户端从服务器获取属性。

清理看起来像这样。

import { Component, OnInit } from '@angular/core';
import { Observable, BehaviorSubject, of } from 'rxjs';
import { map } from 'rxjs/operators';
import { Attribute } from '../attribute';
import { HttpClient } from '@angular/common/http';

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

  private attributesAsObservable: Observable<Attribute[]>;
  private attributes$: BehaviorSubject<Attribute[]>;
  private context = 'localhost:3000';

  constructor(private http: HttpClient) { }

  ngOnInit() {
    let attributes = [{id: 12, name: 'test'}] as Attribute[];
    this.attributes$ = new BehaviorSubject<Attribute[]>(attributes)
    this.attributesAsObservable = of(attributes)

    console.log("Find id: 12", this.getAttribute(12))
    console.log("Find id causes server call: 1", this.getAttribute(1))

  }

  getAttribute(id: number): Observable<Attribute> {
    let attributeFound = this.findFromStored(id);
    if (attributeFound) {
      return of(attributeFound)
    } else {
      return of(this.fetchAttributeFromServer(id))
    }
  }

  private findFromStored(id: number): Attribute {
    let attributes = this.attributes$.getValue();
    return attributes.find(attribute => attribute.id === id)
  }

  private fetchAttributeFromServer(id: number): Attribute {
    this.httpCall(id).subscribe( attribute => {
      this.addNewAttributeToStore(attribute);
    });
  }

  private addNewAttributeToStore(attribute: Attribute) {
    let attributes: Attribute[] = this.attributes$.getValue();
    attributes.push(attribute)
    this.attributes$.next(attributes)
  }

  //THIS SHOULD BE EXTRACTED TO A SERVICE
  private httpCall(id: number): Observable<Attribute> {
    console.log('Return fake http Observable');
    return of<Attribute>({id: 1, name: 'test'})
    // return this.http.get<Attribute>(
    //   `${this.context}/attributeService/getAttribute/${id}`
    // );
  }
}

如果您从服务器获取值,则此重构不起作用。原因是异步 http 调用。 HTTP 客户端将在 Observable 上返回,我们无法确定服务器何时响应。

IMO 你可以做的是在你的组件上引入一个新属性。此属性包含BehaviourSubject&lt;Attribute&gt;(或在您的情况下为BehaviourSubject&lt;Observable&lt;Attribute&gt;&gt;)。让我们称之为 currentAttribute$。任何时候你打电话给getAttribute(id),你都会打电话给currentAttribute$.next()

让我们改变它。

import { Component, OnInit } from '@angular/core';
import { Observable, BehaviorSubject, of } from 'rxjs';
import { Attribute } from '../attribute';
import { HttpClient } from '@angular/common/http';

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

  private attributesAsObservable: Observable<Attribute[]>;
  private attributes$: BehaviorSubject<Attribute[]>;
  private currentAttributeFoundById: BehaviorSubject<Attribute>;
  private context = 'localhost:3000';

  constructor(private http: HttpClient) { }

  ngOnInit() {
    let attributes = [{id: 12, name: 'test'}] as Attribute[];
    this.attributes$ = new BehaviorSubject<Attribute[]>(attributes);
    this.attributesAsObservable = of(attributes);
    this.currentAttributeFoundById = new BehaviorSubject<Attribute>({});

    this.currentAttributeFoundById.subscribe(attribute => {
      console.log('Current Attribute by ID is:', attribute)
    });

    this.setAttributeBy(12);
    this.setAttributeBy(12);
    this.setAttributeBy(1);
  }

  setAttributeBy(id: number) {
    let attributeFound = this.findFromStored(id);
    if (attributeFound) {
      this.currentAttributeFoundById.next(attributeFound);
    } else {
      this.setAttributeFromServer(id)
    }
  }

  private findFromStored(id: number): Attribute {
    let attributes = this.attributes$.getValue();
    return attributes.find(attribute => attribute.id === id)
  }

  private setAttributeFromServer(id: number) {
    this.httpCall(id).subscribe(attribute => {
      this.addNewAttributeToStore(attribute);
      this.currentAttributeFoundById.next(attribute);
    });
  }

  private addNewAttributeToStore(attribute: Attribute) {
    let attributes: Attribute[] = this.attributes$.getValue();
    attributes.push(attribute)
    this.attributes$.next(attributes)
  }

  //THIS SHOULD BE EXTRACTED TO A SERVICE
  private httpCall(id: number): Observable<Attribute> {
    console.log('Return fake http Observable');
    return of<Attribute>({id: 1, name: 'test'})
    // return this.http.get<Attribute>(
    //   `${this.context}/attributeService/getAttribute/${id}`
    // );
  }
}

此更改允许代码按预期运行(仅在需要时从服务器获取)。

如 cmets 中所述,您可以使用 switchMapconcatMapmergeMap 等来获得第一个解决方案。

【讨论】:

    猜你喜欢
    • 2019-07-14
    • 2021-09-02
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 2020-06-24
    • 2018-08-22
    • 2020-04-09
    • 2017-02-17
    相关资源
    最近更新 更多