【问题标题】:Knockout JS View Refresh and FilteringKnockout JS 视图刷新和过滤
【发布时间】:2014-12-10 16:56:46
【问题描述】:

我对 Knockout 有点陌生,所以请耐心等待。我有一个数据表,我试图从表头部分的文本框中动态过滤。我只能在文本框中添加一个字符,然后过滤数据并且控件失去焦点。例如,我无法输入“XX”,因为当输入第一个“X”并且光标从文本框中移除时,过滤操作开始并完成。

这是我的灵感 - http://jsfiddle.net/Xpx7f/20/

这是标记

   // This where I'm filtering
   <thead>               
       <tr data-bind="foreach: columnNames">
           <td>
               // why isn't this "data-bind="textInput: filters"?
               // the observable name is filters not filter
               <input type='text' data-bind="textInput: filter" /> 
           </td>
       </tr>
   </thead>

   <tbody data-bind="foreach: enrollments">
       <tr>
          <td data-bind="text: WinId"></td>
          <td data-bind="text: EffectiveYear"></td>
          <td data-bind="text: FileKeeperGroupId"></td>                    
       </tr>
    </tbody>  

这是淘汰赛

   var viewModel = (function(){
            self = this;
            self.enrollments = ko.observableArray([]);  
            self.filters = ko.observableArray([]); 

            self.filteredItems = ko.computed(function () {                  
                var filter = self.filters(); 
                var enrollments = ko.utils.arrayFilter(self.enrollments(), function (item) {
                    for (var col in filter) {                    
                        var v = (item[col] || '').toString(); // column value
                        var f = filter[col]; // what's typed in header
                        if (v.lastIndexOf(f, 0) === 0) return true;
                    }
                    return false;
                });

                if(enrollments.length > 0)
                {
                    self.enrollments(enrollments);
                }
                else{
                    return self.enrollments();
                }
            });

            var subscriptions = [];
            self.columnNames = ko.computed(function () {
                ko.utils.arrayForEach(subscriptions, function (s) { s.dispose(); });
                subscriptions = [];
                if (self.enrollments().length === 0) return [];
                var props = [];
                var obj = self.enrollments()[0];
                for (var name in obj) {         
                    var p = { name: name, filter: ko.observable('') };
                    subscriptions.push(p.filter.subscribe(filterOnChanged, p));
                    props.push(p);
                }                      
                return props;
            });

            var filterOnChanged = function (value) {
                var filters = self.filters();
                filters[this.name] = value;            
                self.filters(filters);     
            };                
        });

        $(document).ready(function(){
            var vm = new viewModel();
            vm.getRecords();
            ko.applyBindings(viewModel);
        });

【问题讨论】:

  • self.columnNames -> 有什么理由让它被计算?
  • 它可以防止字段名称被硬编码,但我可以 100% 错误
  • 好吧,我没有看到您的 self.enrollments 的填充位置。据我了解,您根据注册中的第一个元素构建您的 columnNames。
  • 按照我在答案中建议的方式尝试一下,看看会发生什么。

标签: asp.net-mvc-4 knockout.js


【解决方案1】:

看起来这正在发生,因为您的 self.columnNames 是一个计算出来的 observable,它依赖于 self.enrollments observable。当应用过滤器时,self.enrollments observable 会发生变化。当你的 self.columnNames observable 得到更新时,重新绑定就会发生,一切都会重新渲染,你会失去焦点,因为 DOM 元素是新的。

我会移动逻辑以在单独的函数中构建列:

function initColumns () {
    ko.utils.arrayForEach(subscriptions, function (s) { s.dispose(); });
    subscriptions = [];
    if (self.enrollments().length === 0) return [];
    var props = [];
    var obj = self.enrollments()[0];
    for (var name in obj) {         
         var p = { name: name, filter: ko.observable('') };
         subscriptions.push(p.filter.subscribe(filterOnChanged, p));
         props.push(p);
     }                      
    return props;
});

然后在初始化 columnNames 可观察数组时调用此函数。

self.columnNames = ko.observableArray(initColumns());

否则你会陷入这个自我更新的循环地狱。

根据小提琴更新

好的,所以你需要做的是删除 self.filteredItems 并创建一个如下所示的计算 observable:

self.filteredEnrollments = ko.computed(function () {                  
    var filter = self.filters(); 
    return ko.utils.arrayFilter(self.enrollments(), function (item) {
        for (var col in filter) {                    
            var v = (item[col] || '').toString(); // column value
            var f = filter[col]; // what's typed in header
            if (v.lastIndexOf(f, 0) === 0) return true;
        }
        return false;
    });
});

比在您的 html 中,绑定到过滤的Enrollments 而不是注册。

<tbody data-bind="foreach: filteredEnrollments">
    <tr>
        <td data-bind="text: WinId"></td>
        <td data-bind="text: EffectiveYear"></td>
       <td data-bind="text: FileKeeperGroupId"></td>                    
    </tr>
</tbody>  

这样您就不会更改您的 enrollments 可观察数组,该数组不会触发 columnNames 计算以重新绑定 DOM。

【讨论】:

  • 我认为你的逻辑是正确的,但我无法让它发挥作用。文本框不存在,我的表没有记录。还有为什么我把它放在哪里很重要 - self.columnNames = ko.observableArray(initColumns());???如果我把它放在虚拟机的顶部,我会得到一个数组错误。
  • self.filteredItems中的filter变量为空所以返回false。
  • 你能创建jsfiddle吗?
  • 我应该可以,但我需要等到明天,因为它在代码中并不直截了当。
  • 我很难提取正确的代码和数据以放入小提琴中。然而,这个小提琴正是我想做的——jsfiddle.net/Xpx7f/20
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 2011-09-19
  • 2017-04-21
  • 2017-09-02
  • 2012-09-05
  • 2023-03-27
  • 2013-11-26
相关资源
最近更新 更多