javascript - pixar - marionette pelicula
Desactivando la envoltura div para Backbone.Marionette.ItemView (5)
Estoy mirando los mensajes de Angry Cats Backbone / Marionette aquí
http://davidsulc.com/blog/2012/04/15/a-simple-backbone-marionette-tutorial/
http://davidsulc.com/blog/2012/04/22/a-simple-backbone-marionette-tutorial-part-2/
y me encontré con la misma pregunta / necesidad publicada aquí:
Backbone.js desactivando wrap por div en render
Pero solo puedo hacer que funcione para Backbone.Views, no Backbone.Marionette.ItemViews.
Por ejemplo, desde los enlaces del tutorial de marionetas simples de la columna vertebral, tome AngryCatView:
AngryCatView = Backbone.Marionette.ItemView.extend({
template: "#angry_cat-template",
tagName: ''tr'',
className: ''angry_cat'',
...
});
La plantilla, #angry_cat-template
, se ve así:
<script type="text/template" id="angry_cat-template">
<td><%= rank %></td>
<td><%= votes %></td>
<td><%= name %></td>
...
</script>
Lo que no me gusta, es que AngryCatView necesita tener
tagName: ''tr'',
className: ''angry_cat'',
- Si angry_cat-template
, entonces angry_cat-template
queda envuelto por un <div>
.
Lo que me gustaría es especificar el HTML en un solo lugar (la plantilla angry_cat) y no tener la mayoría del HTML (todas las etiquetas <td>
) en angry_cat-template y un pequeño HTML (la etiqueta <tr>
) en AngryCatView. Me gustaría escribir esto en angry_cat-template:
<script type="text/template" id="angry_cat-template">
<tr class="angry_cat">
<td><%= rank %></td>
<td><%= votes %></td>
<td><%= name %></td>
...
</tr>
</script>
Simplemente me parece más limpio, pero he estado dando vueltas con la respuesta de Derik Bailey en "Backbone.js apagando wrap por div en render" y no puedo hacer que funcione para Backbone.Marionette.
¿Algunas ideas?
¿No sería más limpio usar JS vainilla en lugar de jQuery para lograr esto?
var Layout = Backbone.Marionette.LayoutView.extend({
...
onRender: function () {
this.setElement(this.el.innerHTML);
}
...
});
2014/02/18 - actualizado para acomodar las mejoras señaladas por @vaughan y @ Thom-Nichols en los comentarios
En muchos de mis artículos Vista / diseños, hago esto:
var Layout = Backbone.Marionette.Layout.extend({
...
onRender: function () {
// Get rid of that pesky wrapping-div.
// Assumes 1 child element present in template.
this.$el = this.$el.children();
// Unwrap the element to prevent infinitely
// nesting elements during re-render.
this.$el.unwrap();
this.setElement(this.$el);
}
...
});
El código anterior solo funciona cuando el divisor de envoltura contiene un solo elemento, que es la forma en que diseño mis plantillas.
En su caso .children()
devolverá <tr class="angry_cat">
, así que esto debería funcionar perfecto.
Estoy de acuerdo, mantiene las plantillas mucho más limpias.
Una cosa a tener en cuenta:
Esta técnica no obliga solo a 1 elemento hijo. .children()
ciegamente .children()
por lo que si ha creado incorrectamente la plantilla para devolver más de un elemento, como el primer ejemplo de plantilla con 3 elementos <td>
, no funcionará bien.
Requiere que su plantilla devuelva un único elemento, como lo hizo en la segunda plantilla con el elemento raíz <tr>
.
Por supuesto, podría escribirse para probar esto si fuera necesario.
Aquí hay un ejemplo de trabajo para los curiosos: http://codepen.io/somethingkindawierd/pen/txnpE
Aunque estoy seguro de que hay una manera de hackear los aspectos internos del render
para que se comporte de la manera que te gustaría, tomar este enfoque significa que estarás luchando contra las convenciones de Backbone y Marionette a lo largo de todo el proceso de desarrollo. ItemView
necesita tener un $el
asociado, y por convención, es un div
menos que especifique un tagName
.
Me identifico, especialmente en el caso de los diseños y las regiones, parece ser imposible evitar que Backbone genere un elemento adicional. Recomiendo aceptar la convención mientras aprende el resto del framework y solo luego decide si vale la pena hackear el render
para comportarse de manera diferente (o simplemente elegir un framework diferente).
Esta solución funciona para volver a renderizar. Necesitas anular el render
.
onRender
trucos onRender
no funcionarán para volver a renderizar. Ellos causarán anidación en cada nueva renderización.
BM.ItemView::render = ->
@isClosed = false
@triggerMethod "before:render", this
@triggerMethod "item:before:render", this
data = @serializeData()
data = @mixinTemplateHelpers(data)
template = @getTemplate()
html = Marionette.Renderer.render(template, data)
#@$el.html html
$newEl = $ html
@$el.replaceWith $newEl
@setElement $newEl
@bindUIElements()
@triggerMethod "render", this
@triggerMethod "item:rendered", this
this
Para IE9 + puedes usar firstElementChild y childElementCount :
var Layout = Backbone.Marionette.LayoutView.extend({
...
onRender: function () {
if (this.el.childElementCount == 1) {
this.setElement(this.el.firstElementChild);
}
}
...
});
Hay una buena razón por la cual Marionette inserta automáticamente el envoltorio DIV. Solo cuando su plantilla consiste en un solo elemento, puede soltarlo. De ahí la prueba para el número de elementos secundarios.
Otra opción es utilizar el método attachElContent presente en cada vista de Marionette. Su implementación predeterminada significa que las repeticiones de la vista sobrescribirán el HTML interno del elemento raíz. Esto finalmente da lugar a la anidación infinita mencionada en la respuesta de bejonbee.
Si prefiere no sobrescribir onRender y / o no requiere una solución JS pura, el siguiente código podría ser justo lo que desea:
var Layout = Backbone.Marionette.LayoutView.extend({
...
attachElContent: function (html) {
var parentEl = this.el.parentElement;
var oldEl;
//View already attached to the DOM => re-render case => prevents
//recursive nesting by considering template''s top element as the
//view''s when re-rendering
if (parentEl) {
oldEl = this.el;
this.setElement(html); //gets new element from parsed html
parentEl.replaceChild(this.el, oldEl); //updates the dom with the new element
return this;
//View hasn''t been attached to the DOM yet => first render
// => gets rid of wrapper DIV if only one child
} else {
Marionette.ItemView.prototype.attachElContent.call(this, html);
if (this.el.childElementCount == 1) {
this.setElement(this.el.firstElementChild);
}
return this;
}
}
...
});
Tenga en cuenta que para volver a procesar el trabajo, el código supone una plantilla con un único elemento secundario que contiene todas las marcas.