【问题标题】:How to filter/search a nested backbone collection?如何过滤/搜索嵌套的主干集合?
【发布时间】:2014-09-09 13:46:07
【问题描述】:

我的 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 属性。

有什么想法吗?

【问题讨论】:

标签: javascript backbone.js filter


【解决方案1】:

如果您的收藏中已经有您想要的模型,只需在收藏本身上使用继承的下划线方法filter()。不过,它将返回一个模型数组,而不是 Backbone 集合。

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

瞧,同一个视图将呈现完整集合或过滤版本,其项目的名称中包含searchQuery。您可以通过更改 filter 调用中的比较来调整搜索方法:您可以使用 RegExp,仅搜索以 (indexOf(searchQuery) == 0) 开头的文件,等等。

花费的时间比预期的要长,希望对您有所帮助。另一种选择是在集合本身中实现这一点,您可以覆盖其toJSON() 方法以返回全部或部分项目。如果您发现自己编写了另一个需要 filterint 的视图,那么创建 SearchableCollection 并从那里继承两者可能是一个更好的主意。保持干燥。 :)

附带说明:您应该看看 MarionetteJS 或构建自己的专用视图(集合等),以免一遍又一遍地输入相同的内容。

【讨论】:

    【解决方案2】:

    我不确定我是否完全理解了您的应用,但以下是我之前做过类似事情的方式:

    在你的模型中添加这个:

    matches: function(search) {
        // a very simple and basic implementation
        return this.get('name').indexOf(search) != -1;
    }
    

    并在UserListTreeViewrender中使用它:

    render: function() {
        var search = $someElement.val();
        var _this = this;
        _.each(this.collection.models, function(model) {
            if (model.matches(search)) {
                _this.addItem(model);
            }
        });
        return this;
    }
    

    非常简单,但有效。这其实是最基础的思想转移版本。您可以通过将其扩展到其他模型和集合、检查某些极端情况以及通过简单优化来提高其性能来改进此方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-13
      • 2021-03-02
      • 1970-01-01
      • 2014-12-24
      相关资源
      最近更新 更多