如何在外部事件中向每个观察者推送不同的数据?

时间:2018-03-22 11:46:22

标签: angular rxjs

我的服务充当数据存储(简单关联数组)。有多个组件对这些数据感兴趣 - 每个组件都需要一个或多个键下的值,但他们从不想要整个事物。存储服务还会侦听外部事件,并通过重新计算数组中的所有值来对其做出反应。我正在尝试使用rxjs Observables实现整个事情。

我读到Observables为每个观察者分别执行他们的subsicribe()函数。这听起来就像我需要的那样,但我无法弄清楚:

  1. 观察者如何在订阅期间告诉Observable它只对所选项目感兴趣?
  2. 如何在重新计算数据时更新所有观察者(发生外部事件)?在下面的示例中,我在Subject上使用next(),但这会通过所有Observable发送相同的数据。
  3. 以下是我现在所拥有的简化版本:

      

    storage.service.ts

    import { Injectable } from '@angular/core';
    import { Observable, Subject } from 'rxjs/Rx';
    
    @Injectable()
    export class StorageService {
        externalEvent = Observable.interval(2500);
        data = {};
    
        output = new Subject();
    
        constructor() { 
            this.externalEvent.subscribe(n => {
                this.data = {"a": Math.random(), "b": Math.random(), "c": Math.random()};
                this.output.next(this.data);
            });
        }
    
    }
    
      

    app.component.ts

    import { Component } from '@angular/core';
    import { StorageService } from './storage.service';
    
    @Component({
    selector: 'app-root',
    template: `
        <app-child [param]="'a'"></app-child>
        <app-child [param]="'b'"></app-child>
    `,
    providers: [StorageService]
    })
    export class AppComponent { }
    
      

    child.component.ts

    import { Component, OnInit, Input } from '@angular/core';
    import { StorageService } from './storage.service';
    
    @Component({
    selector: 'app-child',
    template: `
        <p>{{ data }}</p>
    `
    })
    export class ChildComponent implements OnInit {
    
        @Input() param: string;
        data: string;
    
        constructor(private storage: StorageService) {
            this.storage.output.subscribe(d => {
                console.log(`child ${this.param} received ${JSON.stringify(d)}`);
                this.data = d[this.param];
            });
        }
        ngOnInit() {
        }
    
    }
    

2 个答案:

答案 0 :(得分:0)

如果我理解正确,您可以将output转换为接受您想要观察的“密钥”的方法。

output(key: string): Observable<any> {
  return Observable.of(this.data[key])
    .merge(this.output.map(newData => newData[key]))
    .filter(Boolean) // Emit only when there's any data?
}

这种方式当您使用新数据调用this.output.next()时(即使它仅包含已更改的键),它也会通过新数据通知所有观察者。

答案 1 :(得分:0)

如果您想获取服务数据,我会这样做:

  

storage.service.ts

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

@Injectable()
export class StorageService {
    externalEvent = Observable.interval(2500);
    data = {};

     private _outputEvent$: Subject<any> = new Subject<any>();

    constructor() { 
        this.externalEvent.subscribe(n => {
            this.data = {"a": Math.random(), "b": Math.random(), "c": Math.random()};
            this.setOutputEvent(this.data);
        });
    }

   /**
   * Transform OutputEvent subject to an observable.
   */
  getOutputEvent() {
    return this._outputEvent$.asObservable();
  }

  /**
   * Sends data through OutputEvent subject.
   * @param value data to output.
   */
  setOutputEvent(value: any) {
    this._outputEvent$.next(value);
  }

}
  

app.component.ts

import { Component } from '@angular/core';
import { StorageService } from './storage.service';

@Component({
selector: 'app-root',
template: `
    <app-child [param]="'a'"></app-child>
    <app-child [param]="'b'"></app-child>
`,
providers: [StorageService]
})
export class AppComponent { }
  

child.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { StorageService } from './storage.service';

@Component({
selector: 'app-child',
template: `
    <p>{{ data }}</p>
`
})
export class ChildComponent implements OnInit {

    @Input() param: string;
    data: string;

    constructor(private storage: StorageService) {
        this.storage.getOutputEvent().subscribe(
            response => {
               console.log(`child ${this.param} received ${JSON.stringify(response)}`);
               // this.data = response[this.param];
               this.data = response.param;
            },
            error => {
                console.log(error);
            }
        );
    }
    ngOnInit() {
    }

}