Multiple Subscriptions on one observable - reactiveui

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

Related

How to load a firebase-collection with all its subcollection in one promise?

I am currently trying to figure out how to load an angular/fire-collection including all of its subcollections with RxJS.
This is my current approach:
return this.collectionRef.valueChanges().pipe(
flatMap((entities: Entity[]) => entity),
mergeMap((entity: Entity) => this.setSubCollection1(entity)),
mergeMap((entity: Entity) => this.setSubCollection2(entity)),
scan((entities: Entity[], entity: Entity) => entities.filter(a => a.id !== entity.id).concat(entity), [])
);
and to load the documents in their subcollections
private setSubCollection1 = (entity: Entity): Observable<Entity> => {
return this.subCollectionRef.valueChanges(actor).pipe(
map((subEntities1: SubEntity1[]) => {
entity.subEntities1 = subEntities1;
return entity;
})
);
}
It works fine when having a full stream.
But now I wanted to get all of data in one single Promise: I already tried .first().toPromise() but this only gets the first entry, and does not finish if the collection has no entries. Using reduce in the query also does not work, because valueChanges() never finishes.
Am I using the wrong operators? Or any other ideas on how to solve that?
I hope to hear from you.

ngxs state operator to insert or replace item in array (upsert)

I'm trying to replace or insert an array item (upsert) with existing ngxs state operators. I am currently using the following iif-statement. Is there an easier way to do this?
setState(
patch({
contractList: iif<Contract[]>(
contractList=>
contractList.some(
contract =>
contract.id === contractId
),
updateItem<Contract>(
contract =>
contract.id === contractId,
patch(loadedContract)
),
insertItem<Contract>(loadedContract)
)
})
)
State operators exist but it doesn't mean you're forced to use them, right. There is a recipe called Immutability Helpers. It's going to be published to the main site in 1-2 weeks.
If we're talking about state operators then it's possible to create custom operators, that's some kind of decomposition:
function insertOrUpdateContract(id: string, loadedContract?: Contract) {
return iif<Contract[]>(
contracts => contracts.some(contract => contract.id === id),
updateItem(contract => contract.id === id, patch(loadedContract)),
insertItem(loadedContract)
);
}
Thus there will be less code in your action handler and it will be more self-descriptive:
ctx.setState(
patch({ contractList: insertOrUpdateContract(contractId, loadedContract) })
);
Otherwise you could have used immer library or something else preferred, that would save more code:
import { produce } from 'immer';
const state = produce(ctx.getState(), draft => {
const index = draft.contractList.findIndex(contract => contract.id === contractId);
// It exists then let's update
if (index > -1) {
draft.contractList[index] = loadedContract;
} else {
draft.contractList.push(loadedContract);
}
});
ctx.setState(state);
For sure immer is an additional package for the project but it's very lightweight. It provides more declarative way to do such immutable updates. Choose what suits you best!
I would stop on some immutability helper when it comes to such complex state updates.

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),
}))

Best way to achieve forward chaining in NRULES

I want to run a rule based on the result of a previous rule. How can I achieve this functionality using forward chaining? I don't want to create a different class object for each rule to achieve forward chaining.
Here in this example an InstantDiscount object is created just for this one rule to achieve forward chaining.
public class PreferredCustomerDiscountRule : Rule
{
public override void Define()
{
Customer customer = null;
IEnumerable<Order> orders = null;
Double total = Double.NaN;
When()
.Match<Customer>(() => customer, c => c.IsPreferred)
.Query(() => orders, x => x
.Match<Order>(
o => o.Customer == customer,
o => o.IsOpen)
.Collect())
.Let(() => total, () => orders.Sum(x => x.Amount))
.Having(() => total > 1000);
Then()
.Yield(_ => new InstantDiscount(customer, total * 0.05));
}
}
public class PrintInstantDiscountRule : Rule
{
public override void Define()
{
InstantDiscount discount = null;
When()
.Match(() => discount);
Then()
.Do(_ => Console.WriteLine("Customer {0} has instant discount of {1}",
discount.Customer.Name, discount.Amount));
}
}
Forward chaining is the process where one rule changes the working memory of the rules engine in such a way as to activate some other rules. This can be achieved by inserting new facts into the rules engine (using Yield or IContext.Insert in NRules), or by changing some of the existing facts (using IContext.Update).
Here is the original example, reformulated to attach the discount to the Customer fact, and then update that fact to achieve forward chaining.
public class PreferredCustomerDiscountRule : Rule
{
public override void Define()
{
Customer customer = null;
IEnumerable<Order> orders = null;
Double total = Double.NaN;
When()
.Match<Customer>(() => customer, c => c.IsPreferred, c => !c.DiscountPercent.HasValue)
.Query(() => orders, x => x
.Match<Order>(
o => o.Customer == customer,
o => o.IsOpen)
.Collect())
.Let(() => total, () => orders.Sum(x => x.Amount))
.Having(() => total > 1000);
Then()
.Do(ctx => ApplyDiscount(customer, 0.05))
.Do(ctx => ctx.Update(customer));
}
private static void ApplyDiscount(Customer customer, double discount)
{
customer.DiscountPercent = discount;
}
}
public class DicsountNotificationRule : Rule
{
public override void Define()
{
Customer customer = null;
When()
.Match(() => customer, c => c.DiscountPercent.HasValue);
Then()
.Do(_ => Console.WriteLine("Customer {0} has instant discount of {1}%",
customer.Name, customer.DiscountPercent));
}
}
When forward chaining by updating existing facts, care must be taken to not re-activate the rule that updated the fact, to avoid undesirable recursion. There are several mechanisms to control recursion in NRules:
Write conditions in such a way that the update invalidates conditions of the rule (this is what we did in the above example; once the discount is set, the rule will no longer match)
Use Repeatability attribute on the rule to prevent re-firing
Use agenda filters to only activate the rule when certain changes occur in the matching facts.
The later two options are described in NRules documentation.

How to Observe Properties of Objects within an ReactiveList

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.

Resources