Pregunta

Say I have movies array, if I wanted to filter by genre I can do something like this

filtered = ko.computed(function() {
        var self = this;

        if ( ! self.genresFilter() || self.genresFilter() === 'all') {
            return this.sourceItems();
        } else {
            return ko.utils.arrayFilter(self.sourceItems(), function(item) {
                return app.utils.inArray(item.genre, self.genresFilter());     
            });
        }
    }, app.viewModels.games);

But where I'm stuck currently is if I had genres, language and length html dropdowns how can I filter the movies efficiently by all or some of those filters, so that I can have russian action movies or action movies of 90 min length etc?

¿Fue útil?

Solución

You'll need to gradually build a filtered list based off of each filter provided. As for the filters themselves, they should either each be represented by an observable or all of them inside an observableArray. This is important because it will trigger the computed to update as you change your filters.

Example:

var filteredList = ko.computed({
    var currentList = this.sourceItems();
    var currentFilters = this.genresFilters();

    ko.utils.arrayForEach(currentFilters, function () {
        currentList = ko.utils.arrayFilter(currentList, function(filter) {
            return app.utils.inArray(filter, currentFilters);     
        });
    });

    return currentList;
});

This code will loop through each filter, take the latest filtered list and only keep the items that satisfy all conditions.

Otros consejos

I know this is old topic, but I had the same issue recently, this is my solution modified for your movies model. You can also check it out here: jsfiddle

var MoviesModel = function(data) {
  var self = this;
  self.Genres = ko.observableArray(data.genres);
  self.Genres.unshift("All");
  self.filterGenre = ko.observable("All");

  self.Languages = ko.observableArray(data.languages);
  self.Languages.unshift("All");
  self.filterLanguage = ko.observable("All");

  self.Lengths = ko.observableArray(data.lengths);
  self.Lengths.unshift("All");
  self.filterLength = ko.observable("All");

  self.movies = ko.observableArray(data.movies);

  self.filteredMovies = ko.computed(function() {
    var filteredArray = ko.utils.arrayFilter(self.movies(), function(item) {
      return (
        (item.genre == self.filterGenre() || self.filterGenre() == "All") &&
        (item.language == self.filterLanguage() || self.filterLanguage() == "All") &&
        (item.length == self.filterLength() || self.filterLength() == "All")
      );
    });
    return filteredArray;
  });
};
var data = {
  genres: ["Drama", "Horror", "Sci-Fi"],
  languages: ["English", "Russian"],
  lengths: ["100", "120", "140", "160"],
  movies: [
    { name: "Godfather", genre: "Drama", language: "English", length: "160" },
    { name: "The shining", genre: "Horror", language: "English", length: "140"},
    { name: "Stalker", genre: "Sci-Fi", language: "Russian", length: "160" },
    { name: "Alien", genre: "Sci-Fi", language: "English", length: "120" },
    { name: "Russiam ark", genre: "Drama", language: "Russian", length: "100" },
    { name: "Psycho", genre: "Horror", language: "English", length: "120" }
  ]
};

var viewModel = new MoviesModel(data);
ko.applyBindings(viewModel);
table td {
  padding-right: 20px;
}
<table>
      <thead>
        <tr>
          <td>Movie name</td>
          <td>Genre</td>
          <td>Language</td>
          <td>Length</td>
        </tr>
        <tr>
          <td></td>
          <td>
            <select data-bind="options: Genres, value: filterGenre"></select>
          </td>
          <td>
            <select
              data-bind="options: Languages, value: filterLanguage"
            ></select>
          </td>
          <td>
            <select data-bind="options: Lengths, value: filterLength"></select>
          </td>
        </tr>
      </thead>

      <tbody data-bind="foreach: filteredMovies">
        <tr>
          <td data-bind="text: name"></td>
          <td data-bind="text: genre"></td>
          <td data-bind="text: language "></td>
          <td data-bind="text: length"></td>
        </tr>
      </tbody>
 </table>

<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

Filtering results is for a combination of values can be done by iterating over all the possible filters, apply the ones where the user selected something, and remove the items that do not fit the filter from the result. It does not matter in what order you apply the filters. The items have to fit all criteria to be a valid result, so you can throw them away as soon as the do not fit.

Here's some pseudo-code.

# item1:
# color: red
# price: 5
# language: EN

# item2:
# color: red
# price: 10
# language: RU

# item3:
# color: green
# price: 7
# language: DE


@items = ( item1, item2, item3 );
foreach filter in @selectedFilters {
  foreach item in @items {
    delete item from @items if filter.value != item.<filter.type>
    # or <, >, whatever
  }
}

return @items
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top