9

Мой взгляд, TuneBook, имеет несколько видов детей вида ClosedTune. У меня также есть отдельные полные просмотры страниц для каждой мелодии, OpenTune. Те же события связаны между собой в ClosedTune и OpenTune, поэтому я разработал свое приложение так, чтобы они оба наследовали от общего «абстрактного» вида Tune.Передача событий родительскому представлению в магистрали

Чтобы сделать мое приложение более масштабируемым Я хотел бы событие для каждого ClosedTune быть делегированы TuneBook, но ремонтопригодность я хотел бы одни и те же обработчики (те, хранящиеся в Tune), которые будут использоваться TuneBook (хотя они очевидно, необходимо обернуть в какую-то функцию).

Проблема заключается в том, что в пределах TuneBook, найти правильный ClosedTune для вызова обработчика. Что хорошего в архитектуре, или есть другие хорошие решения для делегирования событий родительскому представлению?

Примечание - не дубликат Backbone View: Inherit and extend events from parent (который о детях, наследующих от родительского класса, в то время как я спрашиваю про детей, которые являются дочерними узлами родителя в DOM)

+0

Если события будут выполнены обработчиками в модели 'Tune', то вы хотите сделать обход' ChildView -> ParentView -> Model' .. вы можете выполнить 'Model.handler' непосредственно из' ChildView' .. В другой руке вы можете использовать «Модель» в качестве посланника: вы можете изменить состояние модели из ChildView и позволить ParentView прослушивать эту модификацию. – fguillen

+0

@fguillen. Менее эффективно связывать события (возможно, я должен понимать, что я имею в виду события DOM, а не настраиваемые события Backbone) на узел DOM каждого дочернего представления, а не только один раз на узел DOM родительского представления. На таблице 300 строк, сгенерированной моей производительностью приложения, заметно вяло при привязке непосредственно к каждой строке. Мне нужен «ParentView» -> [childView] -> childModel' – wheresrhys

ответ

10

В вашем родительском представлении (распространяется также от Backbone.Events), я бы привязал onEvent к событию DOM. В триггере он запускает базовое событие, включающее в себя некоторый атрибут «id», который знает ваш ребенок (предположительно некоторый идентификатор строки?).

var TuneBook = Backbone.View.extend(_.extend({}, Backbone.Events, { 
    events: { 
     "click .tune .button": "clickHandler" 
    }, 
    clickHandler: function (ev) { 
     this.trigger('buttonClick:' + ev.some_id_attr, ev); 
    }, 

})); 

Воспроизведение детей затем естественно подпишится на родительское представление, которое их касается. Ниже я делаю это в initialize, передавая родительский вид, а также тот специальный атрибут id, который вы использовали ранее в options.

var ClosedTune = Backbone.View.extend({ 

    initialize: function (options) { 
     options.parent.on('buttonClick:' + options.id, this.handler, this); 
    }, 

    handler: function (ev) { 
     ... 
    }, 

}); 

Конечно, Вы можете также создать аналогичные абонентов на Tune или OpenTune.

+2

Этот подход использует родительское представление как «агрегатор событий», который является хорошим шаблоном для последующего использования. Кроме того, вы также можете создать отдельный объект, который является вашим агрегатором событий, и передать его вашим представлениям. Затем вашим детям не нужно проходить DOM, чтобы найти подходящий объект для прослушивания событий. Для получения дополнительной информации об агрегаторах событий см. Эту статью: http://lostechies.com/derickbailey/2012/04/03/revisiting-the-backbone-event-aggregator-lessons-learned/ –

+0

Это выглядит идеально. Попробуем это сегодня вечером – wheresrhys

+0

Я реализовал это почти точно, с единственным изменением, которое я приложил к слушателям событий «ClosedTune» в методе addOne() «TuneBook», чтобы: a) избежать необходимости передавать ссылки на родителя; b) разрешить использование из 'ClosedTune' в других контекстах – wheresrhys

7

Вот пара возможностей.

1. Централизованные: хранить ClosedTune объекты в TuneBook например

  1. хранить ссылку на каждый ClosedTune в tune_book.tunes. Как вы заполняете tune_book.tunes зависит от вас; так как вы упомянули метод сумматора на TuneBook, вот что я проиллюстрировал ниже.

  2. В обработчике TuneBook событий, извлекать ClosedTune из tune_book.tunes, используя что-то вроде атрибута id мишени события в качестве ключа. Затем вызовите обработчик Tune или ClosedTune.

http://jsfiddle.net/p5QMT/1/

var Tune = Backbone.View.extend({ 
    className: "tune", 

    click_handler: function (event) { 
    event.preventDefault(); 
    console.log(this.id + " clicked"); 
    }, 

    render: function() { 
    this.$el.html(
     '<a href="" class="button">' + this.id + '</a>' 
    ); 

    return this; 
    } 
}); 

var ClosedTune = Tune.extend({}); 

var OpenTune = Tune.extend({ 
    events: { 
    "click .button" : 'click_handler' 
    } 
}); 

var TuneBook = Backbone.View.extend({ 
    events: { 
    "click .tune .button" : 'click_handler' 
    }, 

    click_handler: function (event) { 
    var tune = this.options.tunes[ 
     $(event.target).closest(".tune").attr('id') 
    ]; 

    tune.click_handler(event); 
    }, 

    add_tune: function (tune) { 
    this.options.tunes[tune.id] = tune; 
    this.$el.append(tune.render().el); 
    }, 

    render: function() { 
    $("body").append(this.el); 
    return this; 
    } 
}); 

var tune_book = new TuneBook({ 
    tunes: {} 
}); 

[1, 2, 3].forEach(function (number) { 
    tune_book.add_tune(new ClosedTune({ 
    id: "closed-tune-" + number 
    })); 
}); 

tune_book.render(); 

var open_tune = new OpenTune({ 
    id: "open-tune-1" 
}); 

$("body").append(open_tune.render().el); 

2. Децентрализованная: связать объект вида с объектом DOM с помощью jQuery.data()

  1. При создании ClosedTune, сохраните ссылку на него, например, this.$el.data('view_object', this).

  2. В случае прослушивания прослушивания, найдите ClosedTune, например. $(event.target).data('view_object').

Вы можете использовать тот же самый точный обработчик для ClosedTuneTuneBook) и OpenTune, если вы хотите.

http://jsfiddle.net/jQZNF/1/

var Tune = Backbone.View.extend({ 
    className: "tune", 

    initialize: function (options) { 
    this.$el.data('view_object', this); 
    }, 

    click_handler: function (event) { 
    event.preventDefault(); 

    var tune = 
     $(event.target).closest(".tune").data('view_object'); 

    console.log(tune.id + " clicked"); 
    }, 

    render: function() { 
    this.$el.html(
     '<a href="" class="button">' + this.id + '</a>' 
    ); 

    return this; 
    } 
}); 

var ClosedTune = Tune.extend({ 
    initialize: function (options) { 
    this.constructor.__super__.initialize.call(this, options); 
    } 
}); 

var OpenTune = Tune.extend({ 
    events: { 
    "click .button" : 'click_handler' 
    } 
}); 

var TuneBook = Backbone.View.extend({ 
    events: { 
    "click .tune .button": Tune.prototype.click_handler 
    }, 

    add_tune: function (tune) { 
    this.$el.append(tune.render().el); 
    }, 

    render: function() { 
    $("body").append(this.el); 
    return this; 
    } 
}); 

var tune_book = new TuneBook({ 
    tunes: {} 
}); 

[1, 2, 3].forEach(function (number) { 
    tune_book.add_tune(new ClosedTune({ 
    id: "closed-tune-" + number 
    })); 
}); 

tune_book.render(); 

var open_tune = new OpenTune({ 
    id: "open-tune-1" 
}); 

$("body").append(open_tune.render().el); 

Ответ на комментарий

я считал вариант 1, но решил против него, как у меня уже есть коллекция настраивать модели в tunebook и не хотел другой объект I» d необходимо сохранить в синхронизации

Я думаю, это зависит от того, какое домашнее хозяйство/синхронизация вы чувствуете необходимость и почему.

(например, в TuneModel.remove() мне нужно, чтобы удалить вид из списка tunebook о виде ... наверное нужно событие, чтобы сделать это, так что это событие только решение начинает выглядеть более привлекательным).

Почему вы чувствуете, что «нужно удалить представление из списка tunebook по взглядам»? (Я не предлагаю, чтобы вы этого не сделали, просто спросите, почему вы хотите.) Так как вы делаете, как вы думаете, подход @ ggozad отличается в этом отношении?

Оба метода хранят ClosedTune объектов в экземпляре TuneBook. В технике @ ggozad это просто скрыто за абстракцией, которая, возможно, делает ее менее очевидной для вас.

В моем примере они хранятся в простом объекте JS (tune_book.tunes). В @ ggozad они хранятся в структуре _callbacks, используемой Backbone.Events.

Добавление ClosedTune:

1.

this.options.tunes[tune.id] = tune; 

2.

this.on('buttonClick:' + tune.id, tune.handler, tune); 

Если вы хотите избавиться от ClosedTune (скажем, удалить его из документа с tune.remove() и вы хотите, чтобы объект вида полностью исчез), используя подход @ ggozad, оставит осиротую ссылку на ClosedTune в tune_book._callbacks, если не выполнять такую ​​же домашнем хозяйстве, что будет иметь смысл с подходом, я предложил:

1.

delete this.options.tunes[tune.id]; 

tune.remove(); 

2.

this.off("buttonClick:" + tune.id); 

tune.remove(); 

Первая строка каждого примера необязательно - в зависимости от того, хотите ли вы очистить объекты ClosedTune или нет.

Вариант 2 - это более или менее то, что я делаю прямо сейчас, но (по другим причинам). Я также сохраняю модель как атрибут данных в представлении. $ El, и я не могу не чувствовать, что есть должен быть лучшим способом, чем хранить ссылки повсюду.

Ну, это в конечном итоге сводится к вашему предпочтению, как структурировать вещи. Если вы предпочитаете хранить объекты вида более централизованным образом, их можно сохранить в экземпляре TuneBook вместо использования jQuery.data. См. № 1: Централизованный.

Так или иначе вы храните ссылки на ClosedTune объектов: с помощью jQuery.data, или в простом объекте в TuneBook, или в _callbacks в TuneBook.

Если вам нравится подход @ ggozad по причинам, которые вы понимаете, идите на это, но это не волшебство. Поскольку он представлен здесь, я не уверен, что преимущество должно обеспечиваться дополнительным уровнем абстракции по сравнению с более простой версией, представленной в № 1. Если есть какое-то преимущество, не стесняйтесь заполнять меня.

+0

Я рассмотрел вариант 1, но решил против него, поскольку у меня уже есть коллекция настраиваемых моделей в tunebook, и мне не нужен другой объект, который мне нужно будет хранить в sync (например, в TuneModel.remove() Мне нужно будет удалить представление из списка просмотров tunebook ... для этого, вероятно, понадобятся события, поэтому решение только для событий станет выглядеть более привлекательным). Вариант 2 - это более или менее то, что я делаю прямо сейчас, но (по другим причинам) я также сохраняю модель как атрибут данных в представлении. $ El, и я не могу не чувствовать, что должен быть лучший способ чем хранение ссылок повсюду. – wheresrhys

+0

@wheresrhys Я не знаю, был ли это фактором в вашем выборе, но я исправил глупые ошибки, которые были в моих предыдущих примерах кода. Я обновил свой ответ с ответом на ваш комментарий (см. Раздел «Ответ на комментарий»). – JMM

+0

Приветствия за тщательный ответ. Это правда, что легко видеть события как волшебную пулю, которая свободно связывает объекты без каких-либо недостатков, и вы правы, что я должен больше подумать о том, чтобы объявлять обратные вызовы при удалении объектов. Тем не менее события по-прежнему остаются моим предпочтительным вариантом. Наличие дублирующих ссылок на отвлеченные взгляды кажется более чистым и менее заманчивым для меня или других разработчиков, чтобы злоупотреблять ими. Что касается использования $ .data, я всегда опасаюсь использовать что-либо, запрашивающее DOM, если это абсолютно необходимо, поскольку взаимодействия DOM относительно медленны. – wheresrhys