我的Backbone应用程序中有一个树形视图,我使用嵌套的集合和模型:
集合:
define(function(require) {
var Backbone = require('backbone')
, UserListModel = require('app/models/userList');
return Backbone.Collection.extend({
model: UserListModel,
url: '/api/lists',
});
});
型号:
define(function(require) {
var Backbone = require('backbone');
return Backbone.Model.extend({
constructor: function(data, opts) {
opts = _.extend({}, opts, {parse: true});
var UserLists = require('app/collections/userLists');
this.children = new UserLists();
Backbone.Model.call(this, data, opts);
},
parse: function(data) {
if (_.isArray(data.children))
this.children.set(data.children);
return _.omit(data, 'chilren');
}
});
});
视图的一部分 :(完整视图:http://laravel.io/bin/O9oYX)
var UserListTreeItemView = Backbone.View.extend({
render: function() {
var data = this.model.toJSON();
data.hasChildren = !!this.model.get('isFolder');
this.$el.html(this.template(data));
if( this.model.get('isFolder') ) {
var list = new UserListTreeView({
collection: this.model.children
});
this.$el.append(list.render().el);
}
return this;
}
});
我使用两个视图将我的集合渲染为树视图。我想在我的树视图中添加搜索功能,我无法弄清楚如何。它应该能够在所有模型及其嵌套模型上搜索name
属性。
有什么想法吗?
答案 0 :(得分:1)
如果您已经在集合中拥有所需的模型,只需在集合本身上使用继承的Underscore方法filter()
即可。但它会返回一个模型数组,而不是Backbone Collection。
http://underscorejs.org/#filter
假设按属性name
进行过滤:
var nameToSearch = "whatever";
var itemsByName = this.model.children.filter(function(item){
return item.get("name").indexOf(nameToSearch) >=0;
}
我要做的是隔离您的getData
方法以涵盖这两种情况:过滤/关闭。
您没有指定如何搜索,但我猜您周围有文本输入,并且您想要使用该值。那只会搜索顶级项目吗?深度搜索会更复杂一些,涉及每个父项在其子项上查找名称。对于您将在每个文件夹中搜索文件的简单情况,请将搜索过滤器保留在父视图状态中。为此,我通常使用普通的香草Backbone模型,只是为了利用事件。
var MySearchView = Backbone.View.extend({
initialize: function(options){
//I like the idea of having a ViewModel to keep state
this.viewState = new Backbone.Model({
searchQuery: ""
});
//whenever the search query is changed, re-render
this.listenTo(this.viewState, "change:searchQuery", this.render);
},
events: {
"click .js-search-button": "doSearch"
},
doSearch: function(e){
e.preventDefault();
var query = this.$(".js-search-input").val();
this.viewState.set("seachQuery", query);
},
render: function(){
var data = this.model.toJSON();
data.hasChildren = !!this.model.get('isFolder');
this.$el.html(this.template(data));
if( this.model.get('isFolder') ) {
//be careful with this, you're not removing your child views ever
if(this._listView) {
this._listView.remove();
}
this._listView = new UserListTreeView({
collection: this.model.children,
**searchQuery: this.viewState.get("searchQuery")**
});
this.$el.append(this._listView.render().el);
}
return this;
}
});
现在在UserListTreeView
中,将模板的数据输入抽象为考虑搜索查询的方法:
var UserListTreeView = Backbone.View.extend({
initialize: function(options){
this.searchQuery = options.searchQuery || "";
},
...
getData: function(){
//filter your collection if needed
var query = this.searchQuery;
if(query !== ""){
return this.collection.filter(function(file){
return file.get("name").indexOf(query) >= 0;
}
else {
return this.collection.toJSON();
}
},
render: function() {
var items = this.getData(),
template = this.template(items);
this.$el.empty().append(template);
return this;
}
});
Voilá,相同的视图将呈现完整集合或其项目名称中包含searchQuery
的过滤版本。您可以通过更改filter
调用中的比较来调整搜索方法:您可以执行RegExp,仅搜索以(indexOf(searchQuery)== 0)开头的文件,依此类推。
比预期更长时间,希望它有所帮助。另一种选择是在集合本身中实现它,你可以覆盖它的toJSON()
方法来返回它上面的全部或一些项目。如果你发现自己编写了另一个需要filterint的视图,那么创建一个SearchableCollection并从那里继承它们可能是个更好的主意。保持干燥。 :)
作为旁注:您应该看看MarionetteJS或构建您自己的专业视图(收集等),以避免一遍又一遍地输入相同内容。
答案 1 :(得分:0)
我不确定我是否完全了解您的应用,但以下是我之前做过类似事情的方式:
在你的模型中添加:
matches: function(search) {
// a very simple and basic implementation
return this.get('name').indexOf(search) != -1;
}
并在UserListTreeView
的{{1}}:
render
非常简单但有效。这实际上是转移这个想法的最基本版本。您可以通过将其扩展到其他模型和集合,检查某些边缘情况并通过简单优化来提高其性能来改进此方法。