knockout.js 中的多个过滤器

Posted

技术标签:

【中文标题】knockout.js 中的多个过滤器【英文标题】:Multiple filters in knockout.js 【发布时间】:2014-04-03 11:31:45 【问题描述】:

假设我有电影数组,如果我想按类型过滤,我可以这样做

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);

但是我目前遇到的问题是,如果我有类型、语言和长度的 html 下拉列表,我如何通过所有或部分过滤器有效地过滤电影,这样我就可以制作 90 分钟的俄罗斯动作片或动作片长度等?

【问题讨论】:

您需要查看所有过滤器选项并应用每个具有值的过滤器。过滤器的顺序无关紧要。我们的想法是从未过滤的结果池中删除所有内容,直到只剩下适合所有过滤器的结果。 【参考方案1】:

您需要根据提供的每个过滤器逐步构建过滤列表。至于过滤器本身,它们应该每个都由一个可观察对象表示,或者它们全部都在一个可观察数组中。这很重要,因为它会在您更改过滤器时触发计算更新。

例子:

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;
);

此代码将遍历每个过滤器,获取最新过滤的列表并仅保留满足所有条件的项目。

【讨论】:

【参考方案2】:

我知道这是老话题,但我最近遇到了同样的问题,这是我为您的电影模型修改的解决方案。你也可以在这里查看: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>

【讨论】:

【参考方案3】:

过滤结果是针对值的组合,可以通过迭代所有可能的过滤器来完成,应用用户选择的内容,并从结果中删除不适合过滤器的项目。应用过滤器的顺序无关紧要。这些项目必须符合所有标准才能成为有效结果,因此您可以在不符合的情况下立即丢弃它们。

这是一些伪代码。

# 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

【讨论】:

以上是关于knockout.js 中的多个过滤器的主要内容,如果未能解决你的问题,请参考以下文章

将多个输入绑定到可观察数组中的同一变量(Knockout.JS)

Knockout JS中的多个依赖选择

GroupBy,在 knockout.js 中过滤

Knockout JS 订阅 2 个 observables 的方式是,如果 2 个 observables 中的任何一个发生变化,我只会收到一次通知

多个布尔值上的 Knockout.js“如果绑定”

Knockout.js 文本绑定 - 折叠多个空格