Vistas de Backbone y el DOM
() translation by (you can also view the original English article)



Resumen
Las vistas de Backbone proporcionan una convención útil y la abstracción de las interfaces de usuario. Sin embargo, si queremos incluir funcionalidades que Backbone no soporta nativamente, tendremos que integrar nuestro propio código o librerías externas en nuestra aplicación Backbone. Y tendremos que tener cuidado para evitar conflictos entre Backbone y las librerías externa que incluyamos en nuestro applicación.
Introducción a Backbone
Backbone es una forma fantástica de organizar tu código del lado cliente. Con abstracciones como modelos, vistas y colecciones, Backbone le ayuda a los desarrolladores serios a escribir aplicaciones bien organizadas y escalables.
Aunque hay muchas alternativas a Backbone, incluidas Angular y Ember, Backbone le brinda a los desarrolladores una libertad increíble para escribir y organizar su código de manera natural y cómoda sin ser demasiado obstinados sobre cómo se ve el Modelo de objetos de documento (DOM).
Skinny en las vistas de Backbone
Las vistas son uno de los componentes más poderosos y flexibles de Backbone. Según los autores de Backbone:
Las vistas de Backbone son casi más convencionales que el código: no determinan nada acerca de tu HTML o CSS y pueden usarse con cualquier biblioteca de plantillas de JavaScript.
Se utilizan para manipular lo que los usuarios ven en su navegador, y facilitan la comunicación con los modelos. Como resultado, en el paradigma de Model-View-Controller, es útil pensar en las vistas de Backbone como vista y controlador.
Esto tiene graves implicaciones al desarrollar aplicaciones con una interacción significativa del usuario. De hecho, hay muchas situaciones en las que es posible que quieras utilizar alguna otra biblioteca para manipular el DOM. La visualización de datos y los juegos basados en plataformas web son dos ejemplos en los que es posible que prefieras que otra biblioteca controle algunas de las representaciones de vista orientadas al usuario. Como resultado, puedes considerar usar jQuery, d3.js, crossfilter o three.js para algunas de tus necesidades de manipulación de DOM.
Afortunadamente, hay maneras de hacer que Backbone funcione bien con estos otros manipuladores DOM.
Manipulación del modelo de objetos de documento en Backbone
Antes de profundizar en esto, repasemos la manipulación DOM en Backbone. Comencemos con un objeto de vista básico.
1 |
var SomeView = Backbone.View.extend({ |
2 |
// Some definition stuff here
|
3 |
});
|
4 |
|
5 |
var aView = new SomeView(); |
Excelente. Ahora, vamos a decirle a la vista cómo renderizarse definiendo un método .render()
.
1 |
var SomeView = Backbone.View.extend({ |
2 |
// define how to render something
|
3 |
render: function() { |
4 |
// get some HTML
|
5 |
var htmlContent = "<p>This some HTML that will be inserted into the DOM</p>"; |
6 |
|
7 |
// insert the html
|
8 |
this.$el.html(htmlContent); |
9 |
|
10 |
// return an instance of the object for chaining
|
11 |
return this; |
12 |
}
|
13 |
});
|
Aquí están sucediendo varias cosas, así que vayamos paso a paso.
Definición de un método .render()
.
En primer lugar, definimos un método .render()
que encapsule la lógica necesaria para renderizar HTML. Ten en cuenta que Backbone viene con un método .render()
listo para usar. Sin embargo, no hace nada. ¡Fue diseñado para sobrescribirse con lógica personalizada!
Obtener contenido HTML
El ejemplo anterior asume que obtienes HTML en alguna parte. Puedes utilizar guiones bajos _.template()
. Alternativamente, podemos usar otras bibliotecas de plantillas, como Handlebars (mi preferida). Lo único que importa es que, de alguna manera, obtenemos algo de contenido HTML.
¿Qué diablos es el
?
Necesitamos un lugar para colocar el contenido HTML; para eso es el
. Como .render()
, el
es un atributo que viene con las vistas de Backbone listas para usar. Hace referencia al elemento HTML (y a todos sus elementos secundarios) contenidos en esta vista. En el ejemplo anterior, no especificamos el
. De forma predeterminada, el
es un div
. Sin embargo, podríamos haber configurado fácilmente el elemento padre así:
1 |
var SomeView = Backbone.View.extend({ |
2 |
el: "article", ... |
3 |
});
|
4 |
|
5 |
var aView = new SomeView(); |
6 |
|
7 |
console.log(aView.el); // an empty "article" HTML element |
También está $el
, que es simplemente el
envuelto en jQuery. Más adelante veremos que $el
juega un papel importante en el dominio de las vistas de Backbone.
Devolviendo this
...
Finalmente, devolvemos una referencia al objeto en sí para permitir el encadenamiento. Si bien no es estrictamente obligatorio, devolver this
es una convención. Sin return this
, necesitaríamos alguna forma de acceder al contenido HTML del elemento. El siguiente código muestra una solución alternativa.
1 |
/**
|
2 |
* If render() returns nothing, we are really
|
3 |
* accessing the `el` property of undefined, which does not
|
4 |
* exist!
|
5 |
*/
|
6 |
aView.render().el; // Should throw an error |
7 |
|
8 |
// Try accessing the HTML
|
9 |
console.log(aView.el); // Should be empty (but defined!) |
10 |
|
11 |
// add HTML to the DOM of 'aView'
|
12 |
aView.render(); |
13 |
|
14 |
// Try accessing the HTML again
|
15 |
console.log(aView.el) // Should contain the HTML |
¡Ay, no hay nada en la pantalla!
Buen punto. Aunque llamamos a .render()
, no hay nada en la pantalla, ¿qué pasa?
Eso se debe a que todavía no hemos interactuado con el DOM. Todo lo que hicimos fue generar algo de HTML y representarlo en un objeto JavaScript llamado aView
. Dado que ahora tenemos acceso al HTML generado, todo lo que tenemos que hacer es agregar o insertar el HTML en el DOM de tu aplicación web.
Para avanzar, también configuraremos una mini-aplicación para que cuando se cargue la página, aparezca la vista. A continuación se muestra cómo debe ser tu HTML y JavaScript.
Configuración básica de HTML
1 |
<html>
|
2 |
<head>
|
3 |
<meta charset="utf-8"> |
4 |
<title>My Awesome Backbone App</title> |
5 |
<!-- Include your CSS Here -->
|
6 |
<link rel="stylesheet" type="text/css" href="/css/styles.css" /> |
7 |
|
8 |
<!-- Include JS dependencies -->
|
9 |
<!-- Backbone depends on underscore and, in this example,
|
10 |
depends on jQuery. Please note the order of the
|
11 |
dependencies -->
|
12 |
<script src="/js/lib/jquery.js"></script> |
13 |
<script src="/js/lib/underscore.js"></script> |
14 |
<script src="/js/lib/backbone.js"></script> |
15 |
</head>
|
16 |
<body>
|
17 |
<div class="app"></div> |
18 |
<!-- Include your custom Backbone code in the below script -->
|
19 |
<script src="/js/app.js"></script> |
20 |
</body>
|
21 |
</html>
|
Esto es lo que está sucediendo en App.js
1 |
// Create a view
|
2 |
var SomeView = Backbone.View.extend({ |
3 |
initialize: function() {}, |
4 |
render: function() { |
5 |
var someHTML = "<p>This is some HTML</p>"; |
6 |
this.$el.html(someHTML); |
7 |
return this; |
8 |
}
|
9 |
});
|
10 |
|
11 |
// Create a router
|
12 |
var Router = Backbone.Router.extend({ |
13 |
// define your routes
|
14 |
routes: { "": "home" }, |
15 |
home: function() { |
16 |
var aView = new SomeView(); |
17 |
$('.app').html(aView.render().el); |
18 |
}
|
19 |
});
|
20 |
|
21 |
// Instantiate your router
|
22 |
new Router(); |
23 |
|
24 |
// Start tracking history
|
25 |
Backbone.history.start(); |
Ve a tu servidor/navegador local, carga la página ¡y tu aplicación debería estar ejecutándose!
Usando Backbone y jQuery simultáneamente
La flexibilidad de Backbone permite que el uso de bibliotecas de terceros manipule el DOM. Un escenario es cuando quieres usar jQuery y Backbone simultáneamente para manipular las vistas. A continuación se muestra un ejemplo actualizado.
1 |
var SomeView = Backbone.View.extend({ |
2 |
|
3 |
// Manipulate DOM indirectly by creating HTML content in a
|
4 |
// Backbone View
|
5 |
render: function() { |
6 |
var someHTML = "<p>Some HTML</p><p class='empty'><p>"; |
7 |
this.$el.html(someHTML); |
8 |
return this; |
9 |
},
|
10 |
|
11 |
// Manipulate DOM directly from within the Backbone View
|
12 |
renderWithJQuery: function() { |
13 |
var otherHTML = "<p>Other HTML</p>"; |
14 |
$('.app').append(otherHTML); |
15 |
|
16 |
// may not make sense to return 'this'
|
17 |
},
|
18 |
|
19 |
// another render method, to keep things interesting
|
20 |
specialRender: function() { |
21 |
this.$('.empty').append("<span>No longer empty!</span>"); |
22 |
return this; |
23 |
}
|
24 |
});
|
25 |
|
26 |
// Later in your app...
|
27 |
|
28 |
// create the view
|
29 |
var aView = new SomeView(); |
30 |
|
31 |
// change the DOM to reflect the newly created view
|
32 |
$('.app').html(aView.render().el); |
33 |
|
34 |
// append more content directly to the DOM using jQuery within
|
35 |
// a Backbone view object
|
36 |
aView.renderWithJQuery(); |
El código anterior dará como resultado dos párrafos en la página. El primer párrafo contiene "Algo de HTML". El segundo párrafo contiene "Otro HTML".
Para poner a prueba tu conocimiento sobre esto, invierte las llamadas al método de la siguiente manera:
1 |
// SomeView is already defined
|
2 |
|
3 |
var aView = new SomeView(); |
4 |
|
5 |
aView.renderWithJQuery(); |
6 |
|
7 |
$('.app').html(aView.render().el); |
El código anterior dará como resultado un párrafo: "Algo de HTML". En ambos casos, también hay un elemento <p>
sin nada en él. Discutiremos esto en un momento.
Manipular el DOM en las vistas de Backbone de manera eficiente
Comprender la magia de la manipulación (y transversal) eficiente del DOM requiere comprender this.$el
y this.$()
. Al usar this.$el
, estamos ajustando la manipulación del DOM al contenido de la vista. Al usar this.$()
, estamos examinando el recorrido de DOM al árbol DOM dentro de la vista.
Como resultado, en el contexto de Backbone, algunos usos de $()
(en lugar de this.$()
) podrían ser ineficientes. Por ejemplo, supongamos que queríamos cruzar el DOM para encontrar algún elemento. Podríamos usar cualquiera de los métodos comunes de recorrido DOM, incluido .find()
, .children()
, .closest()
, .first()
, y así sucesivamente.
Si sabemos, a priori, que el elemento que buscamos se encuentra en algún lugar dentro del DOM de la vista, entonces debemos usar this.$()
para evitar buscar un árbol DOM más grande innecesariamente. Si el elemento que buscamos está fuera del DOM de la vista, entonces tendremos que usar $()
.
Por ejemplo, el método .specialRender()
utiliza el recorrido DOM localizado para garantizar que buscamos elementos con la clase empty
dentro del contexto de la vista. Si se encuentra, establece el contenido HTML de esos elementos para agregar un span
y el texto "Ya no está vacío".
Conclusión
En este artículo revisamos las vistas de Backbone, discutimos cómo renderizar las vistas de Backbone en el DOM y exploramos cómo hacer que Backbone funcione bien con otras bibliotecas que quizás quieras utilizar para manipular el DOM. También aprendimos sobre el recorrido DOM localizado y los métodos identificados para cruzar el DOM de manera eficiente e ineficiente.
En la siguiente parte de este artículo profundizaremos en ejemplos más complicados de cómo hacer que varias bibliotecas trabajen juntas en la manipulación del DOM.