I have a regular type view of a list of items (array of objects) that the user can filter by typing text into a TextBox, but I also want to be able to sort it, so I have some buttons for selecting order by fields (e.g. name, count)
I can do either one by e.g. (and none of this is actual code, just written from memory here):
var myItems = [
{ name: "foo", count: 14 }, {}, {}, {}
]
var textFilterObservable = Observable("")
var orderSelectionObservable = Observable("id");
var exportedList = textFilterObservable.map(function(t) {
var re = new RegExp(t);
return myItems.filter(function(i) {
return re.test(i.name);
})
}).inner()
BUT how on Earth am I meant to combine these two in a sensible way?
I’ve tried making a “compound observable”, like this
var both = Observable({ text: textFilterObservable, order: orderSelectionObservable }) // even .inner()
var exportedList = both.map(function(b) { ..never fires on UI changes })
I’ve tried both.addSubscriber() also…
the both.map() seems to fire if I set something in code, but not through UI changes/clicks…
How are you meant to do this in a clean way? (I’ve done ugly hacks…)
your use-case is somewhat complicated and requires to do a little ritual dance around the fire to get it working. Knowing some magic spells and yelling them out at the midnight, while running naked around your house in circles might help too.
Anyway, here’s the thing. As you have correctly found, you have to somehow bind to both search and filter, and you’ve already tried to do that. The approach you took failed because of one simple reason - one of those has to be the primary criteria. I imagine you’d first like to sort the list, and then filter it by the search keyword.
So I took the time and liberty to write a fully functional snippet that does exactly what you needed:
<App Background="#eee">
<JavaScript>
var Observable = require('FuseJS/Observable');
var data = Observable();
var search = Observable('b');
var order = Observable('title');
function SingleEntry(id, title) {
this.id = id;
this.title = title;
};
function fillData() {
data.add(new SingleEntry(1,'EA title'));
data.add(new SingleEntry(2,'DE title'));
data.add(new SingleEntry(3,'CD title'));
data.add(new SingleEntry(4,'BC title'));
data.add(new SingleEntry(5,'AB title'));
};
// call fillData here so that there is something in data observable before the reactives fire
fillData();
var ordered = order.map(function(orderKey) { // this here fires whenever the order value changes
var tmp = data.toArray(); // now we tap into the data variable
tmp.sort(function(a,b) {
if (a[orderKey] > b[orderKey]) {
return 1;
}
if (a[orderKey] < b[orderKey]) {
return -1;
}
return 0;
});
return tmp;
}).inner(); // get the inner array, so we do not have an array in an array
var searched = search.map(function(keyword) { // this here fires whenever the search value changes
var pattern = new RegExp(keyword, 'i');
return ordered.where(function(e) { // notice how we tap into ordered, which is reactive
return pattern.test(e.title);
});
}).inner(); // and again, get the inner thing
searched.addSubscriber(function(x) {
console.log('searched and ordered: ' + JSON.stringify(x));
});
module.exports = {
'searched': searched // make sure you only export the last thing in the reactives row, searched
};
</JavaScript>
</App>
Sorry for not adding any UX, but the addSubscriber makes it fire anyway and logs stuff to Monitor. I’m sure you can get it going from here.
Aha, I added some quick UI to it and I see it works nicely. Thanks!
It was the // notice how we tap into ordered, which is reactive .where() part with the chaining that was the part where I couldn’t figure out how to chain the RX bits.