backbone.js - 如何从点击事件中访问一个模型

9

我有一个包含CellModels的CellCollection的BoardView。我从数据库中获取了集合,然后创建了CellViews。

这一切都很顺利,直到我尝试通过在BoardView上点击事件访问CellModel。我根本无法访问底层模型...只有视图。有没有办法做到这一点?

我已经尝试包含下面相关的代码:

CellModel = Backbone.Model.extend({});

CellCollection = Backbone.Collection.extend({
    model : CellModel
});

CellView = Backbone.View.extend({
    className : 'cell',
});

BoardView = Backbone.View.extend({
    this.model.cells = new CellCollection();

    render : function() {
        this.cellList    = this.$('.cells');
        return this;
    },

    allCells : function(cells) {
        this.cellList.html('');
        this.model.cells.each(this.addCell);
        return this;
    },

    addCell : function(cell) {
        var view = new Views.CellView({
            model : cell
        }).render();

        this.cellList.append(view.el);
    },

    events : {
        'click .cell' : 'analyzeCellClick',
    },

    analyzeCellClick : function(e) {
        // ?????????
    }
});

我需要在BoardView上进行“点击”,而不是CellView,因为它涉及特定于面板的逻辑。

3个回答

12

很好的问题!我认为最好的解决方案是实施一个

EventBus(也称为事件分发器

来协调应用程序不同领域之间的所有事件。

采用这种方法看起来干净、松散耦合、易于实现、可扩展,并且实际上建议由backbone文档,详见Backbone文档

请还要阅读更多关于这个主题的信息herehere,因为(尽管我努力了)我的解释似乎有点平庸。

五步解释:

  1. Create an EventBus in your main or somewhere else as a util and include/require it

     var dispatcher = _.clone(Backbone.Events); // or _.extends 
    
  2. Add one or more callback hanlder(s) to it

     dispatcher.CELL_CLICK = 'cellClicked'
    
  3. Add a trigger to the Eventlistener of your childView (here: the CellView)

     dispatcher.trigger(dispatcher.CELL_CLICK , this.model);
    
  4. Add a Listener to the Initialize function of your parentView (here: the BoardView)

     eventBus.on(eventBus.CARD_CLICK, this.cardClick);
    
  5. Define the corresponding Callback within of your parentView (and add it to your _.bindAll)

     cellClicked: function(model) {
     // do what you want with your data here
     console.log(model.get('someFnOrAttribute')
     }
    

11
我可以想到至少两种方法来解决这个问题:
  1. 在初始化时将BoardView传递给CellView,然后在CellView中处理事件:

    var CellView = Backbone.View.extend({
        className : 'cell',
    
        initialize: function(opts) {
            this.parent = opts.parent
        },
    
        events : {
            'click' : 'analyzeCellClick',
        },
    
        analyzeCellClick : function() {
            // pass the relevant CellModel to the BoardView
            this.parent.analyzeCellClick(this.model);
        }
    });
    
    var BoardView = Backbone.View.extend({
        // ...
    
        addCell : function(cell) {
            var view = new Views.CellView({
                model : cell,
                parent : this
            }).render();
    
            this.cellList.append(view.el);
        },
    
        analyzeCellClick : function(cell) {
            // do something with cell
        }
    });
    

    这段代码是可以工作的,但我更喜欢避免视图相互调用方法,因为这会使它们之间的耦合度更高。

  2. 在渲染CellModel时,请将其id附加到DOM上:

  3. var CellView = Backbone.View.extend({
        className : 'cell',
    
        render: function() {
            $(this.el).data('cellId', this.model.id)
            // I assume you're doing other render stuff here as well
        }
    });
    
    var BoardView = Backbone.View.extend({
        // ...
    
        analyzeCellClick : function(evt) {
            var cellId = $(evt.target).data('cellId'),
                cell = this.model.cells.get(cellId);
            // do something with cell
        }
    });
    

    这种方法可能更清晰一些,因为它避免了上面提到的紧密耦合,但我认为任何一种方式都可以工作。


楼主在这里。两个解决方案都完美无缺地运行了... #1 对我的需求来说非常完美。非常感谢。 - cosmo_kramer
很好 - 如果您对答案满意,可以通过单击得分下面的复选框来接受它 :)。 - nrabinowitz
6
在我看来,方案2不是个好主意。Backbone的整个重点在于从服务器获取数据并在客户端视图中呈现它,而不是从视图获取数据并发送回去。虽然可以运作,但这并不是一个好的方式。 - Sơn Trần-Nguyễn

6
我会让 CellView 处理点击事件,但它只会触发一个 Backbone 事件:
var CellView = Backbone.View.extend({
    className : 'cell',

    initialize: function() {
        _.bindAll(this, 'analyzeCellClick');
    }

    events : {
        'click' : 'analyzeCellClick',
    },

    analyzeCellClick : function() {
        this.trigger('cellClicked', this.model);
    }
});

var BoardView = Backbone.View.extend({
    // ...

    addCell : function(cell) {
        var view = new Views.CellView({
            model : cell
        }).render();

        this.cellList.append(view.el);
        view.bind('cellClicked', function(cell) {
              this.analyzeCellClick(cell);
            };
    },

    analyzeCellClick : function(cell) {
        // do something with cell
    }
});

1
不错的解决方案..但是你的代码中有几个错误。
  1. view.bind('cellClicked' this.analyzeCellClick)
  2. 只需尝试分析analyzeCellClick中获取的上下文。它将是视图,因为此方法由cellview触发。因此,最好使用闭包或使用Function.bind。
- Sachin Jain
这种方法似乎更符合我对 Backbone 工作方式的理解。不知道我错了没? - b_dubb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接