首页 > 解决方案 > 如何确定是什么触发了计算出的 observable 发生变化?淘汰赛JS

问题描述

问题概述

我很难确定是什么触发了计算出的 observable 发生变化。

我有一个下拉选择框,用于过滤表中显示的可观察数组。这是选择框:

筛选选择字段

这是我定义选择的方式:

// Javascript
let filters = ["All", "Pending", "Accepted", "Rejected", "Expired"];

// HTML
<select data-bind="options: filters, value: filter"> 
</select>

当用户选择一个值时,这个计算的 observable 用于过滤主数据数组:

self.filteredItems = ko.computed(function() {
        var filter = self.filter();
        if (!filter || filter == "All") {
            return self.quotes();
        } else {
            return ko.utils.arrayFilter(self.quotes(), function(i) {
                console.log(i);
                return i.status() == filter;
            });
        }
    });

如您所见,引号数组中的状态属性正在与下拉列表中的过滤器值进行比较。一切都很好。但现在我也在数组中添加关键字搜索(如下图所示):

关键词搜索

我将关键字文本字段绑定到一个可观察对象:

self.keyword = ko.observable();

修改代码以处理关键字搜索

我相信我可以通过更改以下内容轻松修改计算出的可观察代码以处理关键字搜索:

return i.status() == filter;

像这样的东西(与数组中的其他属性相比)

return i.fullName() == keyword || i.amount() == keyword || ..etc

我不知道如何确定是按过滤器下拉列表过滤数组,还是按关键字过滤。不知何故,我需要知道是什么触发了计算出的 observable 发生变化,不是吗?

有任何想法吗?

这是完整的视图模型:

// Quotes View Model

// +---------------------------------------------------------------------------+
// |  Quote View Model                                                         |
// |                                                                           |
// |  quotes-view-model.js                                                     |
// +---------------------------------------------------------------------------+
// |  Shows a list of all Quotes                                               |
// +---------------------------------------------------------------------------+/

let QuoteModel = function(id, quote_number, created_date, expiration_date, amount, client, status){

     this.id = ko.observable();
     this.quote_number = ko.observable(quote_number);
     this.created_date = ko.observable(created_date);
     this.expiration_date = ko.observable(expiration_date);
     this.amount = ko.observable(amount);
     this.client = ko.observable(client);
     this.status = ko.observable(status);

}



let ClientModel = function(id, fullName){
    this.id = ko.observable(id);
    this.fullName = ko.observable(fullName);
}

// Define Status Dropdown filters
let filters = ["All", "Pending", "Accepted", "Rejected", "Expired"];


function QuoteViewModel() {

    var self = this; // Scope Trick


    /* QUOTE Observables */
    self.quotes = ko.observableArray();
    self.clients = ko.observableArray();

    self.keyword = ko.observable();
    self.searchType = ko.observable();

    self.filters = ko.observableArray(filters);
    self.filter = ko.observable('');

    self.filteredItems = ko.computed(function() {
        var filter = self.filter();
        if (!filter || filter == "All") {
            return self.quotes();
        } else {
            return ko.utils.arrayFilter(self.quotes(), function(i) {
                console.log(i);
                return i.status() == filter;
            });
        }
    });

    /* GET PAGE DATA */

    /* CLIENTS */
           $.getJSON(apiCustomersAll,
            function(data) {
                var fullName;
                $.each(data,
                    function(key, val) {
                        fullName = val.first_name + " " + val.last_name;
                        self.clients.push(new ClientModel(val.id, fullName));
                    });
            });

          $.getJSON(apiQuotesAll,
            function(data) {
                var fullName;
                $.each(data,
                    function(key, val) {
                        fullName = self.getClientById(val.client_id);
                        console.log(`Full name is ${fullName}`);
                        self.quotes.push(new QuoteModel(val.id, 
                                                        val.quote_number, 
                                                        formatDate(val.created_date), 
                                                        formatDate(val.expiration_date), 
                                                        val.amount, 
                                                        fullName, 
                                                        val.status
                                                      ));
                    });
            });


        // Search Client Array, Return Full Name
        self.getClientById = function(id) {
            const client = self.clients().find(function(val){
                return val.id() == id;
            });

            if(client) {
                return client.fullName();   
            }

            return undefined;
            }



       self.search = function(){
         // to do
       }


}



ko.applyBindings(new QuoteViewModel());

标签: javascriptknockout.js

解决方案


仅使用一个文本框来允许自由文本搜索会给过滤器增加一些复杂性,我可以理解为什么您添加了searchTypeobservable。但是,您可以测试输入的文本是否可以被视为数量,然后使用此检查区分过滤器。我相信当您检查金额是否大于插入的值时,过滤器会更有用,而不是检查是否完全相等,取决于您。

此外:由于这些过滤器的组合将应用于两个实体(报价和客户),您还可以决定是否应将自由文本过滤器应用为两个数据集之间的交集联合。在下面的建议中,我再次过滤结果数据集(交集):

var quoteStatusOptions = ["All", "Pending", "Accepted", "Rejected", "Expired"];

var data = {
  quotes: [{id: 1, client: 1, amount: 300, status: "Pending"},{id: 2, client: 3, amount: 200, status: "Accepted"},{id: 3, client: 2, amount: 100, status: "Expired"}],
  clients: [{id: 1, fullName: "Leanne Graham"},{id: 2, fullName: "Ervin Howell"},{id: 3, fullName: "Clementine Bauch"}]
};

function QuoteModel(data) {
  this.id = ko.observable(data.id);
  this.client = ko.observable(data.client);
  this.amount = ko.observable(data.amount);
  this.status = ko.observable(data.status);
}

function ClientModel(data) {
  this.id = ko.observable(data.id);
  this.fullName = ko.observable(data.fullName);
}

function QuoteViewModel(data) {
  var self = this;
  self.quotes = ko.observableArray(ko.utils.arrayMap(data.quotes, function(quote) {
    return new QuoteModel(quote);
  }));
  self.quoteStatusOptions = ko.observableArray(quoteStatusOptions);
  self.quoteStatusFilter = ko.observable("");
  self.clients = ko.observableArray(ko.utils.arrayMap(data.clients, function(client) {
    return new ClientModel(client);
  }));
  self.searchTextFilter = ko.observable("");
  
  self.filteredQuotes = ko.computed(function() {
    var quotes = self.quotes(); /* Track quotes array changes */
    var quoteStatusFilter = self.quoteStatusFilter(); /* Track quote status filter changes */
    var clients = self.clients(); /* Track clients array changes */
    var searchTextFilter = self.searchTextFilter(); /* Track text filter changes */
    
    var isAmount = !isNaN(parseFloat(searchTextFilter)) && isFinite(searchTextFilter);
    
    var filteredQuotes = ko.utils.arrayFilter(quotes, function(quote) {
      var includeAll = !quoteStatusFilter || quoteStatusFilter === "All";
      var includeByStatus = includeAll || quote.status.peek() === quoteStatusFilter;
      var includeByAmount = parseFloat(quote.amount.peek()) >= parseFloat(searchTextFilter);
      return isAmount ? includeByStatus && includeByAmount : includeByStatus;
    });
    
    var filteredClients = ko.utils.arrayFilter(clients, function(client) {
      var includeAll = searchTextFilter === "";
      var includeByName = ~client.fullName.peek().toLowerCase().indexOf(searchTextFilter.toLowerCase());
      return isAmount ? true : includeAll || includeByName;
    });

    var result = [];
    for(var i=0, l=filteredQuotes.length; i<l; i++) {
      var clientId = filteredQuotes[i].id.peek();
      for(var j=0, k=filteredClients.length; j<k; j++) {
        var id = filteredClients[j].id.peek();
        if(clientId === id) {
          result.push(filteredQuotes[i]);
        }
      }
    }
    return result;
  });
  
  self.clientFullName = function(id) {
    var result = ko.utils.arrayFirst(self.clients(), function(client) {
      return client.id.peek() === id.peek();
    });
    return result.fullName.peek();
  }
}

$(document).ready(function() {
  ko.applyBindings(new QuoteViewModel(data));
});
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
  <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
</head>

<body>
  <div>
    <select data-bind="options: quoteStatusOptions, value: quoteStatusFilter"></select>
    <input type="text" data-bind="textInput: searchTextFilter">
  </div>
  <ul data-bind="foreach: filteredQuotes">
    <li>
      <span data-bind="text: status"> </span>
      <span data-bind="text: id"> </span>:
      <span data-bind="text: $parent.clientFullName(id)"> </span>
      $<span data-bind="text: amount"> </span>
    </li>
  </ul>
</body>

</html>


推荐阅读