2017-03-28 1 views
1

IdStreamの最新の値を取得し、それをコマンドExecuteのアクションに使用したいとします。RX:ObservableからReactiveCommandに最新の値を渡す方法

public IObservable<Option<Guid>> IdStream { get; } 

IdStream = documentStream.OfType<DocumentOpened().Select(x => x.Document.Id.Some()) 
.Merge(documentStream.OfType<DocumentClosed().Select(x => Option<Guid>.None())); 

var saveCommand = ReactiveCommand.Create(() => Save(id), CanExecute); 

私は答えhttps://stackoverflow.com/a/31168822/7779560を使用しようと、このような何か持っていた:

var saveCommand = ReactiveCommand.Create(() => { }, CanExecute); 
saveCommand.WithLatestFrom(IdStream, (_, id) => id) 
      .Subscribe(id => Save(id)); 

をそして、それは動作しますが、私は(この場合にはIsExecutingとThrownExceptionsコマンドの機能を使用することはできません彼らコマンド作成中にExecuteとして渡した空のアクションに対してのみ動作します)。

UPD:

実行順序:

  • documentStreamプロセスDocumentOpenedイベント(いくつかのID値を取得する - 私はそれをチェックする)を作成
  • コマンドを作成
    1. IdStream
    2. saveCommand実行

    どうすれば実現できますか?

    UPD 2:コマンド本体内のメソッド(例えば、SaveAsync)も待つ必要があります。

  • 答えて

    1

    これは機能しますか?リプレイは最新の値を保持します。コマンドが実行されると、最新の値を取得し、その後にTake(1)は1つの値しか必要としないので、その後にサブスクライブを解除し、それをSaveにプッシュします。あなたのコードの一部を置換して

    ここ

    は若干簡素化と拡張テストケースである

    [Test] 
        public void ExecuteUsingLastProducedValue() 
        { 
         Subject<string> producer = new Subject<string>(); 
         IObservable<bool> CanExecute = Observable.Return(true); 
         IObservable<string> IdStream = producer; 
         string SaveCalledWith = String.Empty; 
    
         Func<string, Task> SaveAsync = (id) => 
         { 
          SaveCalledWith = id; 
          return Task.Delay(0); 
         }; 
    
         // IdStream creating 
         var connectedIdStream = 
          IdStream 
          .Replay(1); 
    
         connectedIdStream 
          .Connect(); 
    
         //Command creating 
         var command = ReactiveCommand.CreateFromObservable(() => 
         { 
          return connectedIdStream 
           .Take(1) 
           .Do(async id => 
           { 
            await SaveAsync(id); 
           }); 
         } 
         , CanExecute); 
    
    
         //Alternate way 
         //Command creating 
         //var command = ReactiveCommand.CreateFromObservable(() => 
         //{ 
         // return connectedIdStream 
         //  .Take(1) 
         //  .SelectMany(id => SaveAsync(id).ToObservable()); 
         //} 
         //, CanExecute); 
    
    
         //documentStream processes DocumentOpened event (get some Id value - I checked it) 
         producer.OnNext("something random"); 
         producer.OnNext("working"); 
    
         //At this point Save still hasen't been called so just verifiyng it's still empty 
         Assert.AreEqual(String.Empty, SaveCalledWith); 
    
         //trigger execution of command 
         command.Execute(Unit.Default).Subscribe(); 
    
         //Verified Saved Called With is called 
         Assert.AreEqual(SaveCalledWith, "working"); 
        } 
    
    +0

    残念ながら、いいえ(私はDoと 'Subscribe'メソッドの組み合わせに' Take(1) 'の有無にかかわらず)しようとしました。私は説明を更新しました、おそらくもっと情報を与えるでしょう。 IdStreamは、 'Execute'が開始されたとき(それが明白でなかった場合)、必要な値をすでに持っています(待ちません)。 –

    +0

    大丈夫私は大きな例で編集し、代わりにリプレイを使用するように変更しました。また、構文とReactiveCommandが欠落していました。 Observableを渡すようにCreateFromObservableを使用したい場合 –

    +0

    ここにいくつかの参考文献があります。http://www.intro.com/jp/content/v1.0.10621.0/02_KeyTypes.html#ReplaySubject http://www.introtorx .com/content/v1.0.10621.0/14_HotAndColdObservables.html#PublishLast –

    0

    あなたはObservable.Sample

    [Fact] 
        public void ExecuteUsingLastProducedValue() 
        { 
         Subject<string> producer = new Subject<string>(); 
         IObservable<bool> CanExecute = Observable.Return(true); 
         IObservable<string> IdStream = producer; 
         string SaveCalledWith = String.Empty; 
    
         Func<string, Task> SaveAsync = (id) => 
         { 
          SaveCalledWith = id; 
          return Task.Delay(0); 
         }; 
    
         // IdStream creating 
         var connectedIdStream = 
          IdStream 
           .Replay(1); 
    
         connectedIdStream 
          .Connect(); 
    
         //Command creating 
         var command = ReactiveCommand.Create(() => { } , CanExecute); 
         connectedIdStream.Sample(command) 
             .Subscribe(id => SaveAsync(id)); 
    
         //documentStream processes DocumentOpened event (get some Id value - I checked it) 
         producer.OnNext("something random"); 
         producer.OnNext("working"); 
    
         //At this point Save still hasen't been called so just verifiyng it's still empty 
         SaveCalledWith.Should().Be(String.Empty); 
    
         //trigger execution of command 
         command.Execute(Unit.Default).Subscribe(); 
    
         //Verified Saved Called With is called 
         SaveCalledWith.Should().Be("working"); 
        } 
    

    を使用したい(私は手でそれを持っていたので、私はxUnitので書き直しました)私がお勧めするものによって。

    [Fact] 
        public void ExecuteUsingLastProducedValue() 
        { 
         var producer = new Subject<string>(); 
         var canExecute = Observable.Return(true); 
         var saveCalledWith = String.Empty; 
    
         void Save(string id) => saveCalledWith = id; 
    
         var rcommand = ReactiveCommand.Create(() => { } , canExecute); 
    
         // When cast to ICommand ReactiveCommand has a 
         // more convienient Execute method. No need 
         // to Subscribe. 
         var command = (ICommand) rcommand; 
    
    
         producer 
          .Sample(rcommand) 
          .Subscribe(Save); 
    
         //documentStream processes DocumentOpened event (get some Id value - I checked it) 
         producer.OnNext("something random"); 
         producer.OnNext("working"); 
    
         //At this point Save still hasen't been called so just verifiyng it's still empty 
         saveCalledWith.Should().Be(String.Empty); 
    
         //trigger execution of command 
         command.Execute(Unit.Default); 
    
         //Verified Saved Called With is called 
         saveCalledWith.Should().Be("working"); 
    
         command.Execute(Unit.Default); 
    
         saveCalledWith.Should().Be("working"); 
    
         producer.OnNext("cat"); 
         saveCalledWith.Should().Be("working"); 
         command.Execute(Unit.Default); 
         saveCalledWith.Should().Be("cat"); 
         producer.OnNext("dog"); 
         saveCalledWith.Should().Be("cat"); 
         command.Execute(Unit.Default); 
         saveCalledWith.Should().Be("dog"); 
        } 
    
    +0

    ReactiveCommandのIsExecuting 'と' ThrownExceptions'プロパティはこの実装では期待どおりに動作します。 – Tony

    0

    ストリームの最新の値をObservableAsPropertyHelper<>プロパティに格納し、コマンドで使用することができます。

    あなたのクラスレベルのプロパティは、次のようになります。

    public IObservable<Option<Guid>> IdStream { get; } 
    
    private ObservableAsPropertyHelper<Option<Guid>> _currentId; 
    public Option<Guid> CurrentId => _currentId.Value; 
    

    そして、あなたのコンストラクタは、このようなものまで配線します:あなたはCurrentIdプロパティのデフォルト値を提供する場合があります

    IdStream.ToProperty(this, x => x.CurrentId, out _currentId); 
    var saveCommand = ReactiveCommand.Create(() => Save(CurrentId), CanExecute); 
    

    を。 ToProperty()コールでそれを行うことができます。

    +0

    これは簡単な方法ですが、明示的な状態管理を避け、ObservableをReactiveCommandにフィードする簡単な方法はありますか? – Tony

    +0

    私が知っているわけではありません。それが作成されるまであなたはコマンドの 'IObservable'を与えられていないからです。あなたが必要とするのは 'IdStream'とボタンクリックイベントで' CombineLatest'です。 –

    関連する問題