KnockoutJs更新计数器后过滤器

时间:2015-04-18 03:04:58

标签: javascript knockout.js

我有很多过滤器:

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

现在,过滤和显示方面的一切都很有效。此外,当我应用FilterMessageTimeFilterCommentTime过滤器时,我会收到正确的消息和评论计数。

问题是: 当我应用FilterExcludeWithoutComments过滤器时,我收到的结果不一致。我试图解释......消息filteredMessagesTimeCount和评论filteredCommentsTimeCount计数器将显示先前选择的结果,但当前错误。这意味着如果我有4条消息和2条评论显示,filteredMessagesTimeCount将显示6(我做了这个),filteredCommentsTimeCount将显示4(我做了这个)......在NEXT过滤器迭代期间,即我再次切换过滤器,然后我会得到前一个选择的正确结果,即filteredMessagesTimeCount = 4和filteredCommentsTimeCount = 2.

由于我的计数器是一个'哑'的CSS元素计数器,执行状态非常重要...所以我认为发生的事情是计数器在元素发生变化(被隐藏)之前触发。

隐藏元素后,如何让计数器触发?这完全是错误的做事方式吗?

关于后者,我知道我可以使用pureComputed来返回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);
}}

// ViewModel Snippet

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

1 个答案:

答案 0 :(得分:1)

评论的过滤属于消息级别......问题是将过滤器缩小到那么远,这样您就可以正确使用它们而不使用全局变量或可怕的东西。

对于那种事情,我通常将'委托'传递给我的对象,指向根级别的观察者viewModel ...这是很好的OO练习,因为嵌套的Message对象应该对它所持有的容器一无所知(在这里抵制使用全局变量......总是很想用JS)。

一旦你完成了这个设置,消息的数量很容易(在根目录下发生)。总评论数是所有messages.filteredMessageComments的聚合计数,可以通过迭代在根计算完成。

以下是仅使用文本框的简化示例(即没有选项/下拉菜单),但应采用相同的方法。

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