У меня есть три разных способа инициализации и рендеринга представления и его подпредставлений, и у каждого из них свои проблемы. Мне любопытно узнать, есть ли лучший способ, который решает все проблемы:
Сценарий первый:
Инициализируйте дочерние элементы в функции инициализации родителя. Таким образом, не все застревает в рендере, поэтому при рендеринге меньше блокировок.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.render().appendTo(this.$('.container-placeholder');
}
Проблемы:
Самая большая проблема заключается в том, что повторный вызов render для родителя удалит все привязки дочерних событий. (Это из-за того, как
$.html()
работает jQuery .) Это можно было бы смягчить, вызвавthis.child.delegateEvents().render().appendTo(this.$el);
вместо этого вызов , но в первом и наиболее частом случае вы выполняете больше работы без необходимости.Добавляя потомков, вы заставляете функцию рендера знать структуру DOM родителей, чтобы получить нужный порядок. Это означает, что изменение шаблона может потребовать обновления функции визуализации представления.
Сценарий второй:
Инициализируйте дочерние элементы в родительском initialize()
буфере, но вместо добавления используйте setElement().delegateEvents()
для установки дочернего элемента к элементу в родительском шаблоне.
initialize : function () {
//parent init stuff
this.child = new Child();
},
render : function () {
this.$el.html(this.template());
this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}
Проблемы:
- Это делает
delegateEvents()
необходимое сейчас, что является небольшим недостатком по сравнению с необходимостью только при последующих вызовах в первом сценарии.
Сценарий третий:
render()
Вместо этого инициализируйте дочерние элементы в методе родителя .
initialize : function () {
//parent init stuff
},
render : function () {
this.$el.html(this.template());
this.child = new Child();
this.child.appendTo($.('.container-placeholder').render();
}
Проблемы:
Это означает, что теперь функция рендеринга должна быть связана со всей логикой инициализации.
Если я отредактирую состояние одного из дочерних представлений, а затем вызову render для родителя, будет создан совершенно новый дочерний вид, и все его текущее состояние будет потеряно. Который также, кажется, мог бы стать рискованным для утечек памяти.
Очень любопытно, чтобы ваши парни взяли это на себя. Какой сценарий вы бы использовали? или есть четвертый магический, который решает все эти проблемы?
Вы когда-нибудь отслеживали визуализированное состояние для View? Скажи renderedBefore
флаг? Кажется, действительно вяло.
источник
delegateEvents()
послеsetElement()
? Согласно документации: «... и переместить делегированные события представления из старого элемента в новый», самsetElement
метод должен обрабатывать повторное делегирование событий.Ответы:
Это большой вопрос. Магистраль великолепна из-за отсутствия допущений, которые она делает, но это означает, что вы должны (решить, как) реализовать подобные вещи самостоятельно. Просматривая свои собственные материалы, я обнаружил, что я (вроде) использую сочетание сценария 1 и сценария 2. Я не думаю, что существует четвертый магический сценарий, потому что достаточно просто все, что вы делаете в сценарии 1 и 2, должно быть сделано.
Я думаю, что было бы проще объяснить на примере, как мне нравится справляться с этим. Скажем, у меня есть эта простая страница, разбитая на указанные виды:
Скажем, HTML после рендеринга выглядит примерно так:
Надеюсь, это довольно очевидно, как HTML совпадает с диаграммой.
ParentView
Имеет 2 дочерние представления,InfoView
иPhoneListView
, а также несколько дополнительных дивы, один из которых,#name
, должен быть установлен в какой - то момент.PhoneListView
содержит собственные дочерние представления, массивPhoneView
записей.Итак, к вашему актуальному вопросу. Я занимаюсь инициализацией и рендерингом по-разному в зависимости от типа представления. Я делю свои взгляды на два типа:
Parent
взгляды иChild
взгляды.Разница между ними проста:
Parent
представления содержат дочерние представления, аChild
представления - нет. Так что в моем примере,ParentView
иPhoneListView
являютсяParent
мнением, в то время какInfoView
иPhoneView
записи являютсяChild
взглядами.Как я уже упоминал ранее, самая большая разница между этими двумя категориями заключается в том, что им разрешено отображать. В идеальном мире я хочу, чтобы
Parent
представления отображались только один раз. Их дочерние представления должны обрабатывать любой повторный рендеринг при изменении модели.Child
виды, с другой стороны, я разрешаю перерисовывать в любое время, когда им нужно, поскольку у них нет других представлений, полагающихся на них.Чуть более подробно, для
Parent
представлений мне нравится, что моиinitialize
функции выполняют несколько вещей:InfoView
, будет назначен#info
).Шаг 1 довольно понятен.
Шаг 2, рендеринг, сделан таким образом, что любые элементы, на которые полагаются дочерние представления, уже существуют, прежде чем я попытаюсь назначить их. Делая это, я знаю, что все дочерние
events
элементы будут правильно установлены, и я могу перерисовывать их блоки столько раз, сколько захочу, не беспокоясь о необходимости повторного делегирования. На самом деле я не вижуrender
здесь никаких детских взглядов, я позволяю им делать это в рамках их собственныхinitialization
.Шаги 3 и 4 фактически обрабатываются в то же время, что и я,
el
при создании дочернего представления. Мне нравится передавать элемент здесь, так как я считаю, что родитель должен определить, где, по его собственному мнению, ребенку разрешено размещать его содержимое.Для рендеринга я стараюсь сделать его довольно простым для
Parent
представлений. Я хочу, чтобыrender
функция не делала ничего, кроме визуализации родительского представления. Без делегирования событий, без представления дочерних представлений, ничего. Просто простой рендер.Иногда это не всегда работает, хотя. Например, в моем примере выше,
#name
элемент должен будет обновляться каждый раз, когда меняется имя в модели. Тем не менее, этот блок является частьюParentView
шаблона и не обрабатывается специальнымChild
представлением, поэтому я работаю над этим. Я создам какую-тоsubRender
функцию, которая только заменяет содержимое#name
элемента и не должна уничтожать весь#parent
элемент. Это может показаться хаком, но я действительно обнаружил, что это работает лучше, чем беспокоиться о повторном рендеринге всего DOM и повторного присоединения элементов и тому подобного. Если бы я действительно хотел сделать его чистым, я бы создал новоеChild
представление (похожее наInfoView
), которое будет обрабатывать#name
блок.Теперь для
Child
представленийinitialization
это очень похоже наParent
представления, только без создания каких-либо дальнейшихChild
представлений. Так:Child
Просмотр рендеринга также очень прост, просто отрендерите и установите содержимое моегоel
. Опять же, не возиться с делегацией или что-то в этом роде.Вот пример кода того, как
ParentView
может выглядеть мой :Вы можете увидеть мою реализацию
subRender
здесь. При наличии изменений , связанных сsubRender
вместо тогоrender
, я не должен беспокоиться о палить и восстановление всего блока.Вот пример кода для
InfoView
блока:Связи являются важной частью здесь. Привязываясь к моей модели, мне никогда не придется беспокоиться о том, чтобы позвонить
render
мне вручную . Если модель изменится, этот блок будет перерисован сам, не влияя на другие виды.Это
PhoneListView
будет аналогично томуParentView
, что вам просто понадобится немного больше логики в ваших функцияхinitialization
иrender
функциях для обработки коллекций. То, как вы обрабатываете коллекцию, действительно зависит от вас, но вам, по крайней мере, нужно будет слушать события коллекции и решать, как вы хотите визуализировать (добавить / удалить или просто заново визуализировать весь блок). Лично мне нравится добавлять новые виды и удалять старые, а не заново отображать весь вид.PhoneView
Будет почти идентиченInfoView
, только прислушиваясь к модели изменений , которые она заботится о.Надеюсь, это немного помогло, пожалуйста, дайте мне знать, если что-то сбивает с толку или недостаточно подробно.
источник
render
внутриinitialize
метода - плохая практика, потому что он не позволяет вам быть более производительным в тех случаях, когда вы не хотите выполнять рендеринг сразу. Что Вы думаете об этом?PhoneListView
?Я не уверен, что это прямо отвечает на ваш вопрос, но я думаю, что это актуально:
http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/
Конечно, контекст, в котором я создал эту статью, отличается, но я думаю, что два решения, которые я предлагаю, наряду с плюсами и минусами каждого, должны заставить вас двигаться в правильном направлении.
источник
Для меня не кажется худшей идеей в мире проводить различие между первоначальной настройкой и последующими настройками ваших представлений с помощью какого-то флага. Чтобы сделать это простым и понятным, флаг должен быть добавлен к вашему собственному представлению, которое должно расширять базовое представление.
Как и Дерик, я не совсем уверен, отвечает ли это прямо на ваш вопрос, но я думаю, что, по крайней мере, стоит упомянуть в этом контексте.
источник
Кевин Пил дает отличный ответ - вот моя версия:
источник
Я пытаюсь избежать связи между такими взглядами. Есть два способа, которые я обычно делаю:
Использовать роутер
По сути, вы позволяете функции маршрутизатора инициализировать родительское и дочернее представление. Таким образом, представление не знает друг друга, но маршрутизатор обрабатывает все это.
Передача одного и того же эль в оба представления
Оба имеют знания об одном и том же DOM, и вы можете заказать их в любое время.
источник
То, что я делаю, дает каждому ребенку личность (который Backbone уже сделал это для вас: cid)
Когда Контейнер выполняет рендеринг, использование 'cid' и 'tagName' генерирует заполнитель для каждого дочернего элемента, поэтому в шаблоне дочерние элементы не имеют представления о том, куда он будет помещен Контейнером.
чем вы можете использовать
указанный заполнитель не требуется, и Container генерирует только заполнитель, а не дочернюю структуру DOM. Cotainer и Children по-прежнему генерируют собственные элементы DOM и только один раз.
источник
Вот легкий миксин для создания и рендеринга подпредставлений, который, я думаю, решает все проблемы в этой теме:
https://github.com/rotundasoftware/backbone.subviews
Подход, используемый этим плагином, заключается в создании и визуализации подпредставлений после первого отображения родительского представления. Затем при последующих визуализациях родительского представления $ .detach элементы подпредставления повторно визуализируют родительский элемент, затем вставляют элементы подпредставления в соответствующие места и повторно визуализируют их. Таким образом, объекты подпредставлений повторно используются при последующих рендерингах, и нет необходимости повторно делегировать события.
Обратите внимание, что случай представления коллекции (где каждая модель в коллекции представлена одним подпредставлением) весьма отличается и, я думаю, заслуживает своего собственного обсуждения / решения. Лучшее общее решение, которое мне известно, - это CollectionView in Marionette .
РЕДАКТИРОВАТЬ: Для случая представления коллекции, вы также можете проверить эту более ориентированную на пользовательский интерфейс реализацию , если вам нужен выбор моделей на основе щелчков и / или перетаскивания для изменения порядка.
источник