Search code examples
javascriptfunctional-programmingreactive-programmingrxjsreactive-extensions-js

Creating a filterable list with RxJS


I'm trying to get into reactive programming. I use array-functions like map, filter and reduce all the time and love that I can do array manipulation without creating state.

As an exercise, I'm trying to create a filterable list with RxJS without introducing state variables. In the end it should work similar to this:

enter image description here enter image description here

I would know how to accomplish this with naive JavaScript or AngularJS/ReactJS but I'm trying to do this with nothing but RxJS and without creating state variables:

var list = [
  'John',
  'Marie',
  'Max',
  'Eduard',
  'Collin'
];

Rx.Observable.fromEvent(document.querySelector('#filter'), 'keyup')
  .map(function(e) { return e.target.value; });

// i need to get the search value in here somehow:
Rx.Observable.from(list).filter(function() {}); 

Now how do I get the search value into my filter function on the observable that I created from my list?

Thanks a lot for your help!


Solution

  • You'll need to wrap the from(list) as it will need to restart the list observable again every time the filter is changed. Since that could happen a lot, you'll also probably want to prevent filtering when the filter is too short, or if there is another key stroke within a small time frame.

    //This is a cold observable we'll go ahead and make this here
    var reactiveList = Rx.Observable.from(list);
    
    //This will actually perform our filtering
    function filterList(filterValue) {
      return reactiveList.filter(function(e) {
       return /*do filtering with filterValue*/;
      }).toArray();
    }
    
    
    var source = Rx.Observable.fromEvent(document.querySelector('#filter'), 'keyup')
      .map(function(e) { return e.target.value;})
    
      //The next two operators are primarily to stop us from filtering before 
      //the user is done typing or if the input is too small
      .filter(function(value) { return value.length > 2; })
      .debounce(750 /*ms*/)
    
      //Cancel inflight operations if a new item comes in.
      //Then flatten everything into one sequence
      .flatMapLatest(filterList);
    
    //Nothing will happen until you've subscribed
    source.subscribe(function() {/*Do something with that list*/});
    

    This is all adapted from one of the standard examples for RxJS here