2017-03-28 7 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 :

실행 순서 :

  1. IdStream
  2. documentStream 프로세스를 DocumentOpened 이벤트 (일부 ID 값을 얻을 - 나는 그것을 확인)을 생성
  3. 명령을 생성
  4. saveCommand 실행

어떻게하면됩니까?

UPD 2 : 명령 본문 내부의 메소드 (예 : SaveAsync)도 기다려야합니다.

답변

1

이 기능이 유용합니까? Replay는 최신 값을 유지합니다. 명령이 실행되면 가장 최근 값을 가져오고 Take (1)은 그 이후에 구독 취소를합니다. 하나의 값만 필요하기 때문에 구독을 푸시합니다. 코드의 일부가 교체와

다음

가 약간 단순화 및 확장 된 테스트 케이스입니다

[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

불행히도, 아니요 ('Take (1)'의 유무에 상관없이'Do'와'Subscribe' 메서드 조합을 모두 시도했습니다). 설명을 업데이트했습니다. 아마도 더 많은 정보를 제공 할 것입니다. IdStream은 Execute가 시작될 때 (기다리지 말아야 함) 필요한 값을 이미 가지고 있습니다 (명확하지 않은 경우). –

+0

괜찮아요. 더 큰 예제를 편집하고 대신 Replay를 사용하도록 변경했습니다. 또한 구문과 ReactiveCommand를 놓쳤습니다. Observable을 전달할 수 있도록 CreateFromObservable을 사용하려고합니다. –

+0

다음은 좋은 참고 자료입니다. http://www.introtorx.com/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"); 
    } 

을 사용하려면 (I 손에 그 있었기 때문에 나는 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; 

그리고이 같은 물건을 연결할 것 생성자 :

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

당신은 CurrentId 속성에 대한 기본값을 제공 할 수 있습니다. ToProperty() 전화에서이 작업을 수행 할 수 있습니다.

+0

그게 쉬운 방법이지만 명백한 상태 관리를 피하고 Observable을 ReactiveCommand에 공급하는 간단한 방법이 있습니까? – Tony

+0

내가 아는 바는 없다. 명령이 내려지기 전까지는 명령의'IObservable' 명령을받지 못하기 때문입니다. 필요한 것은'IdStream'과 버튼 클릭 이벤트로'CombineLatest'입니다. –