【问题标题】:switchMap Type void is not assignable to type ObservableInput<{}>switchMap 类型 void 不可分配给类型 ObservableInput<{}>
【发布时间】:2019-05-06 19:48:34
【问题描述】:

我正在使用 Angular http 和 RXJS 来检索一些应用程序设置。 http 使用 RXJS shareReplay 来避免多次相同的调用。

这按预期工作,检查“网络”选项卡我只能看到对我的后端 API 的 1 次调用。

我的应用程序中的某些组件将调用服务并使用结果来呈现页面。其他组件,需要像上面一样调用服务,然后再次对结果执行“某些操作”,例如更多的 http 调用,然后渲染页面。当试图做后者时,我得到类型错误:

Argument of type '(result: any) => void' is not assignable to parameter of type '(value: any, index: number)' => ObservableInput<{}>

在我的 switchMap 使用中。

-- 没有 switchMap 的简单示例 ----

cart.component.ts

ngOnInit() {
    this.systemPreferences.getPreferences()
        .subscribe(
            (result: any) => {
                this.headerTitle = result.title || '';
            },
            (err) => {
                console.log('there has been an error');
            },
            () => // Some completion code
        );
}

systemPreferences.service.ts:

const CACHE_SIZE = 1;

export class SystemPreferences {
    private $cache: Observable<Object>

    private requestConfig() {
         return this.http.get("/api/some/endpoint").pipe(
             map(response => response.value)
         );
    }

    public getPreferences() {
        if (!this.cache$) {
            this.cache$ = this.requestConfig().pipe(
             shareReplay(CACHE_SIZE)
            );
        }

        return this.cache$;
    }
}

/api/some/endpoint 示例响应:

{
    "appName": "Tables Forks and Spoons",
    "title": "My title",
    "regionUrls": [
        "http://www.region1.com:8021",
        "http://www.region2.com:8021",
        "http://www.region3.com:8021",
        "http://www.region4.com:8021"
    ],
    "nonRegionUrls": [
        "http://www.non-region1.com:8021",
        "http://www.non-region2.com:8021",
        "http://www.non-region3.com:8021",
        "http://www.non-region4.com:8021"
    ]
}

-- 使用 switchMap 的类型错误简单示例 ----

cart.component.ts

ngOnInit() {
    this.systemPreferences.getPreferences()
        .pipe(
            .switchMap((result: any) => {

               this.systemPreferences.getLiveUrl(result['regionUrls']);

            }))
        .subscribe(
            (result: any) => {
                this.liveLink = result.liveLink; // result.livelink is added as part of this.systemPreferences.getLiveUrl above
                this.headerTitle = result.title || '';
            },
            (err) => {
                console.log('there has been an error');
            }
        );
}

systemPreferences.service.ts:

const CACHE_SIZE = 1;

export class SystemPreferences {
    private $cache: Observable<Object>

    private requestConfig() {
         return this.http.get("/api/some/endpoint").pipe(
             map(response => response.value)
         );
    }

    public getPreferences() {
        if (!this.cache$) {
            this.cache$ = this.requestConfig().pipe(
             shareReplay(CACHE_SIZE)
            );
        }

        return this.cache$;
    }

    public getLiveUrl(urls: any) {
        let promises = [];

        urls.forEach((url) => {
           promises.push(this.http.get(url + 'some/endpoint/')); 
        });

        const results = forkJoin(promises) {
            .subscribe((data: any) => {
                return this.getBest(data);
            }
        }
    }

    public getBest(data: any) {
        // Loops through data, and returns a single URL as a string
        // 'http//www.thebesturltouse.com'
    }
}

更新:

尝试按照建议返回第二个 observable,见下文,与上面的类型 void 错误相同。

cart.component.ts

ngOnInit() {
    this.systemPreferences.getPreferences()
        .pipe(
            .switchMap((result: any) => {
               return this.systemPreferences.getLiveUrl(result['regionUrls']);
            }))
        .subscribe(
            (result: any) => {
                this.liveLink = result.liveLink; // result.livelink is added as part of this.systemPreferences.getLiveUrl above
                this.headerTitle = result.title || '';
            },
            (err) => {
                console.log('there has been an error');
            }
        );
}

systemPreferences.service.ts:

const CACHE_SIZE = 1;

export class SystemPreferences {
    private $cache: Observable<Object>
    private $switchObs: Observable<Object> 

    private requestConfig() {
         return this.http.get("/api/some/endpoint").pipe(
             map(response => response.value)
         );
    }

    public getPreferences() {
        if (!this.cache$) {
            this.cache$ = this.requestConfig().pipe(
             shareReplay(CACHE_SIZE)
            );
        }

        return this.cache$;
    }

    public getLiveUrl(urls: any) {
        let promises = [];

        urls.forEach((url) => {
           promises.push(this.http.get(url + 'some/endpoint/')); 
        });

        const results = forkJoin(promises) {
            .subscribe((data: any) => {
                this.$switchObs = this.getBest(data);
                return this.$switchObs;
            }
        }
    }

    public getBest(data: any) {
        // Loops through data, and returns a single URL as a string
        // 'http//www.thebesturltouse.com'
    }
}

更新到多个 switchMaps

cart.component.ts

ngOnInit() {
    this.systemPreferences.getPreferences()
        .pipe(
            .switchMap((result: any) => {
               return this.systemPreferences.getLiveUrl(result['regionUrls']);
            }),
            .switchMap((liveRegion: any) => {
               return this.systemPreferences.getBest(liveRegion);
            }))
        .subscribe(
            (bestUrl: String) => {
                console.log('best');
                console.log(bestUrl);
            },
            (err) => {
                console.log('there has been an error');
            }
        );
}

systemPreferences.service.ts:

const CACHE_SIZE = 1;

export class SystemPreferences {
    private $cache: Observable<Object>
    private $switchObs: Observable<String> 

    private requestConfig() {
         return this.http.get("/api/some/endpoint").pipe(
             map(response => response.value)
         );
    }

    public getPreferences() {
        if (!this.cache$) {
            this.cache$ = this.requestConfig().pipe(
             shareReplay(CACHE_SIZE)
            );
        }

        return this.cache$;
    }

    public getLiveUrl(urls: any) {
        let promises = [];

        urls.forEach((url) => {
           promises.push(this.http.get(url + 'some/endpoint/')); 
        });

        return forkJoin(promises);
    }

    public getBest(data: any) {
        // Loops through data, and returns a single URL as a string
        // 
        return this.$switchObs; // = 'http//www.thebesturltouse.com'
    }
}

有了以上内容,在console.log('best')/console.log(bestUrl)上,我看到了:

'best'
h
'best'
t
'best'
t
'best'
p
'best'
:
'best'
/
'best'
/
'best'
w
'best'
w
'best'
w
'best'
.
'best'
b
etc etc etc

而不仅仅是字符串 http//www.thebesturltouse.com


更新。

在多 switchMap 示例中,如果我返回 Obserbable.of(this.$switchObs);我得到了预期的单字符串 URL。

感觉这不是解决这个问题的最佳方法。

【问题讨论】:

  • 随便看一眼,需要从switchMap内返回第二个observable
  • @xyz - 抱歉,第二个可观察到的?还是“第二个”可观察的?
  • 'the' 第二个可观察的。在switchMap() 中返回,例如:return this.systemPreferences.getLiveUrl(result['regionUrls']);
  • @xyz - 请参阅更新部分,尝试您的建议,同样的问题/错误。查看新的私有 $switchObs: Observable too
  • getLiveUrl()返回results

标签: angular rxjs


【解决方案1】:

问题与switchMap 运算符本身无关。而是您的getLiveUrlmethod 是void 类型。 switchMap 需要一个返回可观察对象作为输入参数的函数。所以你希望 getLiveUrl 返回一个 observable。为了实现这一点,代码可能看起来像这样(请注意,我不是 100% 确定 getBest 实际在做什么,这可能会影响解决方案。):

 public getLiveUrl(urls: any) {
        let promises = [];

        urls.forEach((url) => {
           promises.push(this.http.get(url + 'some/endpoint/')); 
        });

        return forkJoin(promises).pipe(
           map(data => this.getBest(data))
        );
    }

我仍然不能 100% 确定您实际想要达到的目标,但我希望这对您有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    • 1970-01-01
    • 2019-01-04
    • 2021-12-05
    相关资源
    最近更新 更多