2017-07-13 11 views
0

クリック数を動的にリッスンするボタンが2つあります。 listeningArray$は、リッスンする必要のあるボタン#の配列(ar)を送出します。誰かが私が聞いているこれらのボタンをクリックすると、クリックされたボタンをログに記録し、時間間隔から値をログに記録する必要があります。switchMap内の動的配列のCombineLatestは定期的にサブスクライブしています。

ar[1,2]から[1]になる場合、ボタン#2のクリックをリッスンする必要があります。したがって、DOMクリックイベントは2のために削除する必要があり、それは.finally()演算子をトリガーするはずです。しかし、1の場合は、登録したままにして、.finally()のコードを実行しないでください。何も登録が解除されていないためです。

const obj$ = {}; 

Rx.Observable.combineLatest(
    Rx.Observable.interval(2000), 
    listeningArray$ // Will randomly emit either [1] or [1,2] 
) 
.switchMap(([x, ar]) => { 
    const observables = []; 

    ar.forEach(n => { 
     let nEl = document.getElementById('el'+n); 

     obj$[n] = obj$[n] || Rx.Observable.fromEvent(nEl, 'click') 
      .map(()=>{ 
      console.log(' el' + n); 
      }) 
      .finally(() => { 
      console.log(' FINALLY_' + n); 
      }); 

     observables.push(obj$[n]); 
    }) 

    return Rx.Observable.combineLatest(...observables); 
}) 
.subscribe() 

は、しかし、間隔が値を発するたびは何が起こっているのか、DOMイベントすべて削除を取得し、すぐに再び追加されます、そして.finally演算子内のコードは12のために実行されます。

これは本当にイライラしています。私は何が欠けていますか?

それは複雑な状況のビットですので、私はこれを作成しました:FRPでhttps://jsfiddle.net/mfp22/xtca98vx/7/

+1

あなたが探している動作をより詳しく説明してください。 –

+0

@MarkvanStratenチップをありがとう。私は私の質問を再構成しました。うまくいけばそれはより明確です。 –

答えて

0

だ呼び出すことができますので、それは退会しなければならないときには、すべてのボタンの口述にTakeWhile()を追加することができ、私は実際には本当に近かったが、私はswitchMapのポイントを誤解。

switchMapは、新しい値が上から放出されるたびにそれが返す観測値からの退会を目的としています。このため、代わりに新しい要求を行う必要がある場合に古い保留中のHttp要求を取り消すために使用できます。

私が持っていた問題は、予想されることです。 switchMapは、現在のものに購読する前に、以前に返された観察可能なものから退会します。私が質問で説明したように、これは容認できませんでした。これが受け入れられないのは、私の実際のプロジェクトでは、fromEventのオブザーバブルがFirebase child_addedイベントを受信して​​いたため、このコールドオブザーバがサブスクライバを持たないことから1人のサブスクライバになったとき、Firebaseはその後、将来的に追加されたものも同様です。

私はしばらくの間mergeMapと遊んでいましたが、実際には以前に返された観察可能なものから手動で退会する必要がありました。

switchMapのプロセスがunsubscribe from old => subscribe to newのプロセスを実行していたので、常に加入者が存在するように、内部の観測可能物のサブスクライバを追加しました。私はtakeUntil(Observable.timer(0))を使って、加入者が蓄積せずにメモリリークを起こしていることを確認しました。

もっと良い解決策があるかもしれませんが、これが私が見つけた最良の解決策でした。

const obj$ = {}; 
 

 
Rx.Observable.combineLatest(
 
    Rx.Observable.interval(2000), 
 
    listeningArray$ // Will randomly emit either [1] or [1,2] 
 
) 
 
.switchMap(([x, ar]) => { 
 
    const observables = []; 
 

 
    ar.forEach(n => { 
 
    let nEl = document.getElementById('el'+n); 
 

 
    obj$[n] = obj$[n] || Rx.Observable.fromEvent(nEl, 'click') 
 
     .map(()=>{ 
 
     console.log(' el' + n); 
 
     }) 
 
     .finally(() => { 
 
     console.log(' FINALLY_' + n); 
 
     }) 
 
     .share(); 
 
    
 
    obj$[n].takeUntil(Rx.Observable.timer(0)) 
 
     .subscribe(); 
 
    observables.push(obj$[n]); 
 
    }) 
 

 
    return Rx.Observable.combineLatest(...observables); 
 
}) 
 
.subscribe()

私も.share()メソッドを追加する必要がありました。私はとにかくそれが必要になるだろう。私はこのパターンを使用して、Angularコンポーネントが必要とするデータを宣言し、他のコンポーネントが望むものを無視して、より良い分離を達成するようにしています。したがって、複数のコンポーネントが同じFirebaseオブザーバブルに登録することができますが、.share()オペレータはFirebaseからの各メッセージが1回だけ処理されるようにします(それぞれのアクションをReduxストアにディスパッチします)。

解決策:https://jsfiddle.net/mfp22/xtca98vx/8/

1

状態は不変です。このように、2番目の放出にマップすると、[1,2]を含む以前の観測可能なcombineLatestは、サブスクライブされなくなり、finallyオペレータが呼び出されます。 [1]

ボタンを1つだけ取り消したい場合は、DOMに状態を保存して(ボタンを追加)、filterを使用してボタンを無視することができます。 それともそれが自分のfinally()

+0

お返事ありがとうございます。私はswitchMapが何をすべきか誤解しました。あなたは私を正しい軌道に乗せる。私はすぐに私が思いついた簡単な解決策で答えるだろう。 –