javascript - 如何确定是什么触发了计算出的 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());
解决方案
仅使用一个文本框来允许自由文本搜索会给过滤器增加一些复杂性,我可以理解为什么您添加了searchType
observable。但是,您可以测试输入的文本是否可以被视为数量,然后使用此检查区分过滤器。我相信当您检查金额是否大于插入的值时,过滤器会更有用,而不是检查是否完全相等,取决于您。
此外:由于这些过滤器的组合将应用于两个实体(报价和客户),您还可以决定是否应将自由文本过滤器应用为两个数据集之间的交集或联合。在下面的建议中,我再次过滤结果数据集(交集):
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>
推荐阅读
- android - QR码扫描仪应用程序在牛轧糖中完美运行
- python-3.x - Keras 错误 - “我们希望张量具有静态批量大小”
- rust - 如何编写一个可以读取和写入缓存的 rust 函数?
- xcode - Xcode 项目中有两个相同的目录,但引用的是同一个文件。为什么有两个?
- c++ - 如何在没有新班级多米诺骨牌效应的情况下创建新班级以造福后代
- android - 有没有办法在 Flutter 中创建嵌套抽屉?
- python - 如何将用户输入的姓名和年龄列表排序为具有相应姓名的平均年龄、最高和最低年龄?
- android - 如何更改 Xamarin 项目的 Android 主题?
- github - GitHub搜索GUI中单引号和双引号有什么区别
- laravel - 处理 Lumen API 内存耗尽错误的最佳方法是什么