Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Activation/Deactivation with ObservableAsPropertyHelper

What is the proper usage of Activation/Deactivation in conjunction with ObservableAsPropertyHelper? Given a view and viewmodel that reflects long lived (hot) observables, the subscription would need to be disposed when the view and viewmodel is unloaded. However ObservableAsPropertyHelper, which is recommended to be readonly is assigned in the constructor of the viewmodel, and cannot be part of the activation/deactivation lifecycle. What is the right way to handle these kind of situations?

public interface ILongLivedObject
{
    IObservable<bool> Status { get; }
}


public class TestViewModel : ReactiveObject
{
    private readonly ObservableAsPropertyHelper<bool> _status;
    public bool Status => _status.Value;

    public TestViewModel(ILongLivedObject obj)
    {
         _status = obj.Status.ToProperty(this, vm => vm.Status); //how is the subscription disposed?
    }


}

This also gets me into a corner when trying to add commands that depends on this status. In my application, a common use case is to have some hardware that is on some specific status (e.g. IsOpen) and allow commands when it is true. Without knowing better, this is what I am trying to do:


public class TestViewModel : ReactiveObject
{
    private readonly ObservableAsPropertyHelper<bool> _status;
    public bool Status => _status.Value;

    public ReactiveCommand<Unit, Unit> DoStuff {get;}

    public TestViewModel(ILongLivedObject obj)
    {
         _status = obj.Status.ToProperty(this, vm => vm.Status); //how is the subscription disposed?
         DoStuff = ReactiveCommand.CreateFromTask(....., this.WhenAnyValue(this, x => x.Status);
    }


}

If I try to move the _status creation into this.WhenActivated, the app will crash as the command is trying to get the value of status before it is created. Am I supposed to (re)create the comand during activation? This seems wrong and pretty costly?

So far, it seems better to have a regular Status property with a protected setter and make a regular subscription in this.WhenActivated - but this is what the handbook tells to avoid for "readonly" properties.

like image 200
Dom Avatar asked Sep 01 '25 10:09

Dom


1 Answers

So one thing to be aware of in Reactive programming, disposing often means "unsubscribe".

You often don't need to unsubscribe since the garbage collector will take care of it for you, providing you create ObservableAsPropertyHelper (abbreviated as OAPH) only with observables generated from the current ViewModel.

In your case however, your observable/object, is related to a object outside the current ViewModel. The OAPH itself is a Disposable object.

So you can use ISupportsActivation (shortly going to have a replacement of IActivableViewModel) and pass your OAPH into it's Disposable property.

public class TestViewModel : ReactiveObject, ISupportsActivation
{
    private readonly ObservableAsPropertyHelper<bool> _status;
    public bool Status => _status.Value;
    public ViewModelActivator Activator { get; } = new ViewModelActivator();

    public TestViewModel(ILongLivedObject obj)
    {
         _status = obj.Status.ToProperty(this, vm => vm.Status);
         this.WhenActivated(disposables =>
         {
             disposables(_status);
         }
    }
}

The disposables parameter passed into the WhenActivated lambda is a Func that takes a IDisposable

In the view, make sure you derive off IActivatable (soon to be renamed IActivatableView) and use WhenActivated in the constructor of the view as well.

like image 93
Glenn Watson Avatar answered Sep 05 '25 16:09

Glenn Watson