4

我已经看到教程展示了在 Angular 中实现 observables 的无数不同方法。就我的目的而言,其中许多似乎过于复杂。其他的用于以前的版本,不再工作。

假设我有一个服务,它有一个名为 的属性numChickens,并且我想允许组件订阅该属性。Do.i.really((need)=> to.chain((a)=>{million.garbledyGook().statements.to('gether)}) 可以做到吗?

这是相关服务的代码:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})

export class ChickenService {

  public chickens: number; 

  constructor() { }

}

...这是使用可观察的组件的代码:

import { Component, OnInit } from '@angular/core';
import { ChickenService } from '../chicken.service';

@Component({
  selector: 'app-chickendisplay',
  templateUrl: './chickendisplay.component.html',
  styleUrls: ['./chickendisplay.component.scss']
})

export class ChickenDisplayComponent implements OnInit {

  constructor(public cs: ChickenService) {
  }

  ngOnInit() {
  }

}

在 Angular 6 中,什么是最简单、最直接、最易读的方式来公开chickensChickenService 中的属性,以便组件类可以以可观察流的形式访问该属性的值?或者让组件模板可以使用异步管道显示值?

我怎么强调都不过分——请不要回答包含 8,192 个字符的闭包墙然后说“看,这很简单”。

我问这个问题不仅是为了我自己,也是为了像我这样的其他人,他们试图将注意力集中在 observables 上,并为所有关于该主题的密集和过时的教程而苦苦挣扎。如果您可以将此解决方案简化为简单的形式,后代将感谢您。

4

3 回答 3

4

最简单的方法是创建一个 private Subject,然后用它来创建你的 public Observable

在下面的代码中,我为您的变量创建了一个getand 。这意味着每次使用 (例如) 更新它时,它都会自动在流上使用新值触发一个新事件。setchickensservice.chickens = 10Observable

import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

export class ChickenService {

  private _chickens: number; // Make this private so we can expose it via a get/set
  private chickenChange$ = new Subject<number>(); // This will be used to create our Observable
  public chickens$ = this.chickenChange$.asObservable(); // This is our Observable

  constructor() { }

  set chickens(val: number) {
    this._chickens = val; // Set the new value
    this.chickenChange$.next(val); // Trigger the subject, which triggers the Observable
  }

  get chickens() {
    return this._chickens;
  }

}
于 2018-07-29T07:22:25.213 回答
3

如果您真的想将其公开为流,您所要做的就是添加一行:

import { Injectable } from '@angular/core';
import { of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ChickenService {

  public chickens: number; 
  public chickens$ = of(chickens);
  constructor() { }
}

请注意,这样做仍然不允许您更新值,因此这样做没有太多附加值。

您可以使用 Subject 通过订阅流来更新值并在 UI 中反映这些更改:

import { Injectable } from '@angular/core';
import { of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class ChickenService {

  public chickens: number;

  public chickens$ = chickensSub$.asObservable();
  private chickensSub$ = new BehaviorSubject();
  constructor() { }

  updateChickens(value: number) {
    this.chichensSub$.next(value);
  }
}

注意:我尽量避免在不必要时明确使用主题。

于 2018-07-29T07:17:26.723 回答
2

您可能会考虑使用装饰器将属性转换为可观察对象。这样做的细节并不是特别有趣,但是一旦你完成了,你就可以写出类似的东西:

export class ChickenService {
  @Observablize()
  public chickens: number; 

  public chickens$: Observable<number>;
}

好的,现在深呼吸。装饰器的实现大致如下(未经测试)。

function Observablize() {
  return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
    const observableKey = propertyKey + "$";
    const privateKey = propertyKey + "_";

    Object.defineProperties(target.prototype, {
      [observableKey]: { value: new Subject<any>() },
      [propertyKey]: { 
        get() { return this[privateKey]; },
        set(v) { this[privateKey] = v; this[observableKey].next(v); }
      }
    });
  };
}
于 2018-07-29T17:32:36.050 回答