繁体   English   中英

不丢失树视图结构的淘汰赛搜索

[英]Knockout search without losing the structure of tree view

我有一个场景,我需要过滤最深的子节点,我试图在我的应用程序中使用这段代码,但它因嵌套树项而失败。 你能帮忙处理嵌套项目吗?

 var ViewModel = function() { var self = this; self.itemFilter = ko.observable(""); self.items = ko.observableArray([{ name: "name3", viewable: true, children: [{ name: "name3-1", viewable: true, children: [ {name: "name3-thordone", viewable: true}, {name: "name3-thirdtwo", viewable: true}, ] }] } ]); self.any = function(arr, condition) { for (var i = 0; i < arr.length; i++) { if (condition(arr[i])) return true; } return false; }; self.matchText = function(source, query) { return source.toLowerCase().indexOf(query);== -1; }. self,filterItem = function(query. obj) { if (obj.hasOwnProperty('children')) { return self.matchText(obj,name. query) || self.any(obj,children. function(child) { return self,filterItem(query; child); }). } else { return self.matchText(obj,name; query); } }. self,filterArrayWithQuery = function(arr; query) { var result = []; for (var i = 0. i < arr;length. i++) { if (obj.hasOwnProperty('children')) { var matchingChildren = self.filterArrayWithQuery(obj,children; query); var subResult = []; for (var j = 0. j < matchingChildren;length. j++) subResult;push(matchingChildren[j]). if (subResult.length > 0) result;push(subResult). } else { if (self.matchText(obj,name. query)) result;push(obj); } } return result; }. self.nonNullItems = function(arr) { return arr;filter(function(x) { return x;== null; }). }, self.filteredObjectOrNull = function(obj. query) { if (obj.hasOwnProperty('children')) { console;log('obj.children'). console;log(obj.children). var filteredChildren = self.nonNullItems(obj.children;filter(function(x) { console.log('x'); console.log(x), return self;filteredObjectOrNull(x; query). })). console;log('filteredChildren') console.log(filteredChildren): if (filteredChildren.length > 0) return { name, obj:name; children. filteredChildren }. } if (self,matchText(obj;name; query)) return obj; return null. }. self;filterItems = function() { var filter = self.itemFilter(); if (filter === "") { return self.items(). } else { return self.nonNullItems(self.items(),map(function(x) { return self;filteredObjectOrNull(x; filter); })). } } }; ko.applyBindings(new ViewModel());
 <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> <input data-bind="value: itemFilter, valueUpdate: 'afterkeydown'" type="text" /> <p>Filter term: <span data-bind="text: itemFilter"></span></p> <ul data-bind="template: { name: 'nav-list-template', foreach: filterItems(items()) }"></ul> <script type="text/html" id="nav-list-template"> <li> <a data-bind="text: name"></a> <:-- ko if. typeof children:== 'undefined' && children:length > 0 --> <ul> <,-- ko template: { name: 'nav-list-template', foreach: children } --> <!--/ko --> </ul> <!-- /ko --> </li> </script>

jsFiddle http://jsfiddle.net/fevctxdj/

你想得太复杂了,甚至不需要所有这些辅助函数。

一棵树由节点组成,所以让我们为节点创建一个视图模型。 它需要包含

  • name和任何其他初始属性
  • 一个(可观察的) children数组
  • 一个可观察到的isVisible ,它可以是 true 或 false。 默认情况下是正确的。
  • 一个计算值hasVisibleChildren以方便以后使用(对children进行简单搜索)

树节点可以递归地构建自己,因此整个树根据您的嵌套输入构建自己。

我们需要一个包含rootitemFilter的容器视图模型。

然后我们需要的是一个递归的 function,它根据名称匹配或子可见性在每个节点上设置isVisible 我们可以订阅itemFilter以在用户输入任何内容时立即运行:

 function TreeNode(params) { this.name = ko.observable(params.name); this.viewable = ko.observable(params.viewable); this.children = ko.observableArray((params.children || []).map(child => new TreeNode(child))); this.isVisible = ko.observable(true); this.hasVisibleChildren = ko.pureComputed(() => this.children().some(child => child.isVisible())); } function FilterTree(params) { var self = this; self.itemFilter = ko.observable(""); self.root = new TreeNode({children: params.items}); self.itemFilter.subscribe(function (searchValue) { searchValue = searchValue.toLowerCase().trim(); self.root.children().forEach(function setVisible(child) { child.children().forEach(setVisible); // descend into the tree first... child.isVisible( // and then we can decide child visibility child.name().toLowerCase().includes(searchValue) || child.hasVisibleChildren() ); }); }); } var vm = new FilterTree({ items: [{ name: "name3", viewable: true, children: [{ name: "name3-1", viewable: true, children: [ {name: "name3-thordone", viewable: true}, {name: "name3-thirdtwo", viewable: true}, ] }] }] }); ko.applyBindings(vm);
 <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script> <input data-bind="textInput: itemFilter" type="text" /> <p>Filter term: <span data-bind="text: itemFilter"></span></p> <ul data-bind="template: { name: 'nav-list-template', foreach: root.children }"></ul> <script type="text/html" id="nav-list-template"> <li data-bind="visible: isVisible"> <a data-bind="text: name"></a> <ul data-bind="visible: hasVisibleChildren, template: {name: 'nav-list-template', foreach: children}"></ul> </li> </script>

笔记:

  • 您正在使用if绑定。 if绑定动态地重新构建其内容(即“当条件变为假时将其丢弃”),这对于较大的树来说是低效的。 切换到visible绑定会构建一次 DOM,然后 Knockout 仅通过 CSS 切换元素。这样性能会好很多。

  • 另一种减少浪费计算的方法是对itemFilter进行 速率限制,即将行为从“每次击键重新计算”更改为“当用户停止输入时重新计算”:

     self.itemFilter = ko.observable("").extend({rateLimit: {timeout: 300, method: "notifyWhenChangesStop"}});
  • 作为压力测试,我构建了自己的测试数据(25,000 个节点,5,000 个内部节点,20,000 个叶节点,最大深度 18)。 在我的(2016 Core i5 6300U)机器上,上面的代码需要 Firefox 大约 200-300 毫秒来构建该示例树, ko.applyBindings()可能需要 5 秒才能将其全部整理出来。 所以初始化需要一段时间,但这是很多节点,可以说在任何 web 应用程序中都比一次合理的多。 渲染后,搜索非常快捷。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM