ReactiveCommand stopped working in ReactiveUI 6 - reactiveui

I'm trying to work on James Nugent labs from the Progressive .NET 2014 and convert it to ReactiveUI 6. Currently I got stuck with CreateAsyncTask, it seems to never run the task code.
The VM code has this:
private IObservable<string> PretendToCallTheServer()
{
return Observable.Return("Hello World")
.Delay(TimeSpan.FromMilliseconds(2000), RxApp.TaskpoolScheduler);
}
public PersonViewModel()
{
_fullName = this.WhenAnyValue(vm => vm.FirstName, vm => vm.LastName, (f, l) => string.Format("{0} {1}", f, l))
.ToProperty(this, vm => vm.FullName);
var firstAndLastFilled = this.WhenAnyValue(vm => vm.FirstName, vm => vm.LastName,
(f, l) => !string.IsNullOrWhiteSpace(f) && !string.IsNullOrWhiteSpace(l));
ChangeName = ReactiveCommand.CreateAsyncTask(firstAndLastFilled, async x => await PretendToCallTheServer());
ChangeName.Subscribe(x => ServerResult = x);
}
private string _serverResult;
public string ServerResult
{
get { return _serverResult; }
set { this.RaiseAndSetIfChanged(ref _serverResult, value); }
}
public readonly ReactiveCommand<string> ChangeName;
And here is the failing test:
[Test]
public void ChangeNamePretendsToCallServerButOurTestRunsFast()
{
(new TestScheduler()).With(sched =>
{
var sut = new PersonViewModel {FirstName = "Jane", LastName = "Appleseed"};
Assert.IsTrue(sut.ChangeName.CanExecute(null));
sut.ChangeName.ExecuteAsync();
sched.AdvanceByMs(1000);
Assert.AreEqual(null, sut.ServerResult);
sched.AdvanceByMs(10000);
Assert.AreEqual("Hello World", sut.ServerResult);
});
}
Basically, I followed the guidelines on conversion of commands to RUI6 but it seems not to work. I set some breakpoints in the task method and on subscription and debugged the test - these breakpoints were never hit.

This is by-design:
// You didn't Subscribe, so it doesn't do anything
sut.ChangeName.ExecuteAsync();
Change this to something like:
var result = default(string);
sut.ChangeName.ExecuteAsync().Subscribe(x => result = x);
sched.AdvanceByMs(1000);
Assert.AreEqual(null, result);
sched.AdvanceByMs(10000);
Assert.AreEqual("Hello World", result);

Related

Google Place Api Prediction class cast to Position or something equivalent

I'm working on a Xamarin Forms project. I have a very simple page where a job seeker searches job posts by entering a location(ex. New York) and a radius(picker). I have the following code in my PageViewModel:
public class SearchPageViewModel : BaseViewModel
{
private readonly INavigation _navigation;
private readonly GooglePlacesApiService _api;
private IEnumerable<JobPost> Posts;
public int RadiusIndex {get;set;}
public SearchPageViewModel (INavigation navigation, IEnumerable<JobPost> posts)
{
_navigation = navigation;
var settings = GoogleApiSettings.Builder.WithApiKey("MY_API_KEY").Build();
_api = new GooglePlacesApiService(settings);
this.posts =posts;
}
public ICommand DoSearchCommand
=> new Command(async () => await DoSearchAsync().ConfigureAwait(false), () => CanSearch);
public ICommand SelectItemCommand
=> new Command<Prediction>(async (prediction) =>
{
//!!!Filter jobposts based on position and radius to be able to display it!!!
_api.ResetSessionToken();
});
private string _searchText;
public string SearchText
{
get => _searchText;
set
{
if (SetProperty(ref _searchText, value))
OnPropertyChanged("CanSearch");
}
}
private List<Prediction> _results;
public List<Prediction> Results
{
get => _results;
set => SetProperty(ref _results, value);
}
public bool CanSearch => !string.IsNullOrWhiteSpace(SearchText) && SearchText.Length > 2;
private async Task DoSearchAsync()
{
var results = await _api.GetPredictionsAsync(SearchText)
.ConfigureAwait(false);
if(results != null && results.Status.Equals("OK"))
{
ResultCount = results.Items.Count;
Results = results.Items;
OnPropertyChanged("HasResults");
}
}
}
public class JobPost
{
public Position Location {get;set;}
}
Ok, so far it works well. Once the search button is clicked the job seeker get a list of predictions and picks a place. But the problem is now, how do I get the positioning(longitude and latitude) of the picked prediction, so I can filter the job posts so I can dsiplay the result to the job seeker.
Thanks in advance.
use the Essentials GeoCoding plugin
var address = "New York";
var locations = await Geocoding.GetLocationsAsync(address);
locations will be a list of potential matches, each with a lat/long
var location = locations?.FirstOrDefault();
if (location != null)
{
Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");
}

Flutter/Dart "Cannot modify an unmodifiable list" occurs when trying to sort a list that is the snapshot of a stream of lists

This is the code inside the builder method of the Streambuilder that causes the issue:
List<User> users = snapshot.data;
users.sort((user1, user2) => (user1.distanceInKm ?? 1000).compareTo(user2.distanceInKm ?? 1000));
If I use the following stream for the Streambuilder the above sorting works:
static Stream<List<User>> getUsersStreamWithDistance(
{#required User loggedInUser}) {
try {
var userSnapshots = _fireStore.collection('users').snapshots().map(
(snap) => snap.documents
.map((doc) => User.fromMap(map: doc.data))
.where((user) => user.email != loggedInUser.email)
.map((user) {
user.updateDistanceToOtherUser(otherUser: loggedInUser);
return user;
}).toList());
return userSnapshots;
} catch (e) {
print(e);
return null;
}
}
But not when I use the following stream, which is the one I need (ZipStream is from rxdart package):
static Stream<List<User>> getSpecifiedUsersStreamWithDistance(
{#required User loggedInUser, #required List<String> uids}) {
try {
List<Stream<User>> listOfStreams = [];
for (var uid in uids) {
Stream<User> streamToAdd = _fireStore
.collection('users')
.where('email', isEqualTo: uid)
.snapshots()
.map((snap) => snap.documents
.map((doc) => User.fromMap(map: doc.data))
.map((user) {
user.updateDistanceToOtherUser(otherUser: loggedInUser);
return user;
}).toList()[0]);
listOfStreams.add(streamToAdd);
}
Stream<List<User>> usersStream = ZipStream.list(listOfStreams);
return usersStream;
} catch (e) {
print(e);
return null;
}
}
Its because, ZipStream.list() creates a new Stream of List.unmodifiable() list.
List<User> users = List.from(snapshot.data); // to convert it editable list
users.sort((user1, user2) => (user1.distanceInKm ?? 1000).compareTo(user2.distanceInKm ?? 1000));

ReactiveUI not responding to changes in test (with Autofixture)

Look at the following codes (simple ones)
public class NewEventViewModel : ReactiveObject
{
readonly ObservableAsPropertyHelper<bool> isSendVisible;
public bool IsSendVisible
{
get { return isSendVisible.Value; }
}
private ReactiveList<string> users;
public ReactiveList<string> Users
{
get { return this.users; }
set { this.RaiseAndSetIfChanged(ref users, value); }
}
public NewEventViewModel()
{
Users = new ReactiveList<string>(new List<string>())
{
ChangeTrackingEnabled = true
};
Users.CountChanged
.Select(x => x > 0)
.ToProperty(this, x => x.IsSendVisible, out isSendVisible);
//Users.Add("commented");
}
}
And the test:
[Fact]
public void ShouldBeVisibleWhenIsAtLeastOneUser()
{
//var sut = new NewEventViewModel();
var fixture = new Fixture();
var sut = fixture.Create<NewEventViewModel>();
sut.Users.Add("something");
sut.IsSendVisible.ShouldBeTrue();
}
The test fails... but when I uncomment the line from ViewModel, it passes.
Looks like the test is ignoring changes to Users. It works when I create the sut manually (new NewEventViewModel()). Why is AutoFixture breaking the test in such a strange way? What am I doing wrong?
You can use
fixture.Build<NewEventViewModel>().OmitAutoProperties().Create()
to temporarily turn off auto-properties.

nancyfx cookies based session values in Get can't be found in Post and vice versa

I'm trying with Nancyfx self hosting. The problem is when I set Session["key"] = value in Get["path"], then I call it in Post["path"], I get empty and vice versa. Below is my code
public class Bootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(Nancy.TinyIoc.TinyIoCContainer container, Nancy.Bootstrapper.IPipelines pipelines)
{
CookieBasedSessions.Enable(pipelines);
}
}
public class TestModule : NancyModule
{
public TestModule()
{
Get["/"] = _ =>
{
Session["App1"] = "Ola";
return Session["App1"] + " " + "Hello World";//"Ola Hello World"
};
Get["/about"] = _ =>
{
return Session["App1"];//"Ola Hello World"
};
Post["/create"] = _ =>
{
return Session["App1"];//emtpty
};
Post["/add"] = _ =>
{
return Session["App1"];//empty
};
}
}
class Program
{
static void Main(string[] args)
{
var cfg = new HostConfiguration();
cfg.UrlReservations.CreateAutomatically = true;
var host = new NancyHost(new Bootstrapper(), cfg, new Uri("http://localhost:5050"));
host.Start();
Console.ReadKey();
WebRequest wr1 = HttpWebRequest.Create("http://localhost:5050/create");
wr1.Method = "POST";
wr1.GetRequestStream();
StreamReader sr1 = new StreamReader(wr1.GetResponse().GetResponseStream());
Console.WriteLine(sr1.ReadToEnd());
Console.ReadKey();
WebRequest wr2 = HttpWebRequest.Create("http://localhost:5050/add");
wr2.Method = "POST";
wr2.GetRequestStream();
StreamReader sr2 = new StreamReader(wr2.GetResponse().GetResponseStream());
Console.WriteLine(sr2.ReadToEnd());
Console.ReadKey();
host.Stop();
}
}
With this problem, now I have no way to save login status or some necessary info. Do you guys have a solution?
As #phill points out the code you posted works as expected.
In fact your bootstrapper and module works, and the session is accessible from all the handlers.
The problem is that when you create new HttpWebRequests the cookies from earlier calls are not preserved. To carry over the cookies from one such request to the next do what this answer says.

ReactiveUI: CanExecute with a non collection property

I've seen question ReactiveUI: Using CanExecute with a ReactiveCommand, however my issue is that I have a string property, UniqueID, and I want it to only execute when it has a length equal to 7. I cannot seem to come up with an observer that doesn't crash the program. What is the correct simple way to do this?
public class MainViewModel : ReactiveValidatedObject
{
public MainViewModel()
{
RetrieveRecord = new ReactiveAsyncCommand(/* what goes here for CanExecute */);
RetrieveRecord.Subscriber(x => Record = new Record(UniqueId));
// or do we use the method RetrieveRecord.CanExecute()?
// the next line crashes the app
RetrieveRecord.CanExecute(UniqueId.Length == 7);
}
public ReactiveAsyncCommand RetrieveRecord { get; private set; }
string _uniqueId;
public string UniqueId
{
get { return _uniqueId; }
set
{
_clientId = value;
this.RaisePropertyChanged(x => x.UniqueId);
}
}
}
How about:
RetrieveRecord = new ReactiveAsyncCommand(
this.WhenAny(x => x.UniqueId, x => x.Value.Length == 7));

Resources