BackboneJS重新排列集合中模型的最佳方式,同时为每个模型维护0索引的有序属性



我在这里遇到了一个问题,我有一个BackboneJS模型集合,每个模型都有一个"序数"属性,用于跟踪其在集合中的顺序。

这是我的游戏数据

var ex_group_test_data = [{
            title: 'PRE EXERCISE',
            id:         0,
            ordinal:    1,
            group_items: [{
                id:         0,
                ordinal:    0,
                title: 'item 1'
            },{
                id:         1,
                ordinal:    1,
                title: 'item 2'
            }]
        },{
            title: 'MAIN PART',
            id:         1,
            ordinal:    0,
            group_items: [{
                id:             2,
                ordinal:        0,
                title:          'item 3',
                description:    'testing descrip'
            },{
                id:         3,
                ordinal:    1,
                title: 'item 4'
            }]
        },{
            title: 'POST EXERCISE BS',
            id:         2,
            ordinal:    2,
            group_items: [{
                id:             2,
                ordinal:        0,
                title:          'item 5',
                description:    'testing descrip'
            },{
                id:         3,
                ordinal:    1,
                title: 'item 6'
            }]
        }];

这是我的主干系列的要点

        Collections.Exercise_Groups = Backbone.Collection.extend({
            model: Models.Exercise_Group,
            comparator: function(model){
                return model.get('ordinal');
            },
            initialize: function(){
                return this;
            }

简单地说,我希望能够获取一个模型,并将其按+1或-1的顺序移动,并保持集合中所有模型的0索引。

最终,我想把这一点提高到一个水平,在这个水平上,我可以放入模型或从任何位置删除它们,并且仍然保持我的0索引,或者取一个模型并将其移动+/-X位置。

有人推荐了实现这一目标的方法吗?

编辑1

我已经找到了一个解决方案,我可能想在明天真正睡一觉后对其进行优化。无论我是将模型相对于其原始位置向前还是向后移动,它都会维护集合中模型的"序数"的0索引。

编辑2呃,实际上它在边缘情况下有错误。

/**
             * Move model to a specified location. 
             * 
             * @param   int [model id]
             * @param   int [mew item position]
             * @return  this
             */
            move_to: function(m_id, new_pos){
                //keep within range
                if(new_pos < 0)
                    new_pos = 0;
                else if(new_pos > (this.length - 1))
                    new_pos = this.length - 1;

                var model = this.get(m_id),
                    old_pos = model.get('ordinal');

                model.set({
                    ordinal: new_pos
                });
                if(new_pos == old_pos){
                    //trigger associated events
                    this.sort();
                    return this;
                }
                //update indexes of affected models
                this.each(function(m){
                    //ordinal of current model in loop
                    var m_ordinal = m.get('ordinal');
                    //skip if this is the model we just updated
                    if(m.get('id') == m_id)
                        return;
                    if(old_pos < new_pos){
                        //moving down, ordinal is increasing
                        if(m_ordinal <= new_pos && m_ordinal != 0){
                            //this is in the range we care about
                            m.set({
                                ordinal: m.get('ordinal') - 1
                            });
                        }
                    }else if(old_pos > new_pos){
                        //moving up, ordinal is decreasing                  

                        if(m_ordinal >= new_pos && (m_ordinal != (this.length - 1))){
                            //this is in the range we care about
                            m.set({
                                ordinal: m.get('ordinal') + 1
                            });
                        }
                    }
                });
                this.sort();
                return this;
            }

编辑3好吧,我想我已经解决了所有的问题,一些简单的范围问题。以下是我已经非常彻底地测试过的一些代码,我相信它是有效的。

/**
                 * Move model to a specified location. 
                 * 
                 * @param   int [model id]
                 * @param   int [mew item position]
                 * @return  this
                 */
                move_to: function(m_id, new_pos){
                    //keep within range
                    if(new_pos < 0)
                        new_pos = 0;
                    else if(new_pos > (this.length - 1))
                        new_pos = this.length - 1;

                    var model = this.get(m_id),
                        old_pos = model.get('ordinal');
                    log('old_pos ' + old_pos);
                    log('new_pos ' + new_pos);

                    model.set({
                        ordinal: new_pos
                    });
                    if(old_pos == new_pos){
                        //trigger associated events
                        this.sort();
                        return this;
                    }
                    var _this = this;
                    //update indexes of affected models
                    this.each(function(m){
                        //ordinal of current model in loop
                        var m_ordinal = m.get('ordinal');
                        //skip if this is the model we just updated
                        if(m.get('id') == m_id)
                            return;
                        if(old_pos < new_pos){
                            //moving down, ordinal is increasing
                            if(m_ordinal <= new_pos && !(m_ordinal <= 0)){
                                //this is in the range we care about
                                m.set({
                                    ordinal: m.get('ordinal') - 1
                                });
                            }
                        }else if(old_pos > new_pos){
                            //moving up, ordinal is decreasing                  
                             log('p1');
                            if(m_ordinal >= new_pos && !(m_ordinal >= (_this.length - 1))){
                                //this is in the range we care about
                                m.set({
                                    ordinal: m.get('ordinal') + 1
                                });
                            }
                        }
                    });
                    this.sort();                    
                    return this;
                }

编辑4

发现另一个错误,进行了修补。

Backbone.Collection.prototype.move_to = function(m_id, new_pos) {
    //keep within range
    if(new_pos < 0)
        new_pos = 0;
    else if(new_pos > (this.length - 1))
        new_pos = this.length - 1;

    var model = this.get(m_id),
        old_pos = model.get('ordinal');
    log('old_pos ' + old_pos);
    log('new_pos ' + new_pos);
    model.set({
        ordinal: new_pos
    });
    if(old_pos == new_pos){
        //trigger associated events
        this.sort();
        return this;
    }
    var _this = this;
    //update indexes of affected models
    this.each(function(m){
        log(m.id);
        //ordinal of current model in loop
        var m_ordinal = m.get('ordinal');
        //skip if this is the model we just updated
        if(m.get('id') == m_id)
            return;
        if(old_pos < new_pos){
            //moving down, ordinal is increasing
            if(m_ordinal <= new_pos && m_ordinal >= old_pos && !(m_ordinal <= 0)){
                //this is in the range we care about
                m.set({
                    ordinal: m.get('ordinal') - 1
                }, {
                    silent: true
                });
            }
        }else if(old_pos > new_pos){
            //moving up, ordinal is decreasing                  
             log('p1');
            if(m_ordinal >= new_pos && m_ordinal <= old_pos && !(m_ordinal >= (_this.length - 1))){
                //this is in the range we care about
                m.set({
                    ordinal: m.get('ordinal') + 1
                }, {
                    silent: true
                });
            }
        }
    });
    this.sort();                    
    return this;
};

如果你查看backbone.js源代码,你会发现例如add方法支持使用将模型添加到某些索引中

collectionName.add(model, {at: index});

从位置移除可能需要对集合进行自定义功能,如:

// Your Collection
removeAt: function(options) {
  if (options.at) {
    this.remove(this.at(options.at));
  }
}

对于+1/-1,您可以制作一个自定义函数,并使用内置的下划线indexOf函数

// Your Collection
moveUp: function(model) { // I see move up as the -1
  var index = this.indexOf(model);
  if (index > 0) {
    this.remove(model, {silent: true}); // silence this to stop excess event triggers
    this.add(model, {at: index-1});
  }
}
moveDown: function(model) { // I see move up as the -1
  var index = this.indexOf(model);
  if (index < this.models.length) {
    this.remove(model, {silent: true}); // silence this to stop excess event triggers
    this.add(model, {at: index+1});
  }
}

通过这种方式,您还可以将moveUp和moveDown实现到模型本身,以获得更易于阅读的代码!

// Your Model
moveUp: function() {
  this.collection.moveUp(this);
}
// And the same for moveDown

但是现在索引属性没有保存在模型本身中。要读取索引,只需使用collection.indexOf(model),但如果您想始终将该信息存储在模型中,则可以绑定到addremove事件,以便在对集合进行更改时更新所有索引:

// Your collection
initialize: function(options?) {
  ...
  this.on('add remove', this.updateModelOrdinals);
  ...
},
...
updateModelOrdinals: function() {
  this.each(function(model, index) {
    this.model.set('ordinal', index);
  });
}

等等!现在,您应该拥有所需的功能,而无需重新设计轮子,并且仍然可以使用Backbone自己的功能保持0索引!很抱歉我有点忘乎所以,问我是否越过了你的头顶。阅读backbone.js的源代码,你可以在那里找到非常有用的东西!

希望这能有所帮助!

最新更新