简体   繁体   English

将可观察的链链接到一个订阅

[英]Chain joined observables to one subscription

I've got a model class called 'Item' which needs two seperate images to be loaded from the backend (a sprite-texture and a shadow-texture to be precise). 我有一个名为“ Item”的模型类,该类需要从后端加载两个单独的图像(准确地说是sprite纹理和shadow纹理)。 I achieved a parallel loading and eventual join of the textures into my Item so that I could simply subscribe to it in another place. 我实现了并行加载并最终将纹理连接到我的Item中,因此我可以在另一个地方简单地订阅它。

Now, I've had this working when I used Base64 to transfer the files to my Angular2 Application. 现在,当我使用Base64将文件传输到我的Angular2应用程序时,我已经完成了这项工作。 But now I want to work with the plain blobs. 但是现在我想使用普通Blob。 Although this is not yet supported by Angular2, it is totally possible to read the private _body property of the response. 尽管Angular2尚不支持此功能,但完全有可能读取响应的private _body属性。 The thing is that my model class should retrieve the images as HTMLImageElements with the data parsed as a Base64 data url. 关键是我的模型类应该将图像检索为HTMLImageElements ,并将数据解析为Base64数据url。 To generate this data url from my blobs I need to use FileReader.readAsDataUrl() which works based on a callback. 要从我的Blob生成此数据url,我需要使用FileReader.readAsDataUrl() ,它基于回调工作。 I think I've figured out how to wrap this callback into an Observable (correct me if this approach is wrong, as I can't confirm it at this point). 我想我已经弄清楚了如何将该回调包装到一个Observable中(如果这种方法不正确,请纠正我,因为我目前无法确认)。

So, now I simply can not figure out how to correctly chain my calls to be able to subscribe to a resulting Observable which then produce my Item, like this ItemService.getItem(1).subscribe(item => ...) 因此,现在我根本无法弄清楚如何正确地链接我的调用以能够订阅生成的Observable,然后生成我的Item,例如ItemService.getItem(1).subscribe(item => ...)

The current setup gives me a weird error stating that the subscribe-method is not defined on the resulting Observable. 当前设置给我一个奇怪的错误,指出未在生成的Observable上定义订阅方法。 Im pretty new to RxJS and would be very glad if someone could show me how to set this up properly :) 我对RxJS很陌生,如果有人可以告诉我如何正确设置它,我将非常高兴:)

/* implementation in ItemService */

getItem(id:number):Observable<Item> {
    return Observable.forkJoin( // join both texture fetches
        this.http.get('/api/items/' + id + '/sprite'),  // fetch sprite texture
        this.http.get('/api/items/' + id + '/shadow'),  // fetch shadow texture
        (spriteResponse, shadowResponse) => {
            // transform responses into proper blobs
            const spriteBlob = new Blob([spriteResponse._body], {type: 'image/png'})
            const shadowBlob = new Blob([shadowResponse._body], {type: 'image/png'})
            return [spriteBlob, shadowBlob]
        })
        .flatMap(result => Observable.forkJoin( // chain with joined image generation (pretty sure this is wrong somehow)
            ItemService.generateImage(result[0]), // parse spriteBlob to image
            ItemService.generateImage(result[1]), // parse shadowBlob to image
            (spriteImage, shadowImage) => {
                // assemble model from images
                const item = new Item()
                item.setSprite(spriteImage)
                item.setShadow(shadowImage)
                return item
            })
        )
}

static generateImage(data:Blob):Observable<HTMLImageElement> {
    const img = new Image() // create new HTML Image
    const reader = new FileReader() // init file reader
    reader.readAsDataURL(data)  // transform blob to base64 data url

    // create observable from callback (is this correct?)
    return Observable.bindCallback(reader.onloadend, () => {
        img.src = reader.result
        return img
    })
}

The problem is two-fold. 问题是双重的。 First is that bindCallback returns a function which returns an Observable when invoked it does not return an Observable . 首先是bindCallback返回一个函数 ,该函数在调用时返回Observable ,但不返回Observable The idea of binding a callback is that you are converting a function that normally reports its async result through a callback into an Observable . 绑定回调的想法是,您正在将通常通过回调报告其异步结果的函数转换为Observable

In this case you are actually waiting on an event to be fired ( loadend ), so you likely want to use fromEvent instead. 在这种情况下,您实际上正在等待要触发的事件( loadend ),因此您可能想使用fromEvent

static generateImage(data:Blob):Observable<HTMLImageElement> {
    const reader = new FileReader() // init file reader
    reader.readAsDataURL(data)  // transform blob to base64 data url

    // create observable from callback (is this correct?)
    return Observable.fromEvent(reader, 'load', (e) => {
       var img = new Image();
       img.src = reader.result;
       return img;
    }).first();
}

Now this isn't terribly robust but should get the job done. 现在,这并不是十分强大,但是应该可以完成工作。 If you want to see a more complete example there is a fromReader method that was written for RxJS4 that you could commandeer for your purposes (the version for RxJ5 hasn't been implemented yet). 如果要查看更完整的示例,可以为fromReader编写一个fromReader方法,您可以根据自己的目的进行命令(RxJ5的版本尚未实现)。

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

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