简体   繁体   English

用forkJoin进行条件可观察

[英]Conditional observables with forkJoin

I have a case where I may or may not need to add observables to a list. 在某些情况下,我可能需要或不需要将可观察值添加到列表中。 I then want to forkJoin the observables I do have so the page can load once all of the data is available. 然后,我想forkJoin加入我所拥有的可观察对象,以便在所有数据可用forkJoin可以加载页面。

let observables: Observable<any>[] = [];

observables.push(this.taskService.getStep(this.housingTransactionId, this.task.stageReferenceId, this.task.stepReferenceId));

if (this.task.associatedChatThreadId) {
    observables.push(this.messageHubService.getChatThread(this.housingTransactionId, this.task.associatedChatThreadId));
}

if (this.task.associatedDocuments && this.task.associatedDocuments.length > 0) {
    this.task.associatedDocuments.forEach(documentId => {
        observables.push(this.documentHubService.getDocumentProperties(this.housingTransactionId, documentId));
    });
}

Observable.forkJoin(observables)
    .subscribe(([step, chatThread, ...documents]) => {

        this.step = step;           
        this.chatThread = chatThread;           
        this.documents = documents;

        this.isPageLoading = false;

    }, error => {
        this.isPageLoading = false;
        console.log(error);
    });

The problem I'm getting is that if I don't have a this.task.associatedChatThreadId , then the observable is not added to the list and when the forkJoin is executed, the ...documents are in the position of the chatThread property in the subscribe method (well, the first document!). 我得到的问题是,如果我没有this.task.associatedChatThreadId ,那么可观察对象将不会添加到列表中,并且在执行forkJoin时, ...documents位于chatThread属性的位置在subscription方法中(嗯,第一个文档!)。

Is there a way to ensure the positioning of the responses from a forkJoin ? 有没有办法确保forkJoin响应的位置? Or should I/can I use a different approach? 还是我/我可以使用其他方法?

Most easily you can add a dumb Observable.of(null) with null value if the condition is not met in order to keep the same order of responses: 如果不满足条件,可以很容易地添加一个null值哑的Observable.of(null) ,以保持相同的响应顺序:

if (this.task.associatedChatThreadId) {
    observables.push(this.messageHubService....);
} else {
    observables.push(Observable.of(null))
}

Then in the subscription you can check if chatThread === null becauese it'll always be present at the same position. 然后在订阅中,您可以检查chatThread === null因为它将始终出现在同一位置。

Alternatively, you could wrap each Observable in observables with some extra object that would make it uniquely identifiable in the subscriber but that would be unnecessarily complicated so I'd personally stick to the first option. 或者,你可以换每个可测量在observables一些额外的对象,将使它唯一可识别的用户,但会被不必要地复杂,所以我个人坚持到第一个选项。

Another approach would be not to use folkJoin but subscribe separately. 另一种方法是不使用folkJoin,而是单独订阅。 At the same time, make isPageLoading a BehaviorSubject which counts how many async requests you currently have. 同时,使isPageLoading一个BehaviorSubject计数您当前有多少个异步请求。 Each time when you make a request, you can have isPageLoading.next(1), and isPageLoading.next(-1) when you finish a request. 每次发出请求时,完成请求时都可以具有isPageLoading.next(1)和isPageLoading.next(-1)。

You could make a helper function that accepts an object which has string keys and observable values and returns an observable that will emit an object with the same keys, but having the resulting values instead of the observables as values. 您可以创建一个辅助函数,该函数接受具有字符串键和可观察值的对象,并返回一个可观察对象,该对象将发射具有相同键的对象,但具有结果值而不是可观察值作为值。

I would not really say that this is a cleaner version than using of(null) like suggested by martin, but it might be an alternative. 我不会真的说这是比使用Martin建议的of(null)更干净的版本,但它可能是替代方法。

function namedForkJoin(map: {[key: string]: Observable<any>}): Observable<{[key: string]: any}> {
    // Get object keys
    const keys = Object.keys(map);

    // If our observable map is empty, we want to return an empty object
    if (keys.length === 0) {
        return of({});
    }


    // Create a fork join operation out of the available observables
    const forkJoin$ = Observable.forkJoin(...keys.map(key => map[key]))

    return forkJoin$
        .map(array => {
            const result = {};
            for (let index = 0; index < keys.length; index++) {
                result[keys[index]] = array[index];
            }

        }));
}

Please keep in mind, I did not have angular or rxjs running here at the moment, so I could not verify the function really works. 请记住,我目前没有在此处运行angular或rxjs,因此无法验证该功能是否确实有效。 But the idea is: 1. Get the keys from the input map. 但想法是:1.从输入映射中获取键。 2. Use the keys to get an array of observables and pass that to fork join. 2.使用键获取可观察对象数组,并将其传递给派生联接。 3. Add a mapping function that converts the resulting array back into an object. 3.添加一个映射函数,将结果数组转换回一个对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM