I have an App made with Xamarin.Forms and ReactiveUI.
Picture a View in this App where you have a kind of dropdown (actually a button that push another view where the user can filter and select one option) and when this "dropdown" is changed, I need to reload a list based on its value.
This "dropdown" won't start with some value, I need to make a async request, grab a value and then update the view.
The problem is, when I'm creating the command to load the documents:
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll, m_scheduler);
I need the Id from the SelectedToDoList, but this will be null at this point.
There's any way to delay this first execution of a command? Or maybe there's a better workflow to solve this?
Here is a sniped on how I'm doing it right now. Let me know if more information is needed.
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll, m_scheduler);
ChangeToDoListCommand = ReactiveCommand.CreateFromTask<DocumentListViewModel, bool>(vm => this.PushPageFromCacheAsync<ToDoListViewModel>((model) => model.ParentViewModel = this));
this.WhenActivated((CompositeDisposable disposables) =>
{
SelectedItem = null;
var SelectedTodoListChanged =
this
.WhenAnyValue(x => x.SelectedTodoList)
.Throttle(TimeSpan.FromSeconds(1), RxApp.MainThreadScheduler)
.Publish();
SelectedTodoListChanged
.Where(x => x == null)
.Subscribe(async _ => SelectedTodoList = await viewService.GetMyToDoListByVaultAsync(RuntimeContext.Current.VaultId))
.DisposeWith(disposables);
SelectedTodoListChanged
.Where(x => x != null)
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);
SelectedTodoListChanged.Connect();
LoadAllDocuments
.ObserveOn(m_scheduler)
.SubscribeOn(m_scheduler)
.Subscribe(list => AddViewsToList(list.ToList()))
.DisposeWith(disposables);
If I understand your question correctly, you need to ensure Id is not null before calling InvokeCommand:
SelectedTodoListChanged
.Where(x => x?.Id != null)
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);
Perhaps a better option is to bake this knowledge into the command itself. Since InvokeCommand respects the execution window of the command (as of RxUI 7, that is), if your command's CanExecute is currently false then InvokeCommand will not actually invoke your command:
var canLoadAllDocuments = this
.WhenAnyValue(x => x.SelectedTodoList?.Id)
.Select(id => id != null);
LoadAllDocuments = ReactiveCommand.CreateFromTask<string, IEnumerable<Document>>(
_ => m_service.GetAllDocumentsByTodoListAsync(SelectedTodoList.Id), canLoadAll,
canLoadAllDocuments,
m_scheduler);
Now you can do this:
SelectedTodoListChanged
.InvokeCommand(LoadAllDocuments)
.DisposeWith(disposables);
Related
My goal is to extract a specific record that has a parameter value specified by me. The data is taken from an external API.
My query looks like this:
var productId = productsResponse.Where(x => x.Parameters.Any(y => y.Values.Any(z => z.Equals(auctionTitle)))).FirstOrDefault();
And it works fine until where filters out all the records. Then the method aborts and debugging cannot continue.
The problem is:
System.ArgumentNullException: Value cannot be null
because source transferred to FirstOrDefault is null.
I also tried:
var productId = productsResponse.Where(x => x.Parameters.Any(y => y.Values.Any(z => z.Equals(auctionTitle)))).DefaultIfEmpty().First();
Please let me know what topic I should read because I have run out of ideas. I really care to understand where I am wrong.
This can be not an answer but try this construction:
var productId = productsResponse
.Where(x => x.Parameters.SelectMany(y => y.Values)
.Any(z => z == auctionTitle))
.FirstOrDefault();
Also if data came from external API, it may be needed more null check.
var productId = productsResponse
.Where(x => x.Parameters?.SelectMany(y => y.Values ?? Enumerable.Empty<Value>())
?.Any(z => z == auctionTitle) == true)
.FirstOrDefault();
I have the following Problem. I have got
aProductionOrderList = new ReactiveList<ProductionOrderViewModel>();
the ProductionOrderViewModel has a Property Itemsleft, which gets updated internally
private readonly ObservableAsPropertyHelper<int> itemsLeft;
public int ItemsLeft => this.itemsLeft.Value;
...
this.itemsLeft = this
.WhenAny(x => x.Ticks, x => x.Value)
.ToProperty(this, x => x.ItemsLeft, scheduler: DispatcherScheduler.Current);
What i want to accomplish is when ever any item in the List comes to a point where the Itemsleft property is 0, it should be removed from the List.
I tried it this way
ProductionOrderList.ItemChanged.Where(x => x.Sender.ProductionOrder.ItemsLeft ==0)
.Subscribe(v =>
{
// do stuff
});
but it did not work unfortunately.
Help is very much appreciated.
So I found a working solution, it even seems rigth and clean, though I am totally open for improvements. I have done the following
this.WhenAnyObservable(o => o.ProductionOrderList.ItemChanged)
.Where(x => x.PropertyName == "ItemsLeft")
.Select(x => x.Sender)
.Where(x => x.ItemsLeft == 0)
.Subscribe(x =>
{
ProductionOrderList.Remove(x);
});
I hope this helps others which habe a similar problem.
I have an Observable like this:
var posChangeObs =
Observable.Publish<DisplayPositionModel>(
Observable
.FromEventPattern<EventHandler<PositionEventArgs>, PositionEventArgs>(h => cine.PositionChange += h,
h => cine.PositionChange -= h,
RxApp.MainThreadScheduler)
.Select(x => new DisplayPositionModel(cine.ToDisplayPosition(x.EventArgs.Position), x.EventArgs.LinearFrameIndex)),
new DisplayPositionModel(cine.ToDisplayPosition(cine.CurrentCinePosition), cine.CurrentLinearCinePosition));
The event this tracks will always occur on a different thread. I pass this Observable to a lot of different view models. In some view models the eventArgs are set to a property using ToProperty. In others I just Subscribe and DoStuff(TM).
What I want to know is how to ensure that these are always marshaled to the UI thread. I have tried adding ObserveOn(RxApp.Main...) on all of the ToProperty and Subscribe calls, but that did not work.
Here is an example of how I am using ToProperty right now and getting cross thread exception:
posChangeObs.ToProperty(this, x => x.CurrentPosition, out _CurrentPosition);
and here is an example Subscription:
posChangeObs
.Select(x => x.LinearFrameIndex)
.Subscribe(x => this.CurrentLinearFrameIndex = x,
e =>
{
throw e;
});
The answer was to add the ObserveOn(RxApp.MainThreadScheduler) to the return of FromEventPattern. Like this
var posChangeObs = Observable.Publish<Position>( Observable.FromEventPattern<EventHandler<PositionEventArgs>,PositionEventArgs>(h => x.PositionChange += h, h => x.PositionChange -= h)
.ObserveOn(RxApp.MainThreadScheduler)
.Select(x => ...));
Below returns all of the people in a building and all of their computers, and this works.
I want to change this to only include the Computers where Active == 1 and only the ActivityLogs where ActivityTypeId == 5. But if they don’t have either I still want the person returned.
public IQueryable<Person> GetPeople(int BuildingId)
{
return this.ObjectContext.People
.Include("Computers")
.Include("ActivityLog")
.Where(p => p.buildingId == BuildingId && !p.migrated)
.OrderBy(p => p.name);
}
Unfortunately this is not possible using Include syntax. As an alternative, you can select each entity individually like so:
var queryWithAllData = this.ObjectContext
.People
.Select(p => new
{
Person = p,
Computers = p.Computers.Where(c => c.Active == 1),
ActivityLogs = p.ActivityLog.Where(a => a.ActivityTypeId == 5)
});
// Do what you need with the records now that you have all the data.
I have some Setting entities that are related to a SettingDescription which is related to a SettingGroup.
Setting history is preserved by making a "Modified" field part of the key.
To get the settings matching a specific category I use this query (after help from here):
var latestSettings =
context.Settings.Include("Description.SettingGroup")
.OrderByDescending(x => x.Modified)
.GroupBy(x =>
new {
x.Category,
x.Group,
x.Name,
x.Target }, x => x)
.Where(x => x.Key.Category == category)
.Select(result => result.FirstOrDefault())
.ToArray();
This returns a set of the latest settings, but the "Include" part is completely ignored. However, I can force load the descriptions by running a second dummy query that loads the descriptions into the context.
var latestSettings =
context.Settings.Include("Description.SettingGroup")
.OrderByDescending(x => x.Modified)
.GroupBy(x =>
new {
x.Category,
x.Group,
x.Name,
x.Target }, x => x)
.Where(x => x.Key.Category == category)
.Select(result => result.FirstOrDefault())
.ToArray();
var settingDescriptions =
context.SettingDescriptions.Include("SettingGroup")
.Where(x => x.Category == category)
.ToArray();
Why is the include ignored in the "stand alone" group query?
Can I combine the setting and description loading into a single query?
AlexJ from the EF team posted an excellent series of tips, including:
"Tip 22 - How to make Include really Include"
http://blogs.msdn.com/b/alexj/archive/2009/06/02/tip-22-how-to-make-include-really-include.aspx
It looks to me like your query is returning "Settings" entities (without a "change of shape") so this tip should apply.