将 属性 公开为可观察对象的最简单语法?

Simplest syntax for exposing a property as an observable?

我在 Angular 中看过教程,展示了无数种实现可观察对象的不同方法。就我的目的而言,其中许多似乎过于复杂。其他的适用于以前的版本,不再有效。

假设我有一个名为 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,什么是最简单,最直接,最易读的方式来暴露chickens属性在ChickenService这样的组件 class 可以访问 属性 作为可观察流的值?或者组件模板可以使用异步管道显示值?

我怎么强调都不过分 - 拜托,没有包含 8,192 个字符的闭包墙然后说 "see, it's simple".

的答案

我问这个问题不仅是为了我自己,也是为了像我这样的其他人,他们正试图围绕可观察的事物进行思考,并在所有关于该主题的密集和过时的教程中苦苦挣扎。如果你能把这个解决方案简化为一个简单的形式,后代会感谢你。

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

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

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

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

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

您可以使用主题来更新值并通过订阅流在 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);
  }
}

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

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

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

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

}

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

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