Por favor trate esta pregunta como estrictamente educativa. Todavía estoy interesado en escuchar nuevas respuestas e ideas para implementar esto
tl; dr
¿Cómo implementaría el enlace de datos bidireccional con JavaScript?
Enlace de datos al DOM
Por enlace de datos al DOM me refiero, por ejemplo, a tener un objeto JavaScript a
con una propiedad b
. Luego tener un <input>
elemento DOM (por ejemplo), cuando el elemento DOM cambia, a
cambia y viceversa (es decir, quiero decir enlace de datos bidireccional).
Aquí hay un diagrama de AngularJS sobre cómo se ve esto:
Así que básicamente tengo JavaScript similar a:
var a = {b:3};
Luego, un elemento de entrada (u otra forma) como:
<input type='text' value=''>
Me gustaría que el valor de entrada sea a.b
el valor de (por ejemplo), y cuando el texto de entrada cambia, también me gustaría a.b
cambiarlo. Cuando a.b
cambia en JavaScript, la entrada cambia.
La pregunta
¿Cuáles son algunas técnicas básicas para lograr esto en JavaScript simple?
En concreto, me gustaría una buena respuesta para referirme a:
- ¿Cómo funcionaría el enlace para los objetos?
- ¿Cómo podría funcionar escuchar el cambio en la forma?
- ¿Es posible, de una manera simple, modificar solo el HTML en el nivel de plantilla? Me gustaría no hacer un seguimiento del enlace en el documento HTML en sí, sino solo en JavaScript (con eventos DOM y JavaScript manteniendo referencia a los elementos DOM utilizados).
Que he probado
Soy un gran fanático del bigote, así que intenté usarlo para crear plantillas. Sin embargo, me encontré con problemas al intentar realizar el enlace de datos en sí, ya que Moustache procesa HTML como una cadena, por lo que después de obtener su resultado no tengo referencia a dónde están los objetos en mi modelo de vista. La única solución que pude pensar para esto fue modificar la cadena HTML (o el árbol DOM creado) con atributos. No me importa usar un motor de plantillas diferente.
Básicamente, tuve la fuerte sensación de que estaba complicando el problema en cuestión y hay una solución simple.
Nota: No proporcione respuestas que utilicen bibliotecas externas, especialmente aquellas que son miles de líneas de código. He usado (¡y me gusta!) AngularJS y KnockoutJS. Realmente no quiero respuestas en la forma 'use framework x'. De manera óptima, me gustaría un futuro lector que no sepa cómo usar muchos marcos para comprender cómo implementar el enlace de datos bidireccional. No espero una respuesta completa , pero sí una que transmita la idea.
fuente
Respuestas:
Una abstracción que actualiza ambos objetos.
Supongo que hay otras técnicas, pero en última instancia, tendría un objeto que tiene referencia a un elemento DOM relacionado y proporciona una interfaz que coordina las actualizaciones de sus propios datos y su elemento relacionado.
El
.addEventListener()
proporciona una interfaz muy agradable para esto. Puede darle un objeto que implemente laeventListener
interfaz, e invocará a sus controladores con ese objeto comothis
valor.Esto le brinda acceso automático tanto al elemento como a sus datos relacionados.
Definiendo tu objeto
La herencia de prototipos es una buena manera de implementar esto, aunque no es obligatorio, por supuesto. Primero crearía un constructor que reciba su elemento y algunos datos iniciales.
Entonces, aquí el constructor almacena el elemento y los datos en las propiedades del nuevo objeto. También vincula un
change
evento a lo dadoelement
. Lo interesante es que pasa el nuevo objeto en lugar de una función como segundo argumento. Pero esto solo no funcionará.Implementando la
eventListener
interfazPara que esto funcione, su objeto necesita implementar la
eventListener
interfaz. Todo lo que se necesita para lograr esto es darle al objeto unhandleEvent()
método.Ahí es donde entra la herencia.
Hay muchas formas diferentes en que esto podría estructurarse, pero para su ejemplo de coordinación de actualizaciones, decidí hacer que el
change()
método solo acepte un valor y quehandleEvent
pase ese valor en lugar del objeto de evento. De esta manera,change()
se puede invocar sin un evento también.Entonces, cuando
change
ocurra el evento, actualizará tanto el elemento como la.data
propiedad. Y lo mismo sucederá cuando llame.change()
a su programa JavaScript.Usando el código
Ahora simplemente crearía el nuevo objeto y dejaría que realizara actualizaciones. Las actualizaciones en el código JS aparecerán en la entrada, y los eventos de cambio en la entrada serán visibles para el código JS.
DEMO: http://jsfiddle.net/RkTMD/
fuente
Mustache.render(template,object)
, suponiendo que quiero mantener un objeto sincronizado con la plantilla (no específico para Moustache), ¿cómo continuaría con eso?input
va a actualizar uno de esos puntos, habrá una asignación de la entrada a ese punto. Veré si puedo encontrar un ejemplo rápido.Template
constructor primario que realiza el análisis, contiene los diferentesMyCtor
objetos y proporciona una interfaz para actualizar cada uno por su identificador. Déjeme saber si usted tiene preguntas. :) EDITAR: ... use este enlace en su lugar ... Había olvidado que tenía un aumento exponencial en el valor de entrada cada 10 segundos para demostrar las actualizaciones de JS. Esto lo limita.Entonces, decidí tirar mi propia solución en la olla. Aquí hay un violín que funciona . Tenga en cuenta que esto solo se ejecuta en navegadores muy modernos.
Que utiliza
Esta implementación es muy moderna: requiere un navegador (muy) moderno y los usuarios dos nuevas tecnologías:
MutationObserver
s para detectar cambios en el dom (también se utilizan oyentes de eventos)Object.observe
para detectar cambios en el objeto y notificar al dom. Peligro, dado que esta respuesta se ha escrito Oo ha sido discutido y decidido por el ECMAScript TC, considere un polyfill .Cómo funciona
domAttribute:objAttribute
asignación, por ejemplobind='textContent:name'
La solución
Aquí está la
dataBind
función, tenga en cuenta que solo son 20 líneas de código y podrían ser más cortas:Aquí hay algunos usos:
HTML:
JavaScript:
Aquí hay un violín que funciona . Tenga en cuenta que esta solución es bastante genérica. Object.observe y mutation observador shimming está disponible.
fuente
obj.name
tiene un setter no se puede observar externamente, pero debe transmitir que ha cambiado desde dentro del setter - html5rocks.com/en/tutorials/es7/observe/#toc-notifications - un poco arroja una llave en las obras para Oo () si desea un comportamiento más complejo e interdependiente utilizando setters. Además, cuandoobj.name
no es configurable, tampoco se permite redefinir su configurador (con varios trucos para agregar notificaciones), por lo que los genéricos con Oo () se eliminan por completo en ese caso específico.Me gustaría agregar a mi preposter. Sugiero un enfoque ligeramente diferente que le permitirá simplemente asignar un nuevo valor a su objeto sin utilizar un método. Sin embargo, debe tenerse en cuenta que esto no es compatible con navegadores especialmente antiguos e IE9 aún requiere el uso de una interfaz diferente.
Lo más notable es que mi enfoque no hace uso de los eventos.
Getters y Setters
Mi propuesta hace uso de la característica relativamente joven de getters y setters , en particular solo setters. En términos generales, los mutadores nos permiten "personalizar" el comportamiento de cómo ciertas propiedades se les asigna un valor y se recuperan.
Una implementación que usaré aquí es el método Object.defineProperty . Funciona en Firefox, Google Chrome y, creo, IE9. No he probado otros navegadores, pero dado que esto es solo teoría ...
De todos modos, acepta tres parámetros. El primer parámetro es el objeto para el que desea definir una nueva propiedad, el segundo una cadena que se parece al nombre de la nueva propiedad y el último un "objeto descriptor" que proporciona información sobre el comportamiento de la nueva propiedad.
Dos descriptores particularmente interesantes son
get
yset
. Un ejemplo se parecería a lo siguiente. Tenga en cuenta que el uso de estos dos prohíbe el uso de los otros 4 descriptores.Ahora hacer uso de esto se vuelve ligeramente diferente:
Quiero enfatizar que esto solo funciona para los navegadores modernos.
Violín de trabajo: http://jsfiddle.net/Derija93/RkTMD/1/
fuente
Proxy
objetos Harmony :) Los setters parecen una buena idea, ¿pero eso no nos obligaría a modificar los objetos reales? Además, en una nota al margen,Object.create
podría usarse aquí (de nuevo, suponiendo que el navegador moderno permita el segundo parámetro). Además, el setter / getter podría usarse para 'proyectar' un valor diferente para el objeto y el elemento DOM :). Me pregunto si también tiene alguna idea sobre la plantilla, eso parece un verdadero desafío aquí, especialmente para estructurar bien :)Proxy
, como dijiste;) Comprendí que el desafío era mantener sincronizadas dos propiedades distintas. Mi método elimina uno de los dos.Proxy
eliminaría la necesidad de usar getters / setters, podría vincular elementos sin saber qué propiedades tienen. Lo que quise decir es que los captadores pueden cambiar más que bindTo.value, pueden contener lógica (y tal vez incluso una plantilla). La pregunta es cómo mantener este tipo de enlace bidireccional con una plantilla en mente. Digamos que estoy asignando mi objeto a un formulario, me gustaría mantener el elemento y el formulario sincronizados y me pregunto cómo continuaría con ese tipo de cosas. Puede ver cómo funciona eso en knockout learn.knockoutjs.com/#/?tutorial=intro por ejemploCreo que mi respuesta será más técnica, pero no diferente, ya que los demás presentan lo mismo usando diferentes técnicas.
Entonces, primero lo primero, la solución a este problema es el uso de un patrón de diseño conocido como "observador", que le permite desacoplar sus datos de su presentación, haciendo que el cambio en una cosa sea transmitido a sus oyentes, pero en este caso Está hecho de dos vías.
Para la forma DOM a JS
Para vincular los datos del DOM al objeto js, puede agregar marcado en forma de
data
atributos (o clases si necesita compatibilidad), de esta manera:De esta manera se puede acceder a través de js usando
querySelectorAll
(o el viejo amigogetElementsByClassName
para compatibilidad).Ahora puede vincular el evento escuchando los cambios de diferentes maneras: un oyente por objeto o un gran oyente al contenedor / documento. La vinculación al documento / contenedor activará el evento por cada cambio realizado en él o su hijo, tendrá una huella de memoria más pequeña pero generará llamadas de eventos.
El código se verá así:
Para el modo JS do DOM
Necesitará dos cosas: un metaobjeto que contendrá las referencias del elemento DOM de la bruja está vinculado a cada objeto / atributo js y una forma de escuchar los cambios en los objetos. Básicamente es de la misma manera: debe tener una forma de escuchar los cambios en el objeto y luego vincularlo al nodo DOM, ya que su objeto "no puede tener" metadatos necesitará otro objeto que contenga metadatos de alguna manera que el nombre de la propiedad se asigna a las propiedades del objeto de metadatos. El código será algo como esto:
Espero haber sido de ayuda.
fuente
Object.observe
ya que el soporte está presente solo en Chrome por ahora. caniuse.com/#feat=object-observeAyer, comencé a escribir mi propia forma de vincular datos.
Es muy divertido jugar con eso.
Creo que es hermoso y muy útil. Al menos en mis pruebas con Firefox y Chrome, Edge también debe funcionar. No estoy seguro acerca de los demás, pero si son compatibles con Proxy, creo que funcionará.
https://jsfiddle.net/2ozoovne/1/
Aquí está el código:
Luego, para configurar, solo:
Por ahora, acabo de agregar el enlace de valor HTMLInputElement.
Avísame si sabes cómo mejorarlo.
fuente
Hay una implementación básica muy simple de enlace de datos bidireccional en este enlace " Enlace de datos bidireccional fácil en JavaScript"
El enlace anterior junto con ideas de knockoutjs, backbone.js y agility.js, condujo a este marco MVVM ligero y rápido, ModelView.js
basado en jQueryque juega muy bien con jQuery y del cual soy el autor humilde (o tal vez no tan humilde).Reproduciendo el código de muestra a continuación (del enlace de la publicación del blog ):
Código de muestra para DataBinder
fuente
Cambiar el valor de un elemento puede desencadenar un evento DOM . Los oyentes que responden a eventos se pueden usar para implementar el enlace de datos en JavaScript.
Por ejemplo:
Aquí hay un código y una demostración que muestra cómo los elementos DOM pueden vincularse entre sí o con un objeto JavaScript.
fuente
Vincula cualquier entrada html
define dos funciones:
usa las funciones:
fuente
Aquí hay una idea con la
Object.defineProperty
cual se modifica directamente la forma en que se accede a una propiedad.Código:
Uso:
violín: aquí
fuente
He revisado algunos ejemplos básicos de javascript usando los controladores de eventos onkeypress y onchange para hacer una vista vinculante a nuestros js y js para ver
Aquí ejemplo plunker http://plnkr.co/edit/7hSOIFRTvqLAvdZT4Bcc?p=preview
fuente
fuente
Una forma simple de vincular una variable a una entrada (enlace bidireccional) es simplemente acceder directamente al elemento de entrada en el captador y definidor:
En HTML:
Y para usar:
Una forma más elegante de hacer lo anterior sin getter / setter:
Usar:
fuente
Es un enlace de datos bidireccional muy simple en vainilla javascript ...
fuente
Tarde a la fiesta, especialmente desde que escribí 2 libs relacionadas hace meses / años, las mencionaré más tarde, pero aún me parece relevante. Para hacerlo realmente corto, las tecnologías de mi elección son:
Proxy
para observación del modeloMutationObserver
para el seguimiento de cambios de DOM (por razones vinculantes, no cambios de valor)addEventListener
controladores regularesEn mi humilde opinión, además del OP, es importante que la implementación de enlace de datos:
user.address.block
shift
,splice
y por igual)Tomando todo eso en consideración, en mi opinión, hace imposible lanzar algunas docenas de líneas JS. Traté de hacerlo como un patrón en lugar de lib , no funcionó para mí.
A continuación, la
Object.observe
eliminación se elimina y, sin embargo, dado que la observación del modelo es una parte crucial, toda esta parte DEBE estar separada de la preocupación por otra lib. Ahora hasta el punto de los principales de cómo tomé este problema, exactamente como OP preguntó:Modelo (parte JS)
Mi opinión sobre la observación del modelo es Proxy , es la única forma sensata de hacerlo funcionar, en mi humilde opinión. Completamente presentado
observer
merece su propia biblioteca, por lo que he desarrollado unaobject-observer
biblioteca con ese único propósito.El / los modelo / s deben registrarse a través de alguna API dedicada, ese es el punto donde los POJO se convierten en
Observable
s, no puedo ver ningún acceso directo aquí. Los elementos DOM que se consideran vistas enlazadas (ver a continuación), se actualizan con los valores del modelo / s al principio y luego con cada cambio de datos.Vistas (parte HTML)
En mi humilde opinión, la forma más limpia de expresar el enlace, es a través de atributos. Muchos hicieron esto antes y muchos lo harán después, así que no hay noticias aquí, esta es solo una forma correcta de hacerlo. En mi caso, elegí la siguiente sintaxis:
<span data-tie="modelKey:path.to.data => targerProperty"></span>
pero esto es menos importante. Lo que es importante para mí, no hay una sintaxis de secuencias de comandos compleja en el HTML; esto está mal, de nuevo, en mi humilde opinión.Todos los elementos designados para ser vistas vinculadas se recopilarán al principio. Me parece inevitable desde el punto de vista del rendimiento administrar un mapeo interno entre los modelos y las vistas, parece un caso correcto donde se debería sacrificar la memoria + algo de administración para guardar las búsquedas y actualizaciones en tiempo de ejecución.
Las vistas se actualizan primero desde el modelo, si está disponible y luego de cambios posteriores, como dijimos. Más aún, todo el DOM debe observarse por medio de
MutationObserver
para reaccionar (vincular / desvincular) en los elementos añadidos / eliminados / modificados dinámicamente. Además, todo esto debe replicarse en ShadowDOM (abrir uno, por supuesto) para no dejar agujeros negros sin unir.La lista de detalles puede ir más allá, pero en mi opinión, esos son los principios principales que harían que el enlace de datos se implementara con un buen equilibrio de integridad de características de uno y la simplicidad sensata del otro lado.
Y por lo tanto, además de lo
object-observer
mencionado anteriormente, también he escrito unadata-tier
biblioteca, que implementa el enlace de datos a lo largo de los conceptos mencionados anteriormente.fuente
Las cosas han cambiado mucho en los últimos 7 años, ahora tenemos componentes web nativos en la mayoría de los navegadores. En mi opinión, el núcleo del problema es compartir el estado entre los elementos, una vez que tiene que es trivial actualizar la interfaz de usuario cuando el estado cambia y viceversa.
Para compartir datos entre elementos, puede crear una clase StateObserver y ampliar sus componentes web a partir de eso. Una implementación mínima se parece a esto:
violín aquí
Me gusta este enfoque porque:
data-
propiedadesfuente