Clint
Clint

Reputation: 6220

Observable still subscribed after SelectMany

I have the standard reactive-ui routing set up for a WPF app, and I have an interface that ViewModel's can implement to provide title information.

public interface IHaveTitle
{
    IObservable<string> Title { get; }
}

In one view model I'm doing the following (for demo purposes):

public IObservable<string> Title => Observable.Interval(TimeSpan.FromSeconds(5)).Select(_ => DateTime.Now.ToLongTimeString());

In my main window screen, I'm doing the following:

disposer(
    ViewModel.Router.CurrentViewModel
    .SelectMany(vm =>
        ((vm as IHaveTitle)?.Title.StartWith("") ?? 
            Observable.Return("")).Select(s => string.IsNullOrEmpty(s) ? vm.UrlPathSegment : $"{vm.UrlPathSegment} > {s}"))
    .ObserveOn(RxApp.MainThreadScheduler)
    .BindTo(this, w => w.Title));

Where disposer is the Action<IDisposable> passed into the this.WhenActivated extension method.

Now, when I navigate around, the title does change to reflect the UrlPathSegment, and while on the primary view model the title updates to show the time every 5 seconds.

The problem I'm seeing however, is that even when I navigate to a different view model, the title observable on the primary view model is still leading to changes on the title.

My question really, is: How do I prevent this? Why isn't it detaching when I navigate away, given I'm selecting based on the CurrentViewModel?

Upvotes: 1

Views: 270

Answers (1)

Kent Boogaart
Kent Boogaart

Reputation: 178630

The problem is the use of SelectMany. You're saying "every time the CurrentViewModel changes, subscribe to this other observable". Since those observables never complete, they remain "active" forever.

You instead want to switch to the new observable:

disposer(
    ViewModel.Router.CurrentViewModel
    .Select(vm =>
        ((vm as IHaveTitle)?.Title.StartWith("") ?? 
            Observable.Return("")).Select(s => string.IsNullOrEmpty(s) ? vm.UrlPathSegment : $"{vm.UrlPathSegment} > {s}"))
    .Switch()
    .ObserveOn(RxApp.MainThreadScheduler)
    .BindTo(this, w => w.Title));

Upvotes: 6

Related Questions