I am using ReactiveUI in WPF. I bind one ListBox.ItemSource to my ViewModel.FileList.
The below Observable.CombinedLatest() doesn't trigger the Func.
But the above single Observable trigger its Func.
The reason I want to use combined observable is:
I would like to display a list of data on the first time load - this is triggered by the ConvertedMessage property value changed.
Then I also need display different list when user enter the words on the search textbox, both of them actually update the same DataBinding Source List.
I also tried two different individual observables, two separate Methods in the Observable.SelectMany(MyMethod), but seems the second observable override the previous one - because only the last observable.MyMethod called.
Is there any idea or suggestion regarding the ReactiveUI CombinedLatest(), is it only trigger the function when all observables produce the element?
this.WhenAnyValue(x => x.ConvertedMessage).Where(x=>x!=null).Subscribe(x => { var msg = x.Message; });
var initial =
this
.WhenAnyValue(x => x.ConvertedMessage)
.Where(x=>x!=null);
var searchObjs =
this
.WhenAnyValue(x => x.SearchText)
.Throttle(TimeSpan.FromMilliseconds(800))
.Select(term => term)
.DistinctUntilChanged()
.Where(term => !string.IsNullOrEmpty(term));
reviewFiles = Observable.CombineLatest(initial, searchObjs, (message, term) => {
ObservableCollection<PrecedentFile> loadedFiles = new ObservableCollection<PrecedentFile>();
var start = 0;
CurrentPage = 1;
if (!string.IsNullOrEmpty(term))
{
//some logic
return searchedResults;
}
else
{
//other logic
return loadedFiles.AsEnumerable<PrecedentFile>();
}
}).Select(x=>x)
.ObserveOn(RxApp.MainThreadScheduler)
.ToProperty(this, x => x.ReviewFiles);
I believe the problem you are facing is the CombineLatest needs all observables passed into it to have at least had event/signals occurred at least once.
In your example above if initial has a trigger but searchObs does not, then the CombineLatest won't fire.
Eg you've had the ConvertedMessage be changed 3 times, but SearchText has never been changed, then the CombineLatest will not fire.
One thing you could do is remove the .Where(x => x != null) and Where(term => !string.IsNullOrEmpty(term)) statements and handle those conditions in your CombineLatest lambda. WhenAnyValue() will be default signal with the initial value of your properties (which is often null). That way both sides have already had a value signalled.
Related
I am new to the whole concept of Observables and rxjs so this might be very obvious. But please help as I am desperate to learn
I have a funtion when a user clicks to show comments under a blog post
showComments(){
this.firestoreservice.getComments(this.job.id).subscribe( data =>{
this.comments = data //type [] of comment objects with (message,posterID,timeposted...)
})
this._showcomments = true;
}
This function calls a service function that returns an observable to my comments list for that job.
In the subscribe to that observable is initialise a variable to hold all my comments.
Before I do that however, is there a way to replace the posterID attribute of each comment with the matching username? See desired outcome below:
showComments(){
this.firestoreservice.getComments(this.job.id).subscribe( data =>{
//for each comment held in data, replace comment.posterID with getUserName(posterID)
//getUserName(posterID) will also return an observable which emmits one string
//then append comment to comments list (or add back in to data then destructively assign
//comments=data )
this.comments = data //type [] of comment objects with (message,posterID,timeposted...)
})
this._showcomments = true;
}
it'll look a little like this....
this.firestoreservice.getComments(this.job.id).pipe(
switchMap(comments => { // switchMap to subscribe to inner
const getUsers$ = comments.map(comment => // array map comments into userName fetch
this.firestoreservice.getUserName(comment.posterID).pipe(
// once you have the username, rx map back into the comment with the username assigned
map(userName => ({...comment, posterID: userName})),
first() // use to complete after one emission to make forkJoin work
)
)
return forkJoin(...getUsers$); // forkJoin them all together for parallel execution
});
).subscribe(data => {
this.comments = data // now just set the data
})
I'm working with DevExpress DataGrid on React.js.
According to this example, I can navigate to specific row that not visible within a specific key.
But in this example, it use the "focusedRowKey" property.
In my case, I use the "focusedRowIndex" property. My problem is that I want to navigate to specific row by index that is not visible.
Is there any "navigateToRow" function that receive index, and not key?
Or can I get the global index, like "getRowIndexByKey" (this function not good for me, if the index is outside the visible rows, it return -1).
Thanks.
Solved it by manipulating between them:
let value = null;
// eslint-disable-next-line default-case
switch (fieldType) {
case 'focusedRowIndex':
value = eventIndex;
break;
case 'focusedRowKey':
value = eventId;
break;
}
const obj = {
[fieldType]: value
};
And then, on the component itself:
<DataGrid
id='grid-container'
dataSource={events}
keyExpr='_Id'
showBorders={true}
focusedRowEnabled={true}
{...obj}
I have a bunch of input fields into which I'd like to set a someClass name only in condition if an input field has some value. Remove someClass name if it doesn't.
I'm currently studying manipulating forms and forms fields using JavaScript and this is the code I'm working on.
// Add a class to an element, without removing/affecting existing values
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) {
// Set HTML elements collection index?
var input = inputs[i];
input.addEventListener('input', function() {
if (input.value.length > 0 && !input.className.match(/(?:^|\s)someClass(?!\S)/)) {
console.log("Input has some value");
input.className += "someClass";
}
if (input.value.length == 0) {
console.log("Input is empty");
input.className = input.className.replace( /(?:^|\s)someClass(?!\S)/g , '' );
}
});
}
The current code do what it suppose to do only on last input field. It occurred to me that var input is overwritten every time there is a new loop.
So I was thinking to target current element by some conditional statement within the loop. I can't think of anything else (with little knowledge I have about this) except manually assigning some of input's attributes.
The issue you are running into is that each input event listener is updating w/e element the variable input is representing at the time the event is fired, and is therefore only updating that element.
So the timeline goes like this:
Add event listener to input 1 | input = input1
Add Event Listener to input 2 | input = input2
Add event listener to input 3 | input = input3
All 3 event listeners are going to change the element held by variable input, which at this time is only input3.
User clicks inside input and types
var input which is input3 gets updated
As Sean pointed out, you need each eventListener function to update the specific element that fired that event. Here you have 3 options:
Use this this keyword
https://jsfiddle.net/5gbnykme/1/
Use event.target as reference
https://jsfiddle.net/5gbnykme/2/
Use a wrapper function to scope your variable locally
https://jsfiddle.net/5gbnykme/3/
TL;DR Make sure the element called in your event listener is the element that triggered the event.
This question does it in Javascript, but I would have thought in Typescript I could do some kind of map/filter operation to do the same thing.
I have an array of objects called Room. Each Room has a property called Width (which is actually a string, eg '4m', '5m', '6.5m').
I need to check the entire array to see if all the widths are the same.
Based on that question I have this, but I was wondering if TypeScript has something better:
let areWidthsTheSame = true;
this.qp.rooms.forEach(function(room, index, rooms) {
if (rooms[index] != rooms[index+1]) areWidthsTheSame = false;
});
Any ideas?
FYI the linked question has a comment that links to these performance tests, which are interesting in the context of this question:
This can be done in the following way:
const widthArr = rooms.map(r => r.width);
const isSameWidth = widthArr.length === 0 ? true :
widthArr.every(val => val === widthArr[0]);
We first convert the rooms array to an array of widths and then we check if all values in widths arrays are equal.
I have this many to many association between KundeInfo and HovedKategori, which I have mapped in my MS SQL database like this:
I have implemented the methods KundeInfo.HovedKategoris:
public IEnumerable<KundeInfo> KundeInfos
{
get
{
using (var dc = new DataClassesBSMAKSDataContext())
{
dc.DeferredLoadingEnabled = false;
var kundeInfoHovedKategoris = dc.KundeInfoHovedKategoris.Where(x => x.HovedKategori_Id == Id);
var kundeInfos = dc.KundeInfos.Where(x => kundeInfoHovedKategoris.Any(y => y.KundeInfo_Id == x.Id));
return kundeInfos.ToList();
}
}
}
... and HovedKategori.KundeInfos:
public IEnumerable<HovedKategori> HovedKategoris
{
get
{
using (var dc = new DataClassesBSMAKSDataContext())
{
var kundeInfoHovedKategoris = dc.KundeInfoHovedKategoris.Where(x => x.KundeInfo_Id == Id);
var hovedKategoris = dc.HovedKategoris.Where(x => kundeInfoHovedKategoris.Any(y => y.HovedKategori_Id == x.Id));
return hovedKategoris.ToList();
}
}
}
This retrieves the associated KundeInfos from a specific HovedKategori and opposite. The problemhowever lies in the serialization. When I call ToList(), or serialize these objects to JSON, linq will try to first follow all references returned by HovedKategori.KundeInfos, if it were that method I were to call first, and then it would for each returned object, try to follow all references returned by KundeInfo.HovedKategoris and so on, until it would cast a stack overflow exception.
If I could somehow prevent linq from following certain properties with an [Ignore] attribute or something, it would work, but I haven't been able to find anything like that.
What can I do in this situation?
This is in part a design issue. What you should really ask yourself is if you need navigation properties in every possible direction. For example if you just add a Kategori ID instead of a navigation property you could still query your context (by using the ID) but do you really need to always get all the Kategori's with all underlying data?
also if you make your properties virtual you have lazy loading and will only get the information if you .Include it or explicitly reference it.
Ok - So I solved this problem by just making it into methods on the respective classes, which it should be in the first place, since it retrieved these entities from the database. So yes, partially design issue.
Virtual did not work, I considered using projection and an abstract class, but it would be such a haystack of inheritance, and class casts, that it would not even be worth considering.
public IEnumerable<KundeInfo> KundeInfos()
{
using (var dc = new DataClassesBSMAKSDataContext())
{
var kundeInfoHovedKategoris = dc.KundeInfoHovedKategoris.Where(x => x.HovedKategori_Id == Id);
var kundeInfos = dc.KundeInfos.Where(x => kundeInfoHovedKategoris.Any(y => y.KundeInfo_Id == x.Id));
return kundeInfos.ToList();
}
}