KnockoutJs 更新计数器 post 过滤器
KnockoutJs Updating Counters post Filter
我有多个过滤器:
FilterMessageTime = Filter on message time
FilterCommentTime = Filter on comment time
FilterCommentStatus = Filter on comment status
FilterExcludeWithoutComments = Filter messages without comments
使用这些过滤器(及其各种组合),我想保持对消息 filteredMessagesTimeCount
和评论 filteredCommentsTimeCount
的恒定计数。
我有以下看法:
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterMessageTime" data-bind="visible: selectedFilterMessageTime">Message Time: </label>
<select class="Filters" id="FilterMessageTime" title='Message Time' data-max-options="1"
data-bind="
options: FilterMessageTime,
optionsText : 'name',
optionsValue: 'name',
value: selectedFilterMessageTime
"></select>
</div>
<br />
<div data-bind="visible: $root.filteredMessagesTimeCount() > 0">
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterCommentTime" data-bind="visible: selectedFilterCommentTime">Comment Time: </label>
<select class="Filters" id="FilterCommentTime" title='Comment Time' data-max-options="1"
data-bind="
options: FilterCommentTime,
optionsText : 'name',
optionsValue : 'name',
value: selectedFilterCommentTime
"></select>
</div>
<br />
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterCommentStatus" data-bind="visible: selectedFilterCommentStatus">Comment Status: </label>
<select class="Filters" id="FilterCommentStatus" title='Status' data-max-options="1"
data-bind="
options: FilterCommentStatus,
optionsText : 'name',
optionsValue : 'name',
value: selectedFilterCommentStatus"></select>
</div>
<br />
</div>
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterExcludeWithoutComments" data-bind="visible: selectedFilterExcludeWithoutComments">Only show messages with comments: </label>
<select class="Filters" id="FilterExcludeWithoutComments" title='Only show messages with comments' data-max-options="1"
data-bind="
options: FilterExcludeWithoutComments,
optionsText : 'name',
optionsValue : 'name',
value: selectedFilterExcludeWithoutComments"></select>
</div>
<br />
</div>
<div id="allMessages" data-bind="foreach: filteredMessagesTime, updateCounters: filteredMessagesTime">
<div class="messageHolder" data-bind="visible: $root.showAllComments(MessageComments), afterRender: $root.updateMCCounters">
<div class="messageSection">
/*Message...*/
</div>
<div class="commentSection">
<ul class="ulIterator" data-bind="foreach: $root.filteredCommentsTime(MessageComments), updateCounters: $root.filteredCommentsTime(MessageComments)">
/*Comments...*/
</ul>
</div>
</div>
</div>
<div >
<p>
<!-- ko if: filteredMessagesTimeCount() == 1 -->
Total <span class="h4" data-bind="text: filteredMessagesTimeCount"></span> message
<!-- /ko -->
<!-- ko if: filteredMessagesTimeCount() > 1 -->
Total <span class="h4" data-bind="text: filteredMessagesTimeCount"></span> messages
<!-- /ko -->
<!-- ko ifnot: filteredMessagesTimeCount -->
No messages
<!-- /ko -->
<!-- ko ifnot: selectedFilterMessageTime() === 'Any' -->
in the <span class="h4" data-bind="text: selectedFilterMessageTimeOption().name"></span>.
<!-- /ko -->
<br />
<!-- ko if: filteredCommentsTimeCount() == 1 -->
Total <span class="h4" data-bind="text: filteredCommentsTimeCount"></span> comment
<!-- /ko -->
<!-- ko if: filteredCommentsTimeCount() > 1 -->
Total <span class="h4" data-bind="text: filteredCommentsTimeCount"></span> comments
<!-- /ko -->
<!-- ko ifnot: filteredCommentsTimeCount -->
No comments
<!-- /ko -->
<!-- ko ifnot: selectedFilterCommentTime() === 'Any' -->
in the <span class="h4" data-bind="text: selectedFilterCommentTimeOption().name"></span>.
<!-- /ko -->
</p>
</div>
附有以下JS:
self.FilterMessageTime = [
{ name: 'Any', include: /./, exclude: null },
{ name: 'Last Hour', include: /minutes?|hour/i, exclude: /hours|days?|weeks?|months?/i},
{ name: 'Last Day', include: /minutes?|hours?|day/i, exclude: /days|weeks?|months?/i},
{ name: 'Last Week', include: /minutes?|hours?|days?|week/i, exclude: /and|weeks?|months?/i },
{ name: 'Last Month', include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i}
];
self.FilterCommentTime = [
{ name: 'Any', include: /./, exclude: null },
{ name: 'Last Hour', include: /minutes?|hour/i, exclude: /hours|days?|weeks?|months?/i },
{ name: 'Last Day', include: /minutes?|hours?|day/i, exclude: /days|weeks?|months?/i },
{ name: 'Last Week', include: /minutes?|hours?|days?|week/i, exclude: /and|weeks?|months?/i },
{ name: 'Last Month',include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i }
];
self.FilterCommentStatus = [
{ id: 4, name: 'Any' },
{ id: 2, name: 'Read' },
{ id: 3, name: 'Unread' }
];
self.FilterExcludeWithoutComments = [
{ id: 1, name: 'No' },
{ id: 2, name: 'Yes' },
];
self.selectedFilterMessageTime = ko.observable(self.FilterMessageTime[0]);
self.selectedFilterMessageTimeOption = ko.computed(function () {
return ko.utils.arrayFirst(self.FilterMessageTime, function (item) {
return item.name === self.selectedFilterMessageTime();
});
});
self.selectedFilterCommentTime = ko.observable(self.FilterCommentTime[0]);
self.selectedFilterCommentTimeOption = ko.computed(function () {
return ko.utils.arrayFirst(self.FilterCommentTime, function (item) {
return item.name === self.selectedFilterCommentTime();
});
});
self.selectedFilterCommentStatus = ko.observable(self.FilterCommentStatus[0]);
self.selectedFilterExcludeWithoutComments = ko.observable(self.FilterCommentStatus[0]);
self.filteredMessagesTimeCount = ko.observable('0');
self.filteredCommentsTimeCount = ko.observable('0');
ko.bindingHandlers.updateCounters = {
update: function (element, valueAccessor) {
ko.utils.unwrapObservable(valueAccessor());
self.updateMCCounters();
}
}
self.updateMCCounters = function () {
//Messages count
self.filteredMessagesTimeCount($('.messageSection:visible').length);
//Comments count
self.filteredCommentsTimeCount($('.commentHolder:visible').length);
}
self.filteredMessagesTime = ko.pureComputed(function () {
return self.filterMessageTime(self.selectedFilterMessageTimeOption());
});
self.filterMessageTime = function (filter) {
var filterToReturn = ko.utils.arrayFilter(self.allMessages(), function (message) {
var d = message.MessageDate;
return filter.include && filter.include.test(d) &&
!(filter.exclude && filter.exclude.test(d));
});
return filterToReturn;
};
self.filteredCommentsTime = function (MessageComments) {
return self.filterCommentTime(self.selectedFilterCommentTimeOption(), MessageComments);
};
self.filterCommentTime = function (filter, MessageComments) {
var filterToReturn = ko.utils.arrayFilter(MessageComments(), function (comment) {
var d = comment.CommentDate;
return filter.include && filter.include.test(d) &&
!(filter.exclude && filter.exclude.test(d));
});
return filterToReturn;
};
self.filterCommentStatus = function (CommentReadAgent) {
if (self.selectedFilterCommentStatus() == null) {
return true;
}
else if (self.selectedFilterCommentStatus() == 'Any') {
$('.publishComment').fadeIn("slow");
$('.commentHolder').fadeIn("slow")
return true;
}
else if (self.selectedFilterCommentStatus()) {
if (self.selectedFilterCommentStatus() == 'Read') {
if (CommentReadAgent() == true) {
$('.publishComment').fadeIn("slow");
$('.commentHolder').fadeIn("slow");
return true;
}
else
return false;
}
else if (self.selectedFilterCommentStatus() == 'Unread') {
if (CommentReadAgent() == false) {
$('.publishComment').fadeIn("slow");
$('.commentHolder').fadeIn("slow");
return true;
}
else
return false;
}
}
return false;
};
self.showAllComments = function (MessageComments) {
if (self.selectedFilterExcludeWithoutComments() == 'Yes') {
if (self.filteredCommentsTime(MessageComments).length > 0) {
return true;
}
else {
return false;
}
}
else {
return true;
}
};
现在,过滤和显示方面的一切都很好用。此外,当我应用 FilterMessageTime
或 FilterCommentTime
过滤器时,我收到了正确的消息和评论数。
问题是:
当我应用 FilterExcludeWithoutComments
过滤器时,我收到不一致的结果。我试图解释...消息 filteredMessagesTimeCount
和评论 filteredCommentsTimeCount
计数器将显示先前选择的结果,但当前是错误的。这意味着如果我有 4 条消息和 2 条评论显示,filteredMessagesTimeCount
将显示 6(我编造的)并且 filteredCommentsTimeCount
将显示 4(我编造的)......在 NEXT 过滤器迭代期间,即我再次切换过滤器,然后我将获得先前选择的正确结果,即 filteredMessagesTimeCount
= 4 和 filteredCommentsTimeCount
= 2。
由于我的计数器是一个 'dumb' CSS 元素计数器,执行状态非常重要......所以我认为正在发生的事情是计数器在元素之前触发已更改(已隐藏)。
如何在元素被隐藏后触发计数器?这是完全错误的做事方式吗?
关于后者,我知道我可以使用 pureComputed
到 return filteredMessagesTimeCount
,即:
self.filteredMessagesCount = ko.pureComputed(function () {
return self.filteredMessages().length;
});
我可以对其他三个过滤器做类似的事情吗?
如果你做到了这一步,我感谢你,欢迎任何反馈。
:)
**更新,我的 .js 设置示例
//型号
function Message(data) {
var self = this;
data = data || {};
self.MessageComments = ko.observableArray([]);
if (data.MessageComments) {
var mappedComments = $.map(data.MessageComments, function (item) { return new Comment(item); });
self.MessageComments(mappedComments);
}}
function viewModel() {
var self = this;
//As per my original post, the JS 'section'
//goes here with the addition of how my messages are loaded updated below
self.loadMessages = function () {
var token = $("input[name='__RequestVerificationToken']").val();
var headers = {};
headers['__RequestVerificationToken'] = token;
return $.ajax({
url: messageUrl,
dataType: "json",
contentType: "application/json",
cache: false,
type: 'GET',
headers: headers,
async: false,
})
.done(function (data) {
var mappedMessages = $.map(data, function (dataItem) {
return new Message(dataItem);
});
self.messages(mappedMessages);
})
.fail(function () {
self.error('unable to load messages');
});
}}
更新
function Message(data, commentFilterTimeDelegate) {
var self = this;
data = data || {};
self.MessageComments = ko.observableArray([]);
self.filteredCommentsTime = ko.computed(function () {
var filter = commentFilterTimeDelegate;
var filterToReturn = ko.utils.arrayFilter(self.MessageComments(), function (comment) {
var d = comment.CommentDate;
return filter.include && filter.include.test(d) &&
!(filter.exclude && filter.exclude.test(d));
});
return filterToReturn;
});
if (data.MessageComments) {
var mappedComments = $.map(data.MessageComments, function (item) { return new Comment(item); });
self.MessageComments(mappedComments);
}}
//视图模型片段
.done(function (data) {
var mappedMessages = $.map(data, function (dataItem) {
//return new Message(dataItem);
return new Message(dataItem, self.selectedFilterCommentTime());
});
self.messages(mappedMessages);
})
更新
剃须刀
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterCommentTime" data-bind="visible: selectedFilterCommentTime">Comment Time: </label>
<select class="Filters" id="FilterCommentTime" title='Comment Time' data-max-options="1"
data-bind="
options: FilterCommentTime,
optionsText : 'name',
optionsValue : 'name',
value: selectedFilterCommentTime
"></select>
</div>
...
<ul class="ulIterator" data-bind="foreach: filteredCommentsTime">
JS
function Message(data, commentFilterTimeDelegate) {
...
self.FilterCommentTime = [
{ name: 'Any', include: /./, exclude: null },
{ name: 'Last Hour', include: /minutes?|hour/i, exclude: /hours|days?|weeks?|months?/i },
{ name: 'Last Day', include: /minutes?|hours?|day/i, exclude: /days|weeks?|months?/i },
{ name: 'Last Week', include: /minutes?|hours?|days?|week/i, exclude: /and|weeks?|months?/i },
{ name: 'Last Month', include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i }
];
self.filteredCommentsTime = ko.computed(function () {
var test = commentFilterTimeDelegate();
var filter = ko.utils.arrayFirst(self.FilterCommentTime, function (item) {
return item.name === commentFilterTimeDelegate().name;
});
var filterToReturn = ko.utils.arrayFilter(self.MessageComments(), function (comment) {
var d = comment.CommentDate;
return filter.include && filter.include.test(d) &&
!(filter.exclude && filter.exclude.test(d));
});
return filterToReturn;
});
}
function viewModel() {
...
self.FilterCommentTime = [
{ name: 'Any', include: /./, exclude: null },
{ name: 'Last Hour', include: /minutes?|hour/i, exclude: /hours|days?|weeks?|months?/i },
{ name: 'Last Day', include: /minutes?|hours?|day/i, exclude: /days|weeks?|months?/i },
{ name: 'Last Week', include: /minutes?|hours?|days?|week/i, exclude: /and|weeks?|months?/i },
{ name: 'Last Month', include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i }
];
self.selectedFilterCommentTime = ko.observable(self.FilterCommentTime[0]);
...
.done(function (data) {
var mappedMessages = $.map(data, function (dataItem) {
//return new Message(dataItem);
return new Message(dataItem, self.selectedFilterCommentTime);
});
self.messages(mappedMessages);
})
...
}
评论的过滤属于消息级别...问题是将过滤器降低到如此程度,以便您可以正确使用它们而无需使用全局变量或其他糟糕的东西。
对于那种事情,我通常将 'delegates' 传递到我的对象中,该对象指向根级别 viewModel 的可观察对象...这是很好的 OO 实践,因为嵌套的 Message 对象应该对容器一无所知它被保存在(抵制在这里使用全局变量......总是用 JS 诱惑)。
完成该设置后,消息计数就很容易了(发生在根目录)。总评论数是所有 messages.filteredMessageComments 的 聚合 计数,可以通过迭代在根部计算完成。
这是一个仅使用文本框(即没有 options/dropdowns)的简化示例,但应采用相同的方法。
** FIDDLE: **
http://jsfiddle.net/brettwgreen/mh1qax40/
** HTML: **
Name Filter: <input type="text" data-bind="value: MessageNameFilter">
Comment Filter: <input type="text" data-bind="value: MessageCommentFilter">
<br />
Message Count: <div data-bind="text: FilteredMessageCount" ></div>
Comment Count: <div data-bind="text: FilteredCommentCount" ></div>
<br />
<div data-bind="foreach: FilteredMessages">
<div data-bind="text: MessageName"></div>
<div data-bind="foreach: FilteredMessageComments">
<div data-bind="text: Comment" style="padding-left: 10px;"></div>
</div>
</div>
JS:
var MessagesData = [
{
MessageName: 'Message One',
MessageComments: [
{Comment: 'Comment One'},
{Comment: 'Comment Two'},
{Comment: 'Comment Three'}
]
},
{
MessageName: 'Message Two',
MessageComments: [
{Comment: 'Comment One'},
{Comment: 'Comment Two'},
{Comment: 'Comment Three'}
]
},
{
MessageName: 'Message Three',
MessageComments: [
{Comment: 'Comment One'},
{Comment: 'Comment Two'},
{Comment: 'Comment Three'}
]
}];
var MessageComment = function(msgComment) {
var self = this;
self.Comment = ko.observable(msgComment.Comment);
};
var Message = function(msg, commentFilter) {
var self = this;
self.MessageName = ko.observable(msg.MessageName);
self.MessageComments = ko.observableArray();
$.each(msg.MessageComments, function(i, m) {
self.MessageComments.push(new MessageComment(m));
});
self.FilteredMessageComments = ko.computed(function() {
var results = [];
$.each(self.MessageComments(), function(i, mc){
// using the injected comment filter function (an observable)
// filter the comments accordingly
if (mc.Comment().indexOf(commentFilter()) !== -1){
results.push(mc);
}
});
return results;
});
};
var vm = function(messages) {
var self = this;
self.MessageNameFilter = ko.observable('');
self.MessageCommentFilter = ko.observable('');
self.Messages = ko.observableArray();
$.each(messages, function(i, m) {
// inject the comment filter so we can filter comments
// inside the messages object
self.Messages.push(new Message(m, self.MessageCommentFilter));
});
self.FilteredMessages = ko.computed(function() {
var results = [];
$.each(self.Messages(), function(i, m){
if (m.MessageName().indexOf(self.MessageNameFilter()) !== -1){
results.push(m);
}
});
return results;
});
// This is easy, just count your filtered messages:
self.FilteredMessageCount = ko.computed(function() {
return self.FilteredMessages().length;
});
// For this one, iterate over the filtered messages and count
// the filtered comments
self.FilteredCommentCount = ko.computed(function() {
var val = 0;
$.each(self.FilteredMessages(), function(i, m){
val += m.FilteredMessageComments().length;
});
return val;
});
};
var vm = new vm(MessagesData);
ko.applyBindings(vm);
我有多个过滤器:
FilterMessageTime = Filter on message time
FilterCommentTime = Filter on comment time
FilterCommentStatus = Filter on comment status
FilterExcludeWithoutComments = Filter messages without comments
使用这些过滤器(及其各种组合),我想保持对消息 filteredMessagesTimeCount
和评论 filteredCommentsTimeCount
的恒定计数。
我有以下看法:
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterMessageTime" data-bind="visible: selectedFilterMessageTime">Message Time: </label>
<select class="Filters" id="FilterMessageTime" title='Message Time' data-max-options="1"
data-bind="
options: FilterMessageTime,
optionsText : 'name',
optionsValue: 'name',
value: selectedFilterMessageTime
"></select>
</div>
<br />
<div data-bind="visible: $root.filteredMessagesTimeCount() > 0">
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterCommentTime" data-bind="visible: selectedFilterCommentTime">Comment Time: </label>
<select class="Filters" id="FilterCommentTime" title='Comment Time' data-max-options="1"
data-bind="
options: FilterCommentTime,
optionsText : 'name',
optionsValue : 'name',
value: selectedFilterCommentTime
"></select>
</div>
<br />
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterCommentStatus" data-bind="visible: selectedFilterCommentStatus">Comment Status: </label>
<select class="Filters" id="FilterCommentStatus" title='Status' data-max-options="1"
data-bind="
options: FilterCommentStatus,
optionsText : 'name',
optionsValue : 'name',
value: selectedFilterCommentStatus"></select>
</div>
<br />
</div>
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterExcludeWithoutComments" data-bind="visible: selectedFilterExcludeWithoutComments">Only show messages with comments: </label>
<select class="Filters" id="FilterExcludeWithoutComments" title='Only show messages with comments' data-max-options="1"
data-bind="
options: FilterExcludeWithoutComments,
optionsText : 'name',
optionsValue : 'name',
value: selectedFilterExcludeWithoutComments"></select>
</div>
<br />
</div>
<div id="allMessages" data-bind="foreach: filteredMessagesTime, updateCounters: filteredMessagesTime">
<div class="messageHolder" data-bind="visible: $root.showAllComments(MessageComments), afterRender: $root.updateMCCounters">
<div class="messageSection">
/*Message...*/
</div>
<div class="commentSection">
<ul class="ulIterator" data-bind="foreach: $root.filteredCommentsTime(MessageComments), updateCounters: $root.filteredCommentsTime(MessageComments)">
/*Comments...*/
</ul>
</div>
</div>
</div>
<div >
<p>
<!-- ko if: filteredMessagesTimeCount() == 1 -->
Total <span class="h4" data-bind="text: filteredMessagesTimeCount"></span> message
<!-- /ko -->
<!-- ko if: filteredMessagesTimeCount() > 1 -->
Total <span class="h4" data-bind="text: filteredMessagesTimeCount"></span> messages
<!-- /ko -->
<!-- ko ifnot: filteredMessagesTimeCount -->
No messages
<!-- /ko -->
<!-- ko ifnot: selectedFilterMessageTime() === 'Any' -->
in the <span class="h4" data-bind="text: selectedFilterMessageTimeOption().name"></span>.
<!-- /ko -->
<br />
<!-- ko if: filteredCommentsTimeCount() == 1 -->
Total <span class="h4" data-bind="text: filteredCommentsTimeCount"></span> comment
<!-- /ko -->
<!-- ko if: filteredCommentsTimeCount() > 1 -->
Total <span class="h4" data-bind="text: filteredCommentsTimeCount"></span> comments
<!-- /ko -->
<!-- ko ifnot: filteredCommentsTimeCount -->
No comments
<!-- /ko -->
<!-- ko ifnot: selectedFilterCommentTime() === 'Any' -->
in the <span class="h4" data-bind="text: selectedFilterCommentTimeOption().name"></span>.
<!-- /ko -->
</p>
</div>
附有以下JS:
self.FilterMessageTime = [
{ name: 'Any', include: /./, exclude: null },
{ name: 'Last Hour', include: /minutes?|hour/i, exclude: /hours|days?|weeks?|months?/i},
{ name: 'Last Day', include: /minutes?|hours?|day/i, exclude: /days|weeks?|months?/i},
{ name: 'Last Week', include: /minutes?|hours?|days?|week/i, exclude: /and|weeks?|months?/i },
{ name: 'Last Month', include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i}
];
self.FilterCommentTime = [
{ name: 'Any', include: /./, exclude: null },
{ name: 'Last Hour', include: /minutes?|hour/i, exclude: /hours|days?|weeks?|months?/i },
{ name: 'Last Day', include: /minutes?|hours?|day/i, exclude: /days|weeks?|months?/i },
{ name: 'Last Week', include: /minutes?|hours?|days?|week/i, exclude: /and|weeks?|months?/i },
{ name: 'Last Month',include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i }
];
self.FilterCommentStatus = [
{ id: 4, name: 'Any' },
{ id: 2, name: 'Read' },
{ id: 3, name: 'Unread' }
];
self.FilterExcludeWithoutComments = [
{ id: 1, name: 'No' },
{ id: 2, name: 'Yes' },
];
self.selectedFilterMessageTime = ko.observable(self.FilterMessageTime[0]);
self.selectedFilterMessageTimeOption = ko.computed(function () {
return ko.utils.arrayFirst(self.FilterMessageTime, function (item) {
return item.name === self.selectedFilterMessageTime();
});
});
self.selectedFilterCommentTime = ko.observable(self.FilterCommentTime[0]);
self.selectedFilterCommentTimeOption = ko.computed(function () {
return ko.utils.arrayFirst(self.FilterCommentTime, function (item) {
return item.name === self.selectedFilterCommentTime();
});
});
self.selectedFilterCommentStatus = ko.observable(self.FilterCommentStatus[0]);
self.selectedFilterExcludeWithoutComments = ko.observable(self.FilterCommentStatus[0]);
self.filteredMessagesTimeCount = ko.observable('0');
self.filteredCommentsTimeCount = ko.observable('0');
ko.bindingHandlers.updateCounters = {
update: function (element, valueAccessor) {
ko.utils.unwrapObservable(valueAccessor());
self.updateMCCounters();
}
}
self.updateMCCounters = function () {
//Messages count
self.filteredMessagesTimeCount($('.messageSection:visible').length);
//Comments count
self.filteredCommentsTimeCount($('.commentHolder:visible').length);
}
self.filteredMessagesTime = ko.pureComputed(function () {
return self.filterMessageTime(self.selectedFilterMessageTimeOption());
});
self.filterMessageTime = function (filter) {
var filterToReturn = ko.utils.arrayFilter(self.allMessages(), function (message) {
var d = message.MessageDate;
return filter.include && filter.include.test(d) &&
!(filter.exclude && filter.exclude.test(d));
});
return filterToReturn;
};
self.filteredCommentsTime = function (MessageComments) {
return self.filterCommentTime(self.selectedFilterCommentTimeOption(), MessageComments);
};
self.filterCommentTime = function (filter, MessageComments) {
var filterToReturn = ko.utils.arrayFilter(MessageComments(), function (comment) {
var d = comment.CommentDate;
return filter.include && filter.include.test(d) &&
!(filter.exclude && filter.exclude.test(d));
});
return filterToReturn;
};
self.filterCommentStatus = function (CommentReadAgent) {
if (self.selectedFilterCommentStatus() == null) {
return true;
}
else if (self.selectedFilterCommentStatus() == 'Any') {
$('.publishComment').fadeIn("slow");
$('.commentHolder').fadeIn("slow")
return true;
}
else if (self.selectedFilterCommentStatus()) {
if (self.selectedFilterCommentStatus() == 'Read') {
if (CommentReadAgent() == true) {
$('.publishComment').fadeIn("slow");
$('.commentHolder').fadeIn("slow");
return true;
}
else
return false;
}
else if (self.selectedFilterCommentStatus() == 'Unread') {
if (CommentReadAgent() == false) {
$('.publishComment').fadeIn("slow");
$('.commentHolder').fadeIn("slow");
return true;
}
else
return false;
}
}
return false;
};
self.showAllComments = function (MessageComments) {
if (self.selectedFilterExcludeWithoutComments() == 'Yes') {
if (self.filteredCommentsTime(MessageComments).length > 0) {
return true;
}
else {
return false;
}
}
else {
return true;
}
};
现在,过滤和显示方面的一切都很好用。此外,当我应用 FilterMessageTime
或 FilterCommentTime
过滤器时,我收到了正确的消息和评论数。
问题是:
当我应用 FilterExcludeWithoutComments
过滤器时,我收到不一致的结果。我试图解释...消息 filteredMessagesTimeCount
和评论 filteredCommentsTimeCount
计数器将显示先前选择的结果,但当前是错误的。这意味着如果我有 4 条消息和 2 条评论显示,filteredMessagesTimeCount
将显示 6(我编造的)并且 filteredCommentsTimeCount
将显示 4(我编造的)......在 NEXT 过滤器迭代期间,即我再次切换过滤器,然后我将获得先前选择的正确结果,即 filteredMessagesTimeCount
= 4 和 filteredCommentsTimeCount
= 2。
由于我的计数器是一个 'dumb' CSS 元素计数器,执行状态非常重要......所以我认为正在发生的事情是计数器在元素之前触发已更改(已隐藏)。
如何在元素被隐藏后触发计数器?这是完全错误的做事方式吗?
关于后者,我知道我可以使用 pureComputed
到 return filteredMessagesTimeCount
,即:
self.filteredMessagesCount = ko.pureComputed(function () {
return self.filteredMessages().length;
});
我可以对其他三个过滤器做类似的事情吗?
如果你做到了这一步,我感谢你,欢迎任何反馈。
:)
**更新,我的 .js 设置示例
//型号
function Message(data) {
var self = this;
data = data || {};
self.MessageComments = ko.observableArray([]);
if (data.MessageComments) {
var mappedComments = $.map(data.MessageComments, function (item) { return new Comment(item); });
self.MessageComments(mappedComments);
}}
function viewModel() {
var self = this;
//As per my original post, the JS 'section'
//goes here with the addition of how my messages are loaded updated below
self.loadMessages = function () {
var token = $("input[name='__RequestVerificationToken']").val();
var headers = {};
headers['__RequestVerificationToken'] = token;
return $.ajax({
url: messageUrl,
dataType: "json",
contentType: "application/json",
cache: false,
type: 'GET',
headers: headers,
async: false,
})
.done(function (data) {
var mappedMessages = $.map(data, function (dataItem) {
return new Message(dataItem);
});
self.messages(mappedMessages);
})
.fail(function () {
self.error('unable to load messages');
});
}}
更新
function Message(data, commentFilterTimeDelegate) {
var self = this;
data = data || {};
self.MessageComments = ko.observableArray([]);
self.filteredCommentsTime = ko.computed(function () {
var filter = commentFilterTimeDelegate;
var filterToReturn = ko.utils.arrayFilter(self.MessageComments(), function (comment) {
var d = comment.CommentDate;
return filter.include && filter.include.test(d) &&
!(filter.exclude && filter.exclude.test(d));
});
return filterToReturn;
});
if (data.MessageComments) {
var mappedComments = $.map(data.MessageComments, function (item) { return new Comment(item); });
self.MessageComments(mappedComments);
}}
//视图模型片段
.done(function (data) {
var mappedMessages = $.map(data, function (dataItem) {
//return new Message(dataItem);
return new Message(dataItem, self.selectedFilterCommentTime());
});
self.messages(mappedMessages);
})
更新
剃须刀
<div style="display: inline-block">
<label class="LabelDropdownPopup" for="FilterCommentTime" data-bind="visible: selectedFilterCommentTime">Comment Time: </label>
<select class="Filters" id="FilterCommentTime" title='Comment Time' data-max-options="1"
data-bind="
options: FilterCommentTime,
optionsText : 'name',
optionsValue : 'name',
value: selectedFilterCommentTime
"></select>
</div>
...
<ul class="ulIterator" data-bind="foreach: filteredCommentsTime">
JS
function Message(data, commentFilterTimeDelegate) {
...
self.FilterCommentTime = [
{ name: 'Any', include: /./, exclude: null },
{ name: 'Last Hour', include: /minutes?|hour/i, exclude: /hours|days?|weeks?|months?/i },
{ name: 'Last Day', include: /minutes?|hours?|day/i, exclude: /days|weeks?|months?/i },
{ name: 'Last Week', include: /minutes?|hours?|days?|week/i, exclude: /and|weeks?|months?/i },
{ name: 'Last Month', include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i }
];
self.filteredCommentsTime = ko.computed(function () {
var test = commentFilterTimeDelegate();
var filter = ko.utils.arrayFirst(self.FilterCommentTime, function (item) {
return item.name === commentFilterTimeDelegate().name;
});
var filterToReturn = ko.utils.arrayFilter(self.MessageComments(), function (comment) {
var d = comment.CommentDate;
return filter.include && filter.include.test(d) &&
!(filter.exclude && filter.exclude.test(d));
});
return filterToReturn;
});
}
function viewModel() {
...
self.FilterCommentTime = [
{ name: 'Any', include: /./, exclude: null },
{ name: 'Last Hour', include: /minutes?|hour/i, exclude: /hours|days?|weeks?|months?/i },
{ name: 'Last Day', include: /minutes?|hours?|day/i, exclude: /days|weeks?|months?/i },
{ name: 'Last Week', include: /minutes?|hours?|days?|week/i, exclude: /and|weeks?|months?/i },
{ name: 'Last Month', include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i }
];
self.selectedFilterCommentTime = ko.observable(self.FilterCommentTime[0]);
...
.done(function (data) {
var mappedMessages = $.map(data, function (dataItem) {
//return new Message(dataItem);
return new Message(dataItem, self.selectedFilterCommentTime);
});
self.messages(mappedMessages);
})
...
}
评论的过滤属于消息级别...问题是将过滤器降低到如此程度,以便您可以正确使用它们而无需使用全局变量或其他糟糕的东西。
对于那种事情,我通常将 'delegates' 传递到我的对象中,该对象指向根级别 viewModel 的可观察对象...这是很好的 OO 实践,因为嵌套的 Message 对象应该对容器一无所知它被保存在(抵制在这里使用全局变量......总是用 JS 诱惑)。
完成该设置后,消息计数就很容易了(发生在根目录)。总评论数是所有 messages.filteredMessageComments 的 聚合 计数,可以通过迭代在根部计算完成。
这是一个仅使用文本框(即没有 options/dropdowns)的简化示例,但应采用相同的方法。
** FIDDLE: **
http://jsfiddle.net/brettwgreen/mh1qax40/
** HTML: **
Name Filter: <input type="text" data-bind="value: MessageNameFilter">
Comment Filter: <input type="text" data-bind="value: MessageCommentFilter">
<br />
Message Count: <div data-bind="text: FilteredMessageCount" ></div>
Comment Count: <div data-bind="text: FilteredCommentCount" ></div>
<br />
<div data-bind="foreach: FilteredMessages">
<div data-bind="text: MessageName"></div>
<div data-bind="foreach: FilteredMessageComments">
<div data-bind="text: Comment" style="padding-left: 10px;"></div>
</div>
</div>
JS:
var MessagesData = [
{
MessageName: 'Message One',
MessageComments: [
{Comment: 'Comment One'},
{Comment: 'Comment Two'},
{Comment: 'Comment Three'}
]
},
{
MessageName: 'Message Two',
MessageComments: [
{Comment: 'Comment One'},
{Comment: 'Comment Two'},
{Comment: 'Comment Three'}
]
},
{
MessageName: 'Message Three',
MessageComments: [
{Comment: 'Comment One'},
{Comment: 'Comment Two'},
{Comment: 'Comment Three'}
]
}];
var MessageComment = function(msgComment) {
var self = this;
self.Comment = ko.observable(msgComment.Comment);
};
var Message = function(msg, commentFilter) {
var self = this;
self.MessageName = ko.observable(msg.MessageName);
self.MessageComments = ko.observableArray();
$.each(msg.MessageComments, function(i, m) {
self.MessageComments.push(new MessageComment(m));
});
self.FilteredMessageComments = ko.computed(function() {
var results = [];
$.each(self.MessageComments(), function(i, mc){
// using the injected comment filter function (an observable)
// filter the comments accordingly
if (mc.Comment().indexOf(commentFilter()) !== -1){
results.push(mc);
}
});
return results;
});
};
var vm = function(messages) {
var self = this;
self.MessageNameFilter = ko.observable('');
self.MessageCommentFilter = ko.observable('');
self.Messages = ko.observableArray();
$.each(messages, function(i, m) {
// inject the comment filter so we can filter comments
// inside the messages object
self.Messages.push(new Message(m, self.MessageCommentFilter));
});
self.FilteredMessages = ko.computed(function() {
var results = [];
$.each(self.Messages(), function(i, m){
if (m.MessageName().indexOf(self.MessageNameFilter()) !== -1){
results.push(m);
}
});
return results;
});
// This is easy, just count your filtered messages:
self.FilteredMessageCount = ko.computed(function() {
return self.FilteredMessages().length;
});
// For this one, iterate over the filtered messages and count
// the filtered comments
self.FilteredCommentCount = ko.computed(function() {
var val = 0;
$.each(self.FilteredMessages(), function(i, m){
val += m.FilteredMessageComments().length;
});
return val;
});
};
var vm = new vm(MessagesData);
ko.applyBindings(vm);