How to Observe Properties of Objects within an ReactiveList - reactiveui

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.

Related

FirstOrDefault throws ArgumentnullException

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();

rxjs shortcut for mapping of one property but streaming the whole object

I want to manipulate properties of an object but streaming the whole object
Is there a shortcut for this beauty?
Observable.of(data)
.map((data) => {data.newDueDate = this.filterDate(data.newDueDate); return data;})
.map((data) => {data.reply = this.generateReply(data.request); return data;})
...
I am searching for something like this, so i can alter the data object in clear atomic steps.
Observable.of(data)
.mapProperty('newDueDate' => this.filterDate)
.mapProperty('reply' => this.generateReply(data.request))
...
You can use do:
Observable.of(data)
.do(data => {
data.newDueDate = this.filterDate(data.newDueDate);
data.reply = this.generateReply(data.request);
})
Edit to the comment:
Observable.of(data)
.do(data => data.newDueDate = this.filterDate(data.newDueDate))
.do(data => data.reply = this.generateReply(data.request))
You can do that or create or own custom operator, but I think that's too much.
Well I think you could add both manipulations into the first map() but I agree it looks kind of lame so you might use Object.assign() for example:
Observable.of(data)
.map(d => Object.assign(d, {
newDueDate: this.filterDate(data.newDueDate),
reply: this.generateReply(data.request),
}))

How to delay ReactiveCommand.CreateFromTask first execution

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);

Multiple Subscriptions on one observable

I have a read-write property on my ViewModel and need two separate actions to occur when it changes :
public decimal Paid {
get { return paid; }
set { this.RaiseAndSetIfChanged(ref paid, value); }
}
...
in the ctor:
this.WhenAnyValue(pb => pb.Paid)
.Select(amount => NumberToEnglish.ToSentence(amount))
.ToProperty(this, x => x.AmountInWords, out amountInWords);
this.WhenAnyValue(pb => pb.Paid)
.Subscribe(amount => SelectedPaymentBatch.Paid = amount );
Is there a way to do this in one statement or is this the correct way to do this?
It's very much feasible to do both in one stream, e.g using Do operator (see below), but I would recommend to keep your current approach, as it correctly separates both concerns, which are unrelated but the fact they trigger on the same property (but that could change).
this.WhenAnyValue(pb => pb.Paid)
.Do(amount => SelectedPaymentBatch.Paid = amount)
.Select(amount => NumberToEnglish.ToSentence(amount))
.ToProperty(this, x => x.AmountInWords, out amountInWords);

How to include related objects in grouped Entity LINQ queries?

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.

Resources