Por favor tratar esta cuestión estrictamente educativo. Todavía estoy interesado en escuchar nuevas respuestas e ideas para implementar este

tl;dr

¿Cómo puedo implementar bi-direccional de enlace de datos con JavaScript?

De Enlace de datos para el DOM

Por enlace de datos para el DOM me refiero a que por ejemplo, tener un objeto de JavaScript a con una propiedad b. Luego tener un <input> elemento de DOM (por ejemplo), cuando el elemento DOM cambios, a cambios y viceversa (es decir, me refiero bidireccional de enlace de datos).

Aquí está un diagrama de AngularJS en lo que parece:

Cómo Implementar DOM Enlace de Datos en JavaScript

Así que básicamente he JavaScript similar a:

var a = {b:3};

A continuación, una entrada (o de otro tipo) elemento como:

<input type='text' value=''>

Me gustaría que el valor de entrada para ser a.b‘s valor (por ejemplo), y cuando el texto de entrada, cambios, me gustaría a.b a cambiar demasiado. Cuando a.b cambios en JavaScript, los cambios en la entrada.

La Pregunta

¿Cuáles son algunas técnicas básicas para lograr esto en la llanura de JavaScript?

En específico, me gustaría una buena respuesta para referirse a:

  • Cómo sería vinculante de trabajo para los objetos?
  • Cómo escuchar a cambiar en la forma podría funcionar?
  • Es posible de una manera sencilla de tener sólo el HTML modificado en el nivel de la plantilla? Me gustaría no tener que seguir la pista de la unión en el documento HTML en sí, sino sólo en JavaScript (con eventos DOM, JavaScript y mantener la referencia a los elementos del DOM utilizado).

Lo he intentado?

Soy un gran fan de Bigote, así que he intentado usarlo para la creación de plantillas. Sin embargo, me encontré con problemas al intentar realizar el enlace de datos desde Bigote procesos de HTML como una cadena así que después de obtener su resultado no tengo ninguna referencia al lugar donde los objetos en mi viewmodel son. La única solución que yo podía pensar de esto fue la modificación de la cadena HTML (o creado árbol DOM), en sí, con sus atributos. No me importa usar un motor de plantillas diferentes.

Básicamente, tengo una fuerte sensación de que me estaba complicando el tema en cuestión y no hay una solución simple.

Nota: por Favor, no dar respuestas que el uso de librerías externas, especialmente aquellas que tienen miles de líneas de código. Yo he usado (y como!) AngularJS y KnockoutJS. Yo realmente no quiero respuestas en el formulario de ‘uso marco de la x’. De manera óptima, me gustaría un futuro lector que no sabe cómo usar muchos marcos para comprender cómo implementar bi-direccional de enlace de datos misma. Yo no espero un completa respuesta, pero una que se obtiene de la idea a través de.

  • Yo basados CrazyGlue sobre Benjamin Gruenbaum del diseño. También es compatible SELECCIONAR, checkbox y radio etiquetas. jQuery es una dependencia.
  • Esta pregunta es totalmente impresionante. Si alguna vez se cierra por ser off-topic o algún otro tonto tonterías, voy a estar seriamente enfadado.
  • gracias por mencionar su CrazyGlue proyecto. He estado buscando una simple 2 datos de forma de cuaderno por un largo tiempo. Parece que no estás usando el Objeto.observar de modo que su navegador soporte debe ser grande. Y no usa bigote de plantillas para su perfecto.
  • ¿Qué hacer?
  • en mi opinión, el enfoque correcto es crear el DOM en JS (como Reaccionar) y no viceversa. Yo pienso que al final eso es lo que vamos a hacer.
  • ¿Eso significa que el JS «intercepta» o se ocupa de todo devuelto por el servidor y recrea lo que el servidor envía, todas las marcas, todo?
  • JS representa el DOM en el servidor y, a continuación, monta el DOM en el cliente. El cliente también puede representar a partir de cero. La parte divertida es que puedes obtener al instante las cargas con plena JS.
  • De un futuro lector, Gracias!

13 Comentarios

  1. 100
    • Cómo sería vinculante de trabajo para los objetos?
    • Cómo escuchar a cambiar en la forma podría funcionar?

    Una abstracción que las actualizaciones de ambos objetos

    Supongo que hay otras técnicas, pero en última instancia, me gustaría tener un objeto que contiene la referencia a una relacionada con el elemento de DOM, y proporciona una interfaz que coordina la actualización de sus propios datos y su elemento relacionado.

    La .addEventListener() proporciona una muy buena interfaz para ello. Que se le puede dar a un objeto que implementa la eventListener de la interfaz, y se va a invocar sus controladores con ese objeto como el this valor.

    Esto le da acceso automático a ambos el elemento y sus datos relacionados.

    La definición de su objeto de

    Prototípico de la herencia es una buena manera de implementar esto, aunque no es obligatorio, por supuesto. Primero te gustaría crear un constructor que recibe su elemento y algunos datos iniciales.

    function MyCtor(element, data) {
        this.data = data;
        this.element = element;
        element.value = data;
        element.addEventListener("change", this, false);
    }

    Así que aquí el constructor almacena el elemento de datos y en las propiedades del nuevo objeto. También se une un change evento a la element. Lo interesante es que pasa el nuevo objeto en lugar de una función como segundo argumento. Pero esto por sí solo no va a funcionar.

    La aplicación de la eventListener interfaz

    Para hacer este trabajo, su objeto debe aplicar la eventListener de la interfaz. Todo lo que se necesita para lograr esto es para dar el objeto de un handleEvent() método.

    Que es donde la herencia viene en.

    MyCtor.prototype.handleEvent = function(event) {
        switch (event.type) {
            case "change": this.change(this.element.value);
        }
    };
    
    MyCtor.prototype.change = function(value) {
        this.data = value;
        this.element.value = value;
    };

    Hay muchas maneras diferentes en que esto podría ser estructurado, pero para el ejemplo de la coordinación de las actualizaciones, me decidí a hacer el change() método sólo acepta un valor, y tener la handleEvent pasar ese valor en lugar del objeto de evento. De esta manera el change() puede ser invocada sin un evento así.

    Así que ahora, cuando la change evento ocurre, se actualizará tanto el elemento y el .data de la propiedad. Y lo mismo ocurrirá cuando llame .change() en su programa de JavaScript.

    Utilizando el código

    Ahora que acaba de crear el nuevo objeto, y se deja llevar a cabo las actualizaciones. Actualizaciones en el código JS aparecerá en la entrada, y los eventos de cambio de la entrada será visible para el código JS.

    var obj = new MyCtor(document.getElementById("foo"), "20");
    
    //simulate some JS based changes.
    var i = 0;
    setInterval(function() {
        obj.change(parseInt(obj.element.value) + ++i);
    }, 3000);

    DEMO: http://jsfiddle.net/RkTMD/

    • +1 Muy limpio enfoque, muy pocas palabras y lo suficientemente simple como para las personas a aprender, mucho más limpio que lo que yo tenía. Un caso de uso habitual es el uso de plantillas de código para representar los objetos de vistas. Me preguntaba cómo podría funcionar esto aquí? En buscadores como Bigote hago algo Mustache.render(template,object), suponiendo que quiero mantener un objeto sincronizado con la plantilla (no específico para el Bigote) , ¿cómo puedo seguir hablando de eso?
    • No he usado plantillas de lado del cliente, pero me imagino que Bigote tiene algunos sintaxis para la identificación de los puntos de inserción, y que la sintaxis incluye una etiqueta. Así que yo creo que la «estática» de las piezas de la plantilla sería dictada en trozos de HTML almacenados en una Matriz, y las partes dinámicas que iría entre los trozos. A continuación, las etiquetas en los puntos de inserción sería utilizado como propiedades del objeto. Entonces, si algunos input es la actualización de uno de esos puntos, no sería una asignación de la entrada a ese punto. Voy a ver si me puede venir para arriba con un ejemplo rápido.
    • Hmmm… no he pensado acerca de cómo limpiamente coordinar dos elementos diferentes. Este es un poco más complicado de lo que pensé en un principio. Tengo curiosidad, aunque, así que necesitamos trabajar en esto un poco más adelante. 🙂
    • Limpia la coordinación de dos diferentes elementos parece muy interesante para mí, es lo que estoy más atascado en. Cualquier sugerencia se agradece 🙂
    • Ugh… Firefox se estrelló justo en mí mientras comentar. De todos modos, no estoy seguro exactamente lo que usted busca, pero este es un ejemplo que utiliza un identificador de sintaxis de la plantilla para apuntar los puntos de inserción. La plantilla también incluye un input con el mismo ID que la inserción identificador (menos el ::: parte que usted va a ver). En lugar del identificador, se crea un nuevo <span> con un IDENTIFICADOR el identificador + "__target__". A continuación, el input y la span se dan a MyCtor, que coordina la actualización de los datos de entrada y de destino.
    • Verás que hay una primaria Template constructor que hace el análisis, contiene las diferentes MyCtor objetos, y proporciona una interfaz para actualizar cada uno por su identificador. Déjeme saber si usted tiene preguntas. 🙂 EDITAR: …el uso de este enlace en lugar… me había olvidado que tenía un aumento exponencial en el valor de la entrada cada 10 segundos para demostrar JS actualizaciones. Esta límites de la misma.
    • totalmente comentó versión además de mejoras de menor importancia.
    • ¿por qué la comunidad wiki esta respuesta? Que bien merecen la reputación esto le dará a usted así como la etiqueta de puntuación, es su propio trabajo y hay varias otras respuestas.
    • Yo wiki todas mis respuestas ahora. No se preocupan por el rep.
    • Creo que su respuesta a esta pregunta muy elegante. He aprendido mucho de él. Una cosa queda claro para mí: cuando ‘addEventListener’ para el ‘cambio’ de evento, ¿por qué comprobar el tipo de evento en ‘handleEvent’? no debe ningún otro evento de incendio. Estoy tratando de entender..
    • Buena pregunta. Fue realmente sólo para hacerlo explícitamente claro lo que está pasando. Una persona que en su lugar podría utilizar el event.type directamente como el nombre de la propiedad, como: this[event.type](this.element.value), sin embargo, creo que es una buena idea para comprobar que el controlador existe, así como una comprobación de validez, ya que las cosas pueden ser pasados por alto cuando se realizan cambios. Así if (this[event.type]) { this[event.type](this.element.value); } else { console.log("Expected %s handler", event.type); } supongo que con sólo un controlador podría estar codificados en su lugar, pero esto toma el cuidado de las cosas para cuando se agregan más.
    • …también tenga en cuenta que poner el elemento en una propiedad del objeto no es realmente necesario. Los elementos enlazados está disponible a través de la event objeto como event.currentTarget. Sin embargo, puede ser útil si desea utilizar el objeto fuera de los controladores.
    • Estoy de acuerdo en que la unión del elemento hace que sea más fácil. Para mi limitada experiencia con javascript me han reestructurado su guión un poco de bits. Tal vez esto ayude a alguien: enlace. Entiendo la razón por la que usted elija para usar ‘este.el cambio’, pero también he eliminado la configuración de elemento.valor’ con la misma en caso de un evento.
    • Esto puede ser una pregunta estúpida, pero sólo por eso tengo claro. Todas las actualizaciones realizadas a la js objeto en el código, debe llamar a que los objetos .la función de cambio?
    • En mi humilde opinión, en esta demo, si cambia el valor de la entrada y dejar el foco, handleEvent de captura de este evento de cambio(actualización). Para este tipo de cambio, se debe llamar explícitamente .change . Espero que esto le ayudará a: el uso de handleEvent() para detectar los eventos

  2. 34

    Así que, me decidí a lanzar mi propia solución en el bote. Aquí está una trabajo violín. Nota: esto sólo funciona en los navegadores modernos.

    Lo que se utiliza

    Esta aplicación es muy moderno – se requiere una (muy) navegador moderno y usuarios de dos nuevas tecnologías:

    • MutationObservers para detectar cambios en el dom (detectores de eventos se utilizan así)
    • Objeto.observar para detectar cambios en el objeto y de la notificación de la dom. Peligro, ya que esta respuesta ha sido escrito O. s ha sido discutido y decidido en contra de la ECMAScript TC, considere la posibilidad de un polyfill.

    Cómo funciona

    • En el elemento, poner un domAttribute:objAttribute asignación, por ejemplo bind='textContent:name'
    • De leer que en el enlace de la función. Observar cambios tanto en el elemento y el objeto.
    • Cuando se produce un cambio – actualizar el elemento relevante.

    La solución

    Aquí es el dataBind función, tenga en cuenta que sólo el 20 líneas de código y puede ser más breve:

    function dataBind(domElement, obj) {    
        var bind = domElement.getAttribute("bind").split(":");
        var domAttr = bind[0].trim(); //the attribute on the DOM element
        var itemAttr = bind[1].trim(); //the attribute the object
    
        //when the object changes - update the DOM
        Object.observe(obj, function (change) {
            domElement[domAttr] = obj[itemAttr]; 
        });
        //when the dom changes - update the object
        new MutationObserver(updateObj).observe(domElement, { 
            attributes: true,
            childList: true,
            characterData: true
        });
        domElement.addEventListener("keyup", updateObj);
        domElement.addEventListener("click",updateObj);
        function updateObj(){
            obj[itemAttr] = domElement[domAttr];   
        }
        //start the cycle by taking the attribute from the object and updating it.
        domElement[domAttr] = obj[itemAttr]; 
    }

    Aquí es algunos de uso:

    HTML:

    <div id='projection' bind='textContent:name'></div>
    <input type='text' id='textView' bind='value:name' />

    JavaScript:

    var obj = {
        name: "Benjamin"
    };
    var el = document.getElementById("textView");
    dataBind(el, obj);
    var field = document.getElementById("projection");
    dataBind(field,obj);

    Aquí es un trabajo violín. Tenga en cuenta que esta solución es bastante genérico. Objeto.observar y mutación observador que el calzado está disponible.

    • Me acaba de pasar a escribir esto (es5) para la diversión, si alguien la encuentra útil – tú mismo jsfiddle.net/P9rMm
    • Tenga en cuenta que cuando obj.name tiene un setter no puede ser observada de forma externa, sino que debe de difusión que ha cambiado desde dentro de la incubadora – html5rocks.com/en/tutorials/es7/observe/#toc-notifications – un poco arroja una llave en las obras de O. o() si desea más complejo, interdependiente comportamiento mediante incubadoras. Además, cuando obj.name no es configurable, redefiniendo es setter (con varios trucos para agregar notificación) también no es permitido – para los medicamentos genéricos con el O. o() son totalmente desguazado en ese caso específico.
    • Objeto.observar es eliminado de todos los navegadores: caniuse.com/#feat=object-observe
    • Un Proxy puede ser utilizado en lugar de Objeto.observar, o github.com/anywhichway/proxy-observe o gist.github.com/ebidel/1b553d571f924da2da06 o el antiguo polyfills, también en github @JvdBerg
  3. 27

    Me gustaría añadir a mi preposter. Sugiero un enfoque ligeramente diferente que permitirá que usted simplemente asignar un nuevo valor a su objeto sin el uso de un método. Cabe señalar sin embargo que esto no es apoyado por sobre todo los mayores navegadores y IE9 todavía requiere el uso de una interfaz diferente.

    La más destacable es que mi enfoque no hacer uso de eventos.

    Getters y Setters

    Mi propuesta hace uso de la relativamente joven característica de getters y setters, particularmente incubadoras sólo. Generalmente hablando, los modificadores nos permite «personalizar» el comportamiento de cómo ciertas propiedades se les asigna un valor y se recupera.

    Una aplicación que voy a utilizar aquí es el Objeto.defineProperty método. Funciona en FireFox, GoogleChrome y – creo – IE9. No he probado otros navegadores, pero ya que esta es la teoría sólo…

    De todos modos, se acepta tres parámetros. El primer parámetro el objeto que desea definir una nueva propiedad, la segunda una cadena que se asemeja a la del nombre de la nueva propiedad y la última un descriptor de «objeto» proporcionar información sobre el comportamiento de la nueva propiedad.

    Dos especialmente interesantes los descriptores de get y set. Un ejemplo sería algo como lo siguiente. Tenga en cuenta que el uso de estos dos prohíbe el uso de los otros 4 descriptores.

    function MyCtor( bindTo ) {
        //I'll omit parameter validation here.
    
        Object.defineProperty(this, 'value', {
            enumerable: true,
            get : function ( ) {
                return bindTo.value;
            },
            set : function ( val ) {
                bindTo.value = val;
            }
        });
    }

    Ahora haciendo uso de este se convierte en ligeramente diferente:

    var obj = new MyCtor(document.getElementById('foo')),
        i = 0;
    setInterval(function() {
        obj.value += ++i;
    }, 3000);

    Quiero recalcar que esto solo funciona para los navegadores modernos.

    De trabajo violín: http://jsfiddle.net/Derija93/RkTMD/1/

    • Si sólo tuviéramos la Armonía Proxy objetos 🙂 Incubadoras hacer parecer una buena idea, pero no que nos obligan a modificar los objetos reales? También, en una nota de lado – Object.create podría ser utilizado aquí (de nuevo, suponiendo navegador moderno que permitió el segundo parámetro). También, el setter/getter podría ser utilizado para el ‘proyecto’ un valor diferente para el objeto y el elemento DOM 🙂 . Me pregunto si usted tiene algún conocimiento de plantillas también, que parece un auténtico desafío, especialmente a la estructura de bien 🙂
    • Al igual que mi preposter, yo no trabajo mucho con el lado del cliente de las plantillas de los motores, lo siento. 🙁 Pero ¿qué significa modificar los objetos reales? Y me gustaría entender sus pensamientos de cómo llegó a entender que el setter/getter podría ser utilizado para …. Los getters/setters aquí se utilizan para nada, pero redirigir todas las aportaciones y recuperaciones del objeto al elemento DOM, básicamente, como un Proxy, como usted dijo. 😉 Yo entendía el desafío de mantener dos propiedades distintas sincronizado. Mi método, se elimina uno de los dos.
    • Un Proxy eliminaría la necesidad de utilizar los métodos getter/setter, usted podría enlazar elementos sin saber qué propiedades tienen. Lo que quiero decir, es que los captadores pueden cambiar más de bindTo.valor que puede contener la lógica (y tal vez incluso una plantilla). La pregunta es cómo mantener este tipo de enlace bidireccional con una plantilla en la mente? Digamos que estoy de asignación mi objeto a un formulario, me gustaría mantener tanto el elemento y la forma sincronizada y me pregunto cómo me gustaría seguir hablando de ese tipo de cosas. Usted puede comprobar fuera de cómo funciona en knockout learn.knockoutjs.com/#/?tutorial=intro por ejemplo
    • Gotcha. Voy a darle un vistazo.
    • Ya veo lo que estamos tratando de entender. La configuración de todo esto con las plantillas de la mente resulta ser un poco más difícil. Voy a estar trabajando en la este secuencia de comandos por un tiempo (y continuamente se rebase). Pero por ahora, me estoy tomando un descanso. Yo en realidad no tengo tiempo para esto.
    • Me gusta mucho este estilo, pero debe editar para agregar enumerable:true que es necesario si se desea iterar a través de las propiedades o stringify el objeto.
    • Buena llamada. Agregó.

  4. 7

    Creo que mi respuesta será más técnico, pero no diferente, ya que los otros presentan la misma cosa con diferentes técnicas.

    Así que, lo primero es lo primero, la solución a este problema es el uso de un patrón de diseño conocido como «observador», que permite separar los datos de su presentación, haciendo el cambio de una cosa ser transmitido a sus oyentes, pero en este caso está hecho de dos vías.

    Para el DOM a JS manera

    Para enlazar los datos de la DOM para el js objeto puede agregar marcado en el formulario de data atributos (o clases si necesita compatibilidad), como este:

    <input type="text" data-object="a" data-property="b" id="b" class="bind" value=""/>
    <input type="text" data-object="a" data-property="c" id="c" class="bind" value=""/>
    <input type="text" data-object="d" data-property="e" id="e" class="bind" value=""/>

    De esta manera se puede acceder a través de js usando querySelectorAll (o el viejo amigo getElementsByClassName para la compatibilidad).

    Ahora puede enlazar el evento escuchar a los cambios en las formas: uno escucha por objeto o un gran oyente del contenedor/documento. Enlace al documento/contenedor se disparará el evento por cada cambio realizado en él o es niño, willhave un menor consumo de memoria, pero se generan las llamadas de evento.

    El código se verá algo como esto:

    //Bind to each element
    var elements = document.querySelectorAll('input[data-property]');
    
    function toJS(){
        //Assuming `a` is in scope of the document
        var obj = document[this.data.object];
        obj[this.data.property] = this.value;
    }
    
    elements.forEach(function(el){
        el.addEventListener('change', toJS, false);
    }
    
    //Bind to document
    function toJS2(){
        if (this.data && this.data.object) {
            //Again, assuming `a` is in document's scope
            var obj = document[this.data.object];
            obj[this.data.property] = this.value;
        }
    }
    
    document.addEventListener('change', toJS2, false);

    Para el JS hacer DOM manera

    Se necesitan dos cosas: un meta-objeto que contendrá las referencias de bruja elemento DOM está designada a cada objeto js/atributo y una manera de escuchar a los cambios en los objetos. Se trata básicamente de la misma manera: usted tiene que tener una manera de escuchar a los cambios en el objeto y, a continuación, se unen el nodo DOM, como su objeto «no puedo» metadatos tendrá otro objeto que contiene los metadatos de una manera que el nombre de la propiedad se asigna a los metadatos de las propiedades del objeto.
    El código será algo como esto:

    var a = {
            b: 'foo',
            c: 'bar'
        },
        d = {
            e: 'baz'
        },
        metadata = {
            b: 'b',
            c: 'c',
            e: 'e'
        };
    function toDOM(changes){
        //changes is an array of objects changed and what happened
        //for now i'd recommend a polyfill as this syntax is still a proposal
        changes.forEach(function(change){
            var element = document.getElementById(metadata[change.name]);
            element.value = change.object[change.name];
        });
    }
    //Side note: you can also use currying to fix the second argument of the function (the toDOM method)
    Object.observe(a, toDOM);
    Object.observe(d, toDOM);

    Espero que haya sido de ayuda.

    • no hay comparabilidad problema con el uso de la .el observador?
    • por ahora se necesita una placa de apoyo o de relleno para Object.observe como apoyo está presente sólo en chrome por ahora. caniuse.com/#feat=object-observe
    • Objeto.observar está muerto. Sólo pensé en cuenta que aquí.
    • ¿Cuál es la cosa correcta a utilizar ahora, ya que esto está muerto?
    • si no estoy equivocado sería proxy trampas, ya que permiten un control más granular de lo que puedo hacer con un objeto, pero tengo que investigar eso.
  5. 7

    Ayer, empecé a escribir mi propia manera de enlazar los datos.

    Es muy divertido jugar con él.

    Creo que es hermoso y muy útil. Al menos en mis pruebas con firefox y chrome, el Borde debe también funciona. No estoy seguro acerca de los demás, pero si el soporte de Proxy, creo que va a trabajar.

    https://jsfiddle.net/2ozoovne/1/

    <H1>Bind Context 1</H1>
    <input id='a' data-bind='data.test' placeholder='Button Text' />
    <input id='b' data-bind='data.test' placeholder='Button Text' />
    <input type=button id='c' data-bind='data.test' />
    <H1>Bind Context 2</H1>
    <input id='d' data-bind='data.otherTest' placeholder='input bind' />
    <input id='e' data-bind='data.otherTest' placeholder='input bind' />
    <input id='f' data-bind='data.test' placeholder='button 2 text - same var name, other context' />
    <input type=button id='g' data-bind='data.test' value='click here!' />
    <H1>No bind data</H1>
    <input id='h' placeholder='not bound' />
    <input id='i' placeholder='not bound'/>
    <input type=button id='j' />

    Aquí está el código:

    (function(){
    if ( ! ( 'SmartBind' in window ) ) { //never run more than once
    //This hack sets a "proxy" property for HTMLInputElement.value set property
    var nativeHTMLInputElementValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
    var newDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
    newDescriptor.set=function( value ){
    if ( 'settingDomBind' in this )
    return;
    var hasDataBind=this.hasAttribute('data-bind');
    if ( hasDataBind ) {
    this.settingDomBind=true;
    var dataBind=this.getAttribute('data-bind');
    if ( ! this.hasAttribute('data-bind-context-id') ) {
    console.error("Impossible to recover data-bind-context-id attribute", this, dataBind );
    } else {
    var bindContextId=this.getAttribute('data-bind-context-id');
    if ( bindContextId in SmartBind.contexts ) {
    var bindContext=SmartBind.contexts[bindContextId];
    var dataTarget=SmartBind.getDataTarget(bindContext, dataBind);
    SmartBind.setDataValue( dataTarget, value);
    } else {
    console.error( "Invalid data-bind-context-id attribute", this, dataBind, bindContextId );
    }
    }
    delete this.settingDomBind;
    }
    nativeHTMLInputElementValue.set.bind(this)( value );
    }
    Object.defineProperty(HTMLInputElement.prototype, 'value', newDescriptor);
    var uid= function(){
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
    });
    }
    //SmartBind Functions
    window.SmartBind={};
    SmartBind.BindContext=function(){
    var _data={};
    var ctx = {
    "id" : uid()    /* Data Bind Context Id */
    , "_data": _data        /* Real data object */
    , "mapDom": {}          /* DOM Mapped objects */
    , "mapDataTarget": {}       /* Data Mapped objects */
    }
    SmartBind.contexts[ctx.id]=ctx;
    ctx.data=new Proxy( _data, SmartBind.getProxyHandler(ctx, "data"))  /* Proxy object to _data */
    return ctx;
    }
    SmartBind.getDataTarget=function(bindContext, bindPath){
    var bindedObject=
    { bindContext: bindContext
    , bindPath: bindPath 
    };
    var dataObj=bindContext;
    var dataObjLevels=bindPath.split('.');
    for( var i=0; i<dataObjLevels.length; i++ ) {
    if ( i == dataObjLevels.length-1 ) { //last level, set value
    bindedObject={ target: dataObj
    , item: dataObjLevels[i]
    }
    } else {    //digg in
    if ( ! ( dataObjLevels[i] in dataObj ) ) {
    console.warn("Impossible to get data target object to map bind.", bindPath, bindContext);
    break;
    }
    dataObj=dataObj[dataObjLevels[i]];
    }
    }
    return bindedObject ;
    }
    SmartBind.contexts={};
    SmartBind.add=function(bindContext, domObj){
    if ( typeof domObj == "undefined" ){
    console.error("No DOM Object argument given ", bindContext);
    return;
    }
    if ( ! domObj.hasAttribute('data-bind') ) {
    console.warn("Object has no data-bind attribute", domObj);
    return;
    }
    domObj.setAttribute("data-bind-context-id", bindContext.id);
    var bindPath=domObj.getAttribute('data-bind');
    if ( bindPath in bindContext.mapDom ) {
    bindContext.mapDom[bindPath][bindContext.mapDom[bindPath].length]=domObj;
    } else {
    bindContext.mapDom[bindPath]=[domObj];
    }
    var bindTarget=SmartBind.getDataTarget(bindContext, bindPath);
    bindContext.mapDataTarget[bindPath]=bindTarget;
    domObj.addEventListener('input', function(){ SmartBind.setDataValue(bindTarget,this.value); } );
    domObj.addEventListener('change', function(){ SmartBind.setDataValue(bindTarget, this.value); } );
    }
    SmartBind.setDataValue=function(bindTarget,value){
    if ( ! ( 'target' in bindTarget ) ) {
    var lBindTarget=SmartBind.getDataTarget(bindTarget.bindContext, bindTarget.bindPath);
    if ( 'target' in lBindTarget ) {
    bindTarget.target=lBindTarget.target;
    bindTarget.item=lBindTarget.item;
    } else {
    console.warn("Still can't recover the object to bind", bindTarget.bindPath );
    }
    }
    if ( ( 'target' in bindTarget ) ) {
    bindTarget.target[bindTarget.item]=value;
    }
    }
    SmartBind.getDataValue=function(bindTarget){
    if ( ! ( 'target' in bindTarget ) ) {
    var lBindTarget=SmartBind.getDataTarget(bindTarget.bindContext, bindTarget.bindPath);
    if ( 'target' in lBindTarget ) {
    bindTarget.target=lBindTarget.target;
    bindTarget.item=lBindTarget.item;
    } else {
    console.warn("Still can't recover the object to bind", bindTarget.bindPath );
    }
    }
    if ( ( 'target' in bindTarget ) ) {
    return bindTarget.target[bindTarget.item];
    }
    }
    SmartBind.getProxyHandler=function(bindContext, bindPath){
    return  {
    get: function(target, name){
    if ( name == '__isProxy' )
    return true;
    //just get the value
    //console.debug("proxy get", bindPath, name, target[name]);
    return target[name];
    }
    ,
    set: function(target, name, value){
    target[name]=value;
    bindContext.mapDataTarget[bindPath+"."+name]=value;
    SmartBind.processBindToDom(bindContext, bindPath+"."+name);
    //console.debug("proxy set", bindPath, name, target[name], value );
    //and set all related objects with this target.name
    if ( value instanceof Object) {
    if ( !( name in target) || ! ( target[name].__isProxy ) ){
    target[name]=new Proxy(value, SmartBind.getProxyHandler(bindContext, bindPath+'.'+name));
    }
    //run all tree to set proxies when necessary
    var objKeys=Object.keys(value);
    //console.debug("...objkeys",objKeys);
    for ( var i=0; i<objKeys.length; i++ ) {
    bindContext.mapDataTarget[bindPath+"."+name+"."+objKeys[i]]=target[name][objKeys[i]];
    if ( typeof value[objKeys[i]] == 'undefined' || value[objKeys[i]] == null || ! ( value[objKeys[i]] instanceof Object ) || value[objKeys[i]].__isProxy )
    continue;
    target[name][objKeys[i]]=new Proxy( value[objKeys[i]], SmartBind.getProxyHandler(bindContext, bindPath+'.'+name+"."+objKeys[i]));
    }
    //TODO it can be faster than run all items
    var bindKeys=Object.keys(bindContext.mapDom);
    for ( var i=0; i<bindKeys.length; i++ ) {
    //console.log("test...", bindKeys[i], " for ", bindPath+"."+name);
    if ( bindKeys[i].startsWith(bindPath+"."+name) ) {
    //console.log("its ok, lets update dom...", bindKeys[i]);
    SmartBind.processBindToDom( bindContext, bindKeys[i] );
    }
    }
    }
    return true;
    }
    };
    }
    SmartBind.processBindToDom=function(bindContext, bindPath) {
    var domList=bindContext.mapDom[bindPath];
    if ( typeof domList != 'undefined' ) {
    try {
    for ( var i=0; i < domList.length ; i++){
    var dataTarget=SmartBind.getDataTarget(bindContext, bindPath);
    if ( 'target' in dataTarget )
    domList[i].value=dataTarget.target[dataTarget.item];
    else
    console.warn("Could not get data target", bindContext, bindPath);
    }
    } catch (e){
    console.warn("bind fail", bindPath, bindContext, e);
    }
    }
    }
    }
    })();

    Entonces, establecer, solo tienes que:

    var bindContext=SmartBind.BindContext();
    SmartBind.add(bindContext, document.getElementById('a'));
    SmartBind.add(bindContext, document.getElementById('b'));
    SmartBind.add(bindContext, document.getElementById('c'));
    var bindContext2=SmartBind.BindContext();
    SmartBind.add(bindContext2, document.getElementById('d'));
    SmartBind.add(bindContext2, document.getElementById('e'));
    SmartBind.add(bindContext2, document.getElementById('f'));
    SmartBind.add(bindContext2, document.getElementById('g'));
    setTimeout( function() {
    document.getElementById('b').value='Via Script works too!'
    }, 2000);
    document.getElementById('g').addEventListener('click',function(){
    bindContext2.data.test='Set by js value'
    })

    Por ahora, sólo he añadido el HTMLInputElement valor unen.

    Hágamelo saber si usted sabe cómo mejorarlo.

  6. 6

    Hay una muy simple simple aplicación de 2 vías de enlace de datos en este enlace «Fácil de Dos vías de Enlace de Datos en JavaScript».

    El enlace anterior, junto con las ideas de knockoutjs, backbone.js y agility.js, dirigido a este ligero y rápido MVVM marco, ModelView.js basado en jQuery que juega muy bien con jQuery y de los cuales yo soy el humilde (o tal vez no tan humilde) autor.

    La reproducción de código de ejemplo siguiente (de blog enlace):

    Código de ejemplo para DataBinder

    function DataBinder( object_id ) {
    //Use a jQuery object as simple PubSub
    var pubSub = jQuery({});
    //We expect a `data` element specifying the binding
    //in the form: data-bind-<object_id>="<property_name>"
    var data_attr = "bind-" + object_id,
    message = object_id + ":change";
    //Listen to change events on elements with the data-binding attribute and proxy
    //them to the PubSub, so that the change is "broadcasted" to all connected objects
    jQuery( document ).on( "change", "[data-" + data_attr + "]", function( evt ) {
    var $input = jQuery( this );
    pubSub.trigger( message, [ $input.data( data_attr ), $input.val() ] );
    });
    //PubSub propagates changes to all bound elements, setting value of
    //input tags or HTML content of other tags
    pubSub.on( message, function( evt, prop_name, new_val ) {
    jQuery( "[data-" + data_attr + "=" + prop_name + "]" ).each( function() {
    var $bound = jQuery( this );
    if ( $bound.is("input, textarea, select") ) {
    $bound.val( new_val );
    } else {
    $bound.html( new_val );
    }
    });
    });
    return pubSub;
    }

    Por lo que se refiere al objeto de JavaScript, una implementación mínima de un
    Modelo de usuario para el bien de este experimento podría ser el siguiente:

    function User( uid ) {
    var binder = new DataBinder( uid ),
    user = {
    attributes: {},
    //The attribute setter publish changes using the DataBinder PubSub
    set: function( attr_name, val ) {
    this.attributes[ attr_name ] = val;
    binder.trigger( uid + ":change", [ attr_name, val, this ] );
    },
    get: function( attr_name ) {
    return this.attributes[ attr_name ];
    },
    _binder: binder
    };
    //Subscribe to the PubSub
    binder.on( uid + ":change", function( evt, attr_name, new_val, initiator ) {
    if ( initiator !== user ) {
    user.set( attr_name, new_val );
    }
    });
    return user;
    }

    Ahora, cada vez que queremos enlazar un modelo de la propiedad de un pedazo de la interfaz de usuario nos
    sólo hay que establecer una adecuada atributo de datos en el correspondiente
    Elemento HTML:

    //javascript
    var user = new User( 123 );
    user.set( "name", "Wolfgang" );
    <!-- html -->
    <input type="number" data-bind-123="name" />
    • Mientras que este vínculo puede responder a la pregunta, es mejor incluir a las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Enlace-sólo respuestas puede ser válido si la página enlazada cambios.
    • señaló, probablemente voy a actualizar cuando tengo más tiempo, ya que es un lugar mucho código para una respuesta post
    • reproducido código de ejemplo en la respuesta de referencia de enlace (aunque yo thinbk esto crea duplicarte el contenido de la mayoría de las veces, de todos modos)
    • Definitivamente crear contenido duplicado, pero ese es el punto – blog enlaces a menudo puede romper con el tiempo, y mediante la duplicación de la relevante el contenido aquí se asegura que estará disponible y útil para los lectores futuros. La respuesta se ve genial ahora!
  7. 3

    Cambiar el valor de un elemento puede desencadenar un Eventos DOM. Los oyentes que responder a los eventos que pueden ser utilizados para implementar el enlace de datos en JavaScript.

    Por ejemplo:

    function bindValues(id1, id2) {
    const e1 = document.getElementById(id1);
    const e2 = document.getElementById(id2);
    e1.addEventListener('input', function(event) {
    e2.value = event.target.value;
    });
    e2.addEventListener('input', function(event) {
    e1.value = event.target.value;
    });
    }

    Aquí es de código y un demo que muestra cómo los elementos del DOM puede ser obligado con otros o con un objeto de JavaScript.

  8. 3

    Enlazar cualquier entrada html

    <input id="element-to-bind" type="text">

    definir dos funciones:

    function bindValue(objectToBind) {
    var elemToBind = document.getElementById(objectToBind.id)    
    elemToBind.addEventListener("change", function() {
    objectToBind.value = this.value;
    })
    }
    function proxify(id) { 
    var handler = {
    set: function(target, key, value, receiver) {
    target[key] = value;
    document.getElementById(target.id).value = value;
    return Reflect.set(target, key, value);
    },
    }
    return new Proxy({id: id}, handler);
    }

    utilizar las funciones de:

    var myObject = proxify('element-to-bind')
    bindValue(myObject);
  9. 2

    He ido a través de algunos conceptos básicos de javascript ejemplo de uso de onkeypress y controladores de eventos onchange para hacer vinculante la vista a nuestro js y js para ver

    Aquí ejemplo plunker http://plnkr.co/edit/7hSOIFRTvqLAvdZT4Bcc?p=preview

    <!DOCTYPE html>
    <html>
    <body>
    <p>Two way binding data.</p>
    <p>Binding data from  view to JS</p>
    <input type="text" onkeypress="myFunction()" id="myinput">
    <p id="myid"></p>
    <p>Binding data from  js to view</p>
    <input type="text" id="myid2" onkeypress="myFunction1()" oninput="myFunction1()">
    <p id="myid3" onkeypress="myFunction1()" id="myinput" oninput="myFunction1()"></p>
    <script>
    document.getElementById('myid2').value="myvalue from script";
    document.getElementById('myid3').innerHTML="myvalue from script";
    function myFunction() {
    document.getElementById('myid').innerHTML=document.getElementById('myinput').value;
    }
    document.getElementById("myinput").onchange=function(){
    myFunction();
    }
    document.getElementById("myinput").oninput=function(){
    myFunction();
    }
    function myFunction1() {
    document.getElementById('myid3').innerHTML=document.getElementById('myid2').value;
    }
    </script>
    </body>
    </html>
  10. 2
    <!DOCTYPE html>
    <html>
    <head>
    <title>Test</title>
    </head>
    <body>
    <input type="text" id="demo" name="">
    <p id="view"></p>
    <script type="text/javascript">
    var id = document.getElementById('demo');
    var view = document.getElementById('view');
    id.addEventListener('input', function(evt){
    view.innerHTML = this.value;
    });
    </script>
    </body>
    </html>
  11. 2

    Una forma sencilla de enlace de una variable de entrada (enlace bidireccional) es acceder directamente el elemento de entrada en los getter y setter:

    var variable = function(element){                    
    return {
    get : function () { return element.value;},
    set : function (value) { element.value = value;} 
    }
    };

    En HTML:

    <input id="an-input" />
    <input id="another-input" />

    Y para el uso:

    var myVar = new variable(document.getElementById("an-input"));
    myVar.set(10);
    //and another example:
    var myVar2 = new variable(document.getElementById("another-input"));
    myVar.set(myVar2.get());


    Un aficionado manera de hacer lo anterior sin getter/setter:

    var variable = function(element){
    return function () {
    if(arguments.length > 0)                        
    element.value = arguments[0];                                           
    else return element.value;                                                  
    }
    }

    A utilizar:

    var v1 = new variable(document.getElementById("an-input"));
    v1(10); //sets value to 20.
    console.log(v1()); //reads value.
  12. 2

    He aquí una idea utilizando Object.defineProperty que modifica directamente la manera en que una propiedad se accede.

    Código:

    function bind(base, el, varname) {
    Object.defineProperty(base, varname, {
    get: () => {
    return el.value;
    },
    set: (value) => {
    el.value = value;
    }
    })
    }

    Uso:

    var p = new some_class();
    bind(p,document.getElementById("someID"),'variable');
    p.variable="yes"

    violín: Aquí

  13. 1

    Es muy simple de dos vías de enlace de datos en javascript….

    <input type="text" id="inp" onkeyup="document.getElementById('name').innerHTML=document.getElementById('inp').value;">
    <div id="name">
    </div>

    • sin duda, esto solo funciona con el evento onkeyup? es decir, si usted hizo una petición ajax, y luego cambió el innerHTML a través de JavaScript, a continuación, esto no iba a funcionar

Dejar respuesta

Please enter your comment!
Please enter your name here