javascript - KnockoutJS 清除函数内的 Observable

标签 javascript knockout.js

我的 Razor View 如下:

<div id="allMessages" data-bind="foreach: messages">
    <!-- ko if: $root.filterMessageTime($data.MessageDate) -->
           //Show some messages
    <!-- /ko -->
</div>
<div data-bind="visible: lastHourCount() > 1 && selectedFilterMessageTime() == 'Last Hour'">
    <p><span class="h4" data-bind="text: lastHourCount()"></span> messages in the <span class="h4">last hour</span>.</p>
</div>
<div data-bind="visible: lastHourCount() == 0 && selectedFilterMessageTime() == 'Last Hour'">
    <p>No new messages in the <span class="h4">last hour</span>.</p>
</div>

我的 knockout 功能如下:

function ViewModel() {
    var self = this;
    var count = 0;

    self.lastHourCount = ko.observable(0);
    self.lastDayCount = ko.observable(0);
    self.lastWeekCount = ko.observable(0);
    self.lastMonthCount = ko.observable(0);

    self.filterMessageTime = function (MessageDate) {
        //Reset Expression
        if (count == self.messages().length) {
            count = 0;
            self.lastHourCount(0);
            self.lastDayCount(0);
            self.lastWeekCount(0);
            self.lastMonthCount(0);
        }
        if (self.selectedFilterMessageTime() == 'Any' || self.selectedFilterMessageTime() == null) {
            return true;
        }
        count = count + 1;
        if (self.selectedFilterMessageTime() == 'Last Hour') {
            if (((MessageDate.indexOf('minute') >= 0) || (MessageDate.indexOf('hour') >= 0)) && (MessageDate.indexOf('hours') == -1)) {
                self.lastHourCount(self.lastHourCount() + 1);
                return true;
            }
            else
                return false;
        }
        else if (self.selectedFilterMessageTime() == 'Last Day') {
            if (((MessageDate.indexOf('day') >= 0) || (MessageDate.indexOf('hours') >= 0) || (MessageDate.indexOf('hour') >= 0) || (MessageDate.indexOf('minute') >= 0)) && (MessageDate.indexOf('days') == -1)) {
                self.lastDayCount(self.lastDayCount() + 1);
                return true;
            }
            else
                return false;
        }
        else if (self.selectedFilterMessageTime() == 'Last Week') {
            if ((MessageDate.indexOf('days') >= 0) || (MessageDate.indexOf('day') >= 0) || (MessageDate.indexOf('hours') >= 0) || (MessageDate.indexOf('hour') >= 0) || (MessageDate.indexOf('minute') >= 0)) {
                self.lastWeekCount(self.lastWeekCount() + 1);
                return true;
            }
        else
                return false;
        }
        else if (self.selectedFilterMessageTime() == 'Last Month') {
            if ((MessageDate.indexOf('month') >= 0) || (MessageDate.indexOf('days') >= 0) || (MessageDate.indexOf('day') >= 0) || (MessageDate.indexOf('hours') >= 0) || (MessageDate.indexOf('hour') >= 0) || (MessageDate.indexOf('minute') >= 0)) {
                self.lastMonthCount(self.lastMonthCount() + 1);
                return true;
            }
            else
                return false;
        }
        return false;
    };
}

方法论:

对于我拥有的每条用户消息,如果用户指定过滤器,则:

  1. 根据传入的 MessageDate 计算该过滤器范围内的消息数量,并将它们放在可观察对象中,即 self.lastMonthCount() 将保存该金额上个月的消息数
  2. 计数达到消息总数时,我们希望重置计数以及可观察计数器,以便可观察计数不会超过任何值。

问题:

似乎我对我的可观察量有某种形式的循环依赖,因为运行函数时存在问题,即“重置”表达式永远无法正确计算,并且似乎函数循环的数量很大(大于Observables 中的消息数量。

注释:

  • 当用常规变量替换可观察量时,一切正常,当然我认为没有数据绑定(bind),这是不好的。
  • 如果我在重置表达式中重新声明我的 Observables,那么它会解决我的问题,但我之前绑定(bind)的 Observables 将不会从新创建的 Observables 接收更新,同样没有好处。
  • 我还尝试将 Observables 设置为 nullundefinied

最佳答案

你的做法是错误的。

如果您有一个包含对象列表的 View 模型,并且希望使它们可过滤,那么您需要做的就是

  • 添加一个保存事件过滤条件的可观察对象
  • 添加一个计算来保存过滤后的列表
  • 您的 View 完全基于过滤列表

剩下的就自然而然了。

function ViewModel() {
    var self = this;

    self.availableFilters = ['any', 'last hour', 'last day', 'last week', 'last month'];
    self.activeFilter = ko.observable(self.availableFilters[0]);
    self.allMessages = ko.observableArray([/* ... fill this ... */]);

    self.filteredMessages = ko.pureComputed(function () {
        return self.filterMessages(self.activeFilter());
    });
    self.filteredMessagesCount = ko.pureComputed(function () {
        return self.filteredMessages().length;
    });

    self.filterMessages = function (filter) {
        return ko.utils.arrayFilter(self.allMessages(), function (message) {
            var d = message.Date();
            switch (filter) {
                case 'any':        return true;
                case 'last hour':  return (d.indexOf('minute') >= 0 || d.indexOf('hour') >= 0) && d.indexOf('hours') == -1;
                case 'last day':   return (d.indexOf('day') >= 0 || d.indexOf('hours') >= 0 || d.indexOf('hour') >= 0 || d.indexOf('minute') >= 0) && d.indexOf('days') == -1;
                case 'last week':  return d.indexOf('days') >= 0 || d.indexOf('day') >= 0 || d.indexOf('hours') >= 0 || d.indexOf('hour') >= 0 || d.indexOf('minute') >= 0;
                case 'last month': return d.indexOf('month') >= 0 || d.indexOf('days') >= 0 || d.indexOf('day') >= 0 || d.indexOf('hours') >= 0 || d.indexOf('hour') >= 0 || d.indexOf('minute') >= 0;
                default:           return false;
            }
        });
    };
}

<select data-bind="options: availableFilters, value: activeFilter"></select>

<div id="allMessages" data-bind="foreach: filteredMessages">
    // show the message
</div>
<div>
    <p>
        <!-- ko if: filteredMessagesCount -->
        <span class="h4" data-bind="text: filteredMessagesCount"></span> messages
        <!-- /ko -->

        <!-- ko ifnot: filteredMessagesCount -->
        No new messages
        <!-- /ko -->

        <!-- ko ifnot: activeFilter() === 'any' -->
        in the <span class="h4" data-bind="text: activeFilter"></span>
        <!-- /ko -->
    </p>
</div>
<小时/>

话虽如此,您过滤消息的方法(通过多个 indexOf() 调用)并不是很漂亮。

基于正则表达式的怎么样:

function ViewModel() {
    var self = this;

    self.availableFilters = [
        {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: /weeks|months?/i},
        {name: 'last month', include: /minutes?|hours?|days?|weeks?|month/i, exclude: /months/i}
    ];
    self.activeFilter = ko.observable(self.availableFilters[0]);
    self.allMessages = ko.observableArray([/* ... fill this ... */]);

    self.filteredMessages = ko.pureComputed(function () {
        return self.filterMessages(self.activeFilter());
    });
    self.filteredMessagesCount = ko.pureComputed(function () {
        return self.filteredMessages().length;
    });

    self.filterMessages = function (filter) {
        return ko.utils.arrayFilter(self.allMessages(), function (message) {
            var d = message.Date();
            return filter.include && filter.include.test(d) &&
                   !(filter.exclude && filter.exclude.test(d));
        });
    };
}

<select 
    data-bind="options: availableFilters, optionsText: 'name', value: activeFilter"
></select>

View 的其余部分是相同的,除了对 activeFilter() 的引用必须更改为 activeFilter().name

关于javascript - KnockoutJS 清除函数内的 Observable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29232230/

相关文章:

javascript - 浏览器中的子弹图 - 替代 d3.js? (具有良好的浏览器支持)

javascript - 关系优先查询

javascript - 使用javascript将两个图像加载到html

javascript - 如何读取以整数为键的对象?

javascript - Bootstrap 日期选择器的问题

javascript - 敲: Click event not firing in some situations

javascript - Knockout.js 中的队列计时器

javascript - JQuery 即使在文档准备好后也没有选择元素

javascript - Chart.js - 如何将标签值显示为 X 和 Y 值的百分比 - 目前始终为 100%

asp.net-mvc - knockout 模板中的js字符串格式索引