Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: When observing child property in a two-way fashion from ViewModel to Model, when the parent updates, the "old" parent gets updated with the new parent value #3324

Open
Tetedeiench opened this issue Jul 27, 2022 · 1 comment
Labels

Comments

@Tetedeiench
Copy link

Describe the bug 🐞

This is tough to describe, I've added a repository to reproduce it. This issue is following a discussion on slack where this has been found almost certainly as a bug.

It happens on both Avalonia and bare WPF ( Thanks to @tomasfil )

When a viewmodel "binds" using WhenAnyValue to a child property of a model object, and the model object is replaced by a new one, the "old" model objects get set by a new object.

It is as if the "source" value of WhenAnyValue was cached, and when the object updates, it isn't reflected.

Here's an example in a repro repository : https://github.com/Tetedeiench/reactiveUiLiveModelEditIssue

public class ScheduleViewModel : ViewModelBase
{
    private SchedulePeriod _period;

    public SchedulePeriod Period
    {
        get => _period;
        set => this.RaiseAndSetIfChanged(ref _period, value);
    }
    
    private bool _isInfinite;

    public bool IsInfinite
    {
        get => _isInfinite;
        set => this.RaiseAndSetIfChanged(ref _isInfinite, value);
    }

    public ReactiveCommand<SchedulePeriod, Unit> SelectPeriodCommand { get; }
(...)
    public ScheduleViewModel()
    {
        this.WhenAnyValue(x => x.Period.IsInfinite)
            .DistinctUntilChanged()
            .ObserveOn(RxApp.MainThreadScheduler)
            .BindTo(this, x => x.IsInfinite);
        
        //viewmodel to model
        this.WhenAnyValue(x => x.IsInfinite)
            .Skip(1)
            .DistinctUntilChanged()
            .ObserveOn(RxApp.MainThreadScheduler)
            .BindTo(this, x => x.Period.IsInfinite);
(...)
    }
}

Let's imagine, in the current state, the IsInfinite bool is true.

The user now invokes the SelectPeriodCommand through a button. It changes the selected period in the viewmodel, thus the "Period" property is mutated.

The new period being set has "IsInfinite" set to false by default.

This triggers the two WhenAnyValue pipeline.

It seems that the second pipeline fires, and updates the previous model object, and set its IsInfinite property to false, instead of binding to the new object, as expected.

It is as if the "Period" value was cached and not properly updated everywhere.

See the screenshot section for a gif showing the visual representation of what happens.

Step to reproduce

  1. Go to https://github.com/Tetedeiench/reactiveUiLiveModelEditIssue , download the repo, build & run it
  2. Click on the control on the right hand side to change the current period properties
  3. Click on the button on the left hand side to change the selected period
  4. See the initial period properties being overwritten by the new period properties

Reproduction repository

https://github.com/Tetedeiench/reactiveUiLiveModelEditIssue

Expected behavior

The "old" Period object should not be mutated, and x.Period should resolve to the most up-to-date value to prevent this behavior

Screenshots 🖼️

LiveModelEdit_KAQ5qCQsQl

IDE

Visual Studio 2022, Rider Windows

Operating system

Windows

Version

11 and others probably

Device

PC

ReactiveUI Version

18.3.1

Additional information ℹ️

No response

@tomasfil
Copy link
Contributor

I can add the way how it feels short as possible:
".WhenAnyValue gets triggered for the new object, but the .BindTo binds into the old object"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants