patterncsharpModerate
A ViewModel using ReactiveUI 6 that loads and sends data
Viewed 0 times
thatusingloadsviewmodelanddatareactiveuisends
Problem
Most of the Rx Compelling Examples™ load and store data locally, which I don't find that Compelling.
I spent some time coming up with how to best perform the following actions but want to make sure I am not Doing It Wrong™.
Here are the cases I am trying to solve:
Some items I am trying to avoid:
```
public class CommonViewModel : ReactiveObject, IRoutableViewModel
{
private readonly RemoteHttpService _remoteClient;
private ReactiveList _items;
public ReactiveList ItemsFromServer
{
get { return _items; }
set { this.RaiseAndSetIfChanged(ref _items, value); }
}
public ReactiveCommand> LoadItems{ get; protected set; }
public ReactiveCommand SendItemsToServerCommand { get; protected set; }
public string UrlPathSegment
{
get { return "CommonViewModel View"; }
}
public IScreen HostScreen { get; private set; }
public CommonViewModel(IScreen screen)
{
HostScreen = screen;
ItemsFromServer = new ReactiveList();
_remoteClient = new RemoteHttpService(); //using refit and polly behind the scenes.
SendItemsToServerCommand = ReactiveCommand.CreateAsyncTask((_, ctx) => InternalSendItemsToServer());
SendItemsToServerCommand.ThrownExceptions.Subscribe(ex => UserError.Throw("Could not send data to server", ex));
LoadItems = ReactiveCommand.CreateAsyncObservable(_ => this.InternalGetItemsFromCacheAndServer());
LoadItems.Subscribe(list =>
{
_items.Clear();
foreach (var item in list){
//using AddRange throw Presentation
I spent some time coming up with how to best perform the following actions but want to make sure I am not Doing It Wrong™.
Here are the cases I am trying to solve:
- Load data from cache and remote server when view model is instantiated
- Save data to server when UI interaction happens (some button press)
- Handle error cases (network down)
Some items I am trying to avoid:
- Errors being swallowed (async in ctor for example)
- Incorrect use of Rx patterns (is there a more 'correct' way)
- Loss of data either through out of sync lists, not using cache correct, other.
```
public class CommonViewModel : ReactiveObject, IRoutableViewModel
{
private readonly RemoteHttpService _remoteClient;
private ReactiveList _items;
public ReactiveList ItemsFromServer
{
get { return _items; }
set { this.RaiseAndSetIfChanged(ref _items, value); }
}
public ReactiveCommand> LoadItems{ get; protected set; }
public ReactiveCommand SendItemsToServerCommand { get; protected set; }
public string UrlPathSegment
{
get { return "CommonViewModel View"; }
}
public IScreen HostScreen { get; private set; }
public CommonViewModel(IScreen screen)
{
HostScreen = screen;
ItemsFromServer = new ReactiveList();
_remoteClient = new RemoteHttpService(); //using refit and polly behind the scenes.
SendItemsToServerCommand = ReactiveCommand.CreateAsyncTask((_, ctx) => InternalSendItemsToServer());
SendItemsToServerCommand.ThrownExceptions.Subscribe(ex => UserError.Throw("Could not send data to server", ex));
LoadItems = ReactiveCommand.CreateAsyncObservable(_ => this.InternalGetItemsFromCacheAndServer());
LoadItems.Subscribe(list =>
{
_items.Clear();
foreach (var item in list){
//using AddRange throw Presentation
Solution
Sorry to disappoint, but you did everything right! This is a quite good example of using RxUI, Akavache, and Refit together.
The only thing I would change, is to not immediately call
Instead, I always call these commands in the View constructor, something like:
This means that any time we get a new ViewModel, we execute
The only thing I would change, is to not immediately call
LoadItems.ExecuteAsyncTask in the ViewModel constructor. Invoking this in the VM constructor means that your VM class becomes more difficult to test, because you always have to mock out the effects of calling LoadItems, even if the thing you are testing is unrelated.Instead, I always call these commands in the View constructor, something like:
this.WhenAnyValue(x => x.ViewModel.LoadItems)
.SelectMany(x => x.ExecuteAsync())
.Subscribe();This means that any time we get a new ViewModel, we execute
LoadItems, which is what we want when the app is running, but not what we want in every unit test.Code Snippets
this.WhenAnyValue(x => x.ViewModel.LoadItems)
.SelectMany(x => x.ExecuteAsync())
.Subscribe();Context
StackExchange Code Review Q#74642, answer score: 11
Revisions (0)
No revisions yet.