Filter list of observables


#1

Is it possible to have a list of observables dynamicly filtered while typing in textinput?

Could anybody please provide me with advise on how to do this?


#2
var unfiltered = Observable(1,2,3);

num being each item of the Observable it works like JavaScript’s filter Array method

https://www.fusetools.com/learn/fusejs#observable-where-where-condition


#3

Edwin Reynoso wrote:

var unfiltered = Observable(1,2,3);

var filtered = unfiltered.where(function(num) {
  return num > 1;
});

num being each item of the Observable it works like JavaScript’s filter Array method

https://www.fusetools.com/learn/fusejs#observable-where-where-condition

Hi,

I tried running your code and print the output in my console so I could understand how it works like this:

var unfiltered = Observable(1,2,3);

var filtered = unfiltered.where(function(num) {
    return num > 1;
});

console.log("Unfiltered: " + JSON.stringify(unfiltered));
console.log("Filtered: " + JSON.stringify(filtered));

The result I got was:

LOG: Unfiltered: {"_subscribers":[],"_isLeaf":true,"_values":[1,2,3]}

LOG: Filtered: {"_subscribers":[],"_isLeaf":false,"_values":[]}

Can you tell me what I’m doing wrong here?


#4

I tried their examaple:

fruits = Observable(
    { name: 'Apple' , color: 'red'    },
    { name: 'Lemon' , color: 'yellow' },
    { name: 'Pear'  , color: 'green'  },
    { name: 'Banana', color: 'yellow' });

goodFruits = fruits.where(function(e){
    return e.color === 'yellow';
});

and I’m getting the same thing, the function is not being called at all

This has to be an internal error


#5

Have you made sure that the filtered observable is actually used somewhere in the UI and not just logged to console?

To guarantee that the observable is evaluated it has to be consumed somewhere in the UX, as mentioned here, here and here.


#6

Remi Pedersen wrote:

Have you made sure that the filtered observable is actually used somewhere in the UI and not just logged to console?

To guarantee that the observable is evaluated it has to be consumed somewhere in the UX, as mentioned here, here and here.

Thnx for the information, I got this to show in my UX.

After that I tried replacing 'yellow' with a observable thats databound to a textfield but I couldn’t get my list to update.

Any advise on how to do this?


#7

Hi

You can check out this sample: https://github.com/fusetools/fuse-samples/tree/master/Samples/FilterOnObservableCondition


#8

I want to achieve a filter/search bar like this.

https://www.youtube.com/watch?v=c9yC8XGaSv4

Is that possible?

The sample that Kristian Hasselknippe gave is good but not exactly what I’m looking for.


#9

It is absolutely possible :slight_smile:

<App Theme="Basic">
    <ClientPanel>
        <JavaScript>
            var Observable = require("FuseJS/Observable");
            allItems = Observable({name: "Apple"},{name: "Lemon"},{name: "Pear"},{name: "Banana"});

            searchString = Observable("");

            // ignore case so just convert both to lowercase before comparison
            function stringContainsString(main,filter){
                return main.toLowerCase().indexOf(filter.toLowerCase()) != -1;
            }

            var filteredItems = allItems.where(function(e){
                return Observable(function() {
                    return stringContainsString(e.name, searchString.value);
                });
            });

            module.exports = {filteredItems: filteredItems, searchString: searchString};
        </JavaScript>

        <TextInput Value="{searchString}" Dock="Top"/>
        <StackPanel>
            <Each Items="{filteredItems}">
                <Text Value="{name}"/>
            </Each>
        </StackPanel>
    </ClientPanel>
</App>


#10

Remi Pedersen wrote:

It is absolutely possible :slight_smile:

<App Theme="Basic">
    <ClientPanel>
        <JavaScript>
            var Observable = require("FuseJS/Observable");
            allItems = Observable({name: "Apple"},{name: "Lemon"},{name: "Pear"},{name: "Banana"});

            searchString = Observable("");

            // ignore case so just convert both to lowercase before comparison
            function stringContainsString(main,filter){
                return main.toLowerCase().indexOf(filter.toLowerCase()) != -1;
            }

            var filteredItems = allItems.where(function(e){
                return Observable(function() {
                    return stringContainsString(e.name, searchString.value);
                });
            });

            module.exports = {filteredItems: filteredItems, searchString: searchString};
        </JavaScript>

        <TextInput Value="{searchString}" Dock="Top"/>
        <StackPanel>
            <Each Items="{filteredItems}">
                <Text Value="{name}"/>
            </Each>
        </StackPanel>
    </ClientPanel>
</App>

This is exactly what I needed. Thanks!


#11

Curiously, I tried the code that Remi shared but it’s not returning the right values all the time. Sometimes it works and sometimes not, which is weird.

For example: If I type “na”, in order to show “Banana”, it shows “Apple”. But if I press “space” on keyboard AND return the cursor, than it shows “Banana”!

I’m not sure what’s wrong…


#12

I think the best practices for doing real-time filtering of search results have changed since I wrote that post.
You can check out this for a more official approach to the problem: https://www.fusetools.com/docs/fusejs/observable#where-condition

:slight_smile:


#13

Hi, Remi.

I tried to use this new approach without success. Would you mind to update the example code?

Thanks in advance!


#14

Hi Carlos, which piece of code did you try? This snippet from the link Remi posted should be more or less what you’re after:

var items = conditionObservable.flatMap(function(v) {
    return itemsObservable.where(function(x) { return v != x; });
});

#15

In this example we have a new Observable apart from the “allItems” array. I think I’m not sure where conditionObservable fits in the context of the example above. I tried to adapt the function with no luck.

Anyway, I will keep trying until find the answer. Maybe I need to read the docs more carefully.


#16

I’m facing the same problem. Here’s my sample code (adapting the code of Jake over Remi’s example):

<App Theme="Basic">
    <ClientPanel>
        <JavaScript>
            var Observable = require("FuseJS/Observable");
            allItems = Observable({name: "Apple"},{name: "Lemon"},{name: "Pear"},{name: "Banana"});

            searchString = Observable("");

            // ignore case so just convert both to lowercase before comparison
            function stringContainsString(main,filter){
                return main.toLowerCase().indexOf(filter.toLowerCase()) != -1;
            }

            var filteredItems = allItems.flatMap(function(e) {
                return allItems.where(function() {
                  return stringContainsString(e.name, searchString.value);
                });
            });

            module.exports = {filteredItems: filteredItems, searchString: searchString};
        </JavaScript>

        <TextInput Value="{searchString}" Dock="Top"/>
        <StackPanel>
            <Each Items="{filteredItems}">
                <Text Value="{name}"/>
            </Each>
        </StackPanel>
    </ClientPanel>
</App>

I can’t make this work either. This flatMap example: https://www.fusetools.com/docs-qa/fusejs/observable-patterns is too short. It lacks a full code with a UX databinding, so that we can connect the dots. Actually, I’m pretty shocked that such a simple list filter like this one still hasn’t appeared in the Fusetools Example page.

I would be glad if someone had an answer for this thread.


#17

@Renato Did you see the API documentation for where. It should explain why you need to use flatmap. It also explains that you can do this in multiple ways.

I have modified your code to follow one of those approaches.

<App>
    <ClientPanel>
        <JavaScript>
            var Observable = require("FuseJS/Observable");
            allItems = Observable({name: "Apple"},{name: "Lemon"},{name: "Pear"},{name: "Banana"});

            searchString = Observable("");

            // ignore case so just convert both to lowercase before comparison
            function stringContainsString(main, filter){
                return main === '' || main.toLowerCase().indexOf(filter.toLowerCase()) !== -1;
            }

            var filteredItems = searchString.flatMap(function(searchValue) {
            	return allItems.where(function(item) { return stringContainsString(item.name, searchValue); });
            });

            module.exports = { filteredItems: filteredItems, searchString: searchString };
        </JavaScript>

        <TextInput Value="{searchString}" Dock="Top"/>
        <StackPanel>
            <Each Items="{filteredItems}">
                <Text Value="{name}"/>
            </Each>
        </StackPanel>
    </ClientPanel>
</App>

#18

Thanks, Anders! It worked now! Fantastic.


#19

Oh wow so glad I found this. Worked perfectly (I multiple external array files loading into different views). I’m going to close my other case I opened. Thanks.


Filtering view based on search entry