2015-12-04 35 views
2

我一直在尝试创建一些cycle.js示例作为嵌套对话框,并使用选择框在它们之间切换。Cycle.js添加加载指示器后HTTP发送多个请求

其中一个对话框是官方Github HTTP搜索示例的克隆。

其他对话更基本的一个没有HTTP,只有DOM。

我觉得自己像是围绕在2之间进行切换,但是我对Rx很新,所以可能会做得不正确或者天真。

这一切似乎运作良好,直到我添加一个加载指标到搜索页面。

为了做到这一点,我把这个:

const vTree$ = responses.HTTP 
    .filter(res$ => res$.request.indexOf(GITHUB_SEARCH_API) === 0) 
    .flatMapLatest(x => x) 
    .map(res => res.body.items) 
    .startWith([]) 
    .map(results => 
     h('div.wrapper', {}, [ 
     h('label.label', {}, 'Search:'), 
     h('input.field', {type: 'text'}), 
     h('hr'), 
     h('section.search-results', {}, results.map(resultView)), 
     ]) 
    ) 

进入这个:

const searchResponse$ = responses.HTTP 
    .filter(res$ => res$.request.indexOf(GITHUB_SEARCH_API) === 0) 
    .flatMapLatest(x => x) 
    .map(res => res.body.items) 
    .startWith([]) 

    const loading$ = searchRequest$.map(true).merge(searchResponse$.map(false)) 

    // Convert the stream of HTTP responses to virtual DOM elements. 
    const vtree$ = loading$.withLatestFrom(searchResponse$, (loading, results) => 
     h('div.wrapper', {}, [ 
     h('label.label', {}, 'Search:'), 
     h('input.field', {type: 'text'}), 
     h('span', {}, loading ? 'Loading...' : 'Done'), 
     h('hr'), 
     h('section.search-results', {}, results.map(resultView)), 
     ]) 
    ) 

我现在有2个问题

  1. 和 '设置为复选框值'对于每次更改复选框,都会记录 两次的“路由已更改”消息。
  2. 仅HTTP请求日志 会触发一次,但如果您在Dev Tools 中观看网络活动,您将同时看到两个GET请求。

感谢您的帮助!

编辑:解决了我自己的问题。见下面的答案。

+0

您正在使用'loading $ .withLatestFrom()',所以当'loading $'有一个新值时它只会产生一个新值。试试'combineLatest()'。 –

+0

我最初创建了一个中间可观察的'state $ = Rx.Observable.combineLatest(searchResponse $,loading $, (resp,loading)=> {return {results:resp,loading:loading}})''。它的工作原理完全一样,因为每次响应都会加载$更改。加载指示器工作得很好,问题是我的HTTP请求触发了2次而不是1次。如果删除加载指示器代码,请求像他们应该一样射击。我觉得它与我如何实现嵌套对话有关。 – SkaterDad

回答

5

我最终通过从头开始重新构建整个应用程序直到找到突破点来解决此问题。

我学到的是,您需要将.share()添加到任何可观察的流,这些流将由多个下游可观察对象进行订阅/映射/等。

const searchRequest$ = DOM.select('.field').events('input') 
    .debounce(500) 
    .map(ev => ev.target.value.trim()) 
    .filter(query => query.length > 0) 
    .map(q => GITHUB_SEARCH_API + encodeURI(q)) 
    .share() //needed because multiple observables will subscribe 

    // Get search results from HTTP response. 
    const searchResponse$ = HTTP 
    .filter(res$ => res$ && res$.request.url.indexOf(GITHUB_SEARCH_API) === 0) 
    .flatMapLatest(x => x) //Needed because HTTP gives an Observable when you map it 
    .map(res => res.body.items) 
    .startWith([]) 
    .share() //needed because multiple observables will subscribe 

    //loading indication. true if request is newer than response 
    const loading$ = searchRequest$.map(true).merge(searchResponse$.map(false)) 
    .startWith(false) 
    .share() 

    //Combined state observable which triggers view updates 
    const state$ = Rx.Observable.combineLatest(searchResponse$, loading$, 
    (res, loading) => { 
     return {results: res, loading: loading} 
    }) 

    //Generate HTML from the current state 
    const vtree$ = state$ 
    .map(({results, loading}) => 
     ......... 
+1

正确,那就是“分享”的意思。他们被称为* hot * observables。在这里阅读更多信息https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables –