Backbone.js - доступ к модели из события click

У меня есть BoardView, содержащий CellCollection для CellModels. Я беру коллекцию из db, а затем создаю CellViews.

Все это работает плавно, пока я не попытаюсь получить доступ к CellModel через событие click на BoardView. Я вообще не могу добраться до базовых моделей... только взглядов. Есть ли способ сделать это?

Я попытался включить соответствующий код ниже:

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, потому что он включает логику, специфичную для платы.

Ответ 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
        }
    });
    

    Это сработает, но я предпочитаю, чтобы представления не вызывали друг друга, поскольку они делают их более тесно связанными.

  • Прикрепите идентификатор CellModel к DOM при его рендеринге:

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

    Это, вероятно, немного чище, потому что он избегает тесной связи, упомянутой выше, но я думаю, что любой из них будет работать.

Ответ 2

Хороший вопрос! Я думаю, лучшим решением было бы реализовать

EventBus aka EventDispatcher

для координации всех событий между различными областями вашего приложения.

Переход на этот маршрут кажется чистым, слабо связанным, легко реализуемым, расширяемым и фактически предложенной базовой документацией, см. Backbone Docs

Пожалуйста, также прочитайте здесь здесь и здесь, потому что (даже хотя я и старался), мое собственное объяснение кажется мне посредственным.

Пятиступенчатое объяснение:

  • Создайте EventBus в своем основном или где-то еще в качестве утилиты и включите/требуйте

     var dispatcher = _.clone(Backbone.Events); // or _.extends 
    
  • Добавьте к нему один или несколько hanlder (s) обратных вызовов

     dispatcher.CELL_CLICK = 'cellClicked'
    
  • Добавьте триггер в Eventlistener вашего childView (здесь: CellView)

     dispatcher.trigger(dispatcher.CELL_CLICK , this.model);
    
  • Добавить прослушиватель в функцию Initialize вашего parentView (здесь: BoardView)

     eventBus.on(eventBus.CARD_CLICK, this.cardClick);
    
  • Определите соответствующий обратный вызов внутри вашего родительского элемента (и добавьте его в свой _.bindAll)

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

Ответ 3

Я бы позволил CellView обработать событие click, но он просто вызовет событие 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
    }
});