web-dev-qa-db-fra.com

Comment implémenter la liaison de données DOM en JavaScript

Veuillez traiter cette question comme strictement éducative. Je suis toujours intéressé par de nouvelles réponses et idées pour mettre cela en œuvre

tl; dr

Comment implémenter la liaison de données bidirectionnelle avec JavaScript?

Liaison de données au DOM

Par liaison de données au DOM, j'entends par exemple un objet JavaScript a avec une propriété b. Puis, ayant un élément DOM <input> (par exemple), lorsque l’élément DOM change, a change et inversement (c’est-à-dire que je parle de liaison de données bidirectionnelle). 

Voici un diagramme de AngularJS sur quoi cela ressemble:

two way data binding

Donc, fondamentalement, j'ai JavaScript similaire à:

var a = {b:3};

Ensuite, un élément input (ou autre formulaire) comme:

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

J'aimerais que la valeur de l'entrée soit la valeur de a.b (par exemple), et lorsque le texte saisi change, j'aimerais que a.b change également. Lorsque a.b change en JavaScript, l'entrée change.

La question

Quelles sont les techniques de base pour accomplir cela en JavaScript?

En particulier, je voudrais une bonne réponse à se référer à:

  • Comment la liaison fonctionnerait-elle pour les objets?
  • Comment l'écoute d'un changement dans la forme peut-elle fonctionner?
  • Est-il possible de manière simple de ne modifier que le code HTML au niveau du modèle? J'aimerais ne pas garder une trace de la liaison dans le document HTML lui-même, mais uniquement en JavaScript (avec les événements DOM et JavaScript en conservant une référence aux éléments DOM utilisés).

Qu'ai-je essayé?

Je suis un grand fan de Moustache, j'ai donc essayé de l'utiliser pour les modèles. Cependant, je me suis heurté à des problèmes lorsque j'essayais de faire la liaison de données, car Moustache traitait HTML en tant que chaîne. Par conséquent, après avoir obtenu son résultat, je n'ai aucune référence à l'emplacement des objets dans mon modèle de vue. La seule solution à laquelle je pouvais penser était de modifier la chaîne HTML (ou l’arborescence DOM créée) avec des attributs. Cela ne me dérange pas d'utiliser un moteur de templates différent.

En gros, j'ai eu le fort sentiment de compliquer le problème et il existe une solution simple.

Remarque: Veuillez ne pas fournir de réponses qui utilisent des bibliothèques externes, en particulier des milliers de lignes de code. J'ai utilisé (et j'aime!) AngularJS et KnockoutJS. Je ne veux vraiment pas de réponses sous la forme 'use framework x'. De manière optimale, j'aimerais un futur lecteur qui ne sait pas utiliser plusieurs frameworks pour comprendre comment implémenter lui-même la liaison de données bidirectionnelle. Je ne m'attends pas à une réponse complète, mais à une idée qui fait passer le message.

225
  • Comment la liaison fonctionnerait-elle pour les objets?
  • Comment l'écoute d'un changement dans la forme peut-elle fonctionner?

Une abstraction qui met à jour les deux objets

Je suppose qu’il existe d’autres techniques, mais qu’en fin de compte, j’aurais un objet qui fasse référence à un élément DOM associé et qui fournit une interface qui coordonne les mises à jour de ses propres données et de son élément associé.

La .addEventListener() fournit une interface très agréable pour cela. Vous pouvez lui donner un objet qui implémente l'interface eventListener et il invoquera ses gestionnaires avec cet objet comme valeur this.

Cela vous donne un accès automatique à l'élément et à ses données associées.

Définir votre objet

L'héritage prototypique est un bon moyen d'implémenter cela, bien que cela ne soit pas obligatoire. Tout d'abord, vous créez un constructeur qui reçoit votre élément et des données initiales.

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

Donc, ici, le constructeur stocke l'élément et les données sur les propriétés du nouvel objet. Il lie également un événement change à la element donnée. La chose intéressante est qu'il passe le nouvel objet au lieu d'une fonction comme second argument. _ {Mais cela seul ne fonctionnera pas. 

Implémentation de l'interface eventListener

Pour que cela fonctionne, votre objet doit implémenter l'interface eventListener. Pour ce faire, il suffit de donner à l'objet une méthode handleEvent()

C'est là que l'héritage entre en jeu.

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;
};

Cela pourrait être structuré de différentes manières, mais pour votre exemple de coordination des mises à jour, j'ai décidé de faire en sorte que la méthode change() n'accepte qu'une valeur et que la variable handleEvent la transmette au lieu de l'objet événement. De cette façon, la change() peut également être invoquée sans événement.

Alors maintenant, lorsque l'événement change se produit, il met à jour l'élément et la propriété .data. Et la même chose se produira lorsque vous appelez .change() dans votre programme JavaScript.

En utilisant le code

Vous venez maintenant de créer le nouvel objet et de le laisser effectuer les mises à jour. Les mises à jour dans le code JS apparaîtront sur l'entrée et les événements de modification sur l'entrée seront visibles pour le code 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/

100
user1106925

J'ai donc décidé de lancer ma propre solution dans le pot. Voici un violon travaillant . Notez que cela ne fonctionne que sur des navigateurs très modernes.

Ce qu'il utilise

Cette implémentation est très moderne - elle nécessite un navigateur (très) moderne et deux nouvelles technologies:

  • MutationObservers pour détecter les modifications dans le dom (les écouteurs d'événements sont également utilisés)
  • Object.observe pour détecter les modifications de l'objet et notifier le dom. Danger, puisque cette réponse a été écrite, O.o a été discuté et décidé par le TC ECMAScript, considérons un remplissage multiple}.

Comment ça marche

  • Sur l'élément, placez un mappage domAttribute:objAttribute - par exemple bind='textContent:name'
  • Lisez cela dans la fonction dataBind. Observez les modifications apportées à l'élément et à l'objet. 
  • Lorsqu'un changement survient, mettez à jour l'élément correspondant.

La solution

Voici la fonction dataBind, notez qu’elle ne contient que 20 lignes de code et peut être plus courte:

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]; 
}

Voici quelques utilisations:

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);

Voici un violon travaillant . Notez que cette solution est assez générique. Le calage de l'observateur de mutation et de l'observant de mutation est disponible.

34
Benjamin Gruenbaum

Je voudrais ajouter à mon préposter. Je suggère une approche légèrement différente qui vous permettra d'attribuer simplement une nouvelle valeur à votre objet sans utiliser de méthode. Il convient de noter cependant que cela n'est pas pris en charge par les navigateurs particulièrement anciens et que IE9 nécessite toujours l'utilisation d'une interface différente.

Le plus remarquable est que mon approche n’utilise pas les événements.

Getters et Setters

Ma proposition utilise la caractéristique relativement récente des getters et setters , en particulier des setters. De manière générale, les mutateurs nous permettent de "personnaliser" le comportement de la manière dont certaines propriétés reçoivent une valeur et sont extraites.

Une implémentation que je vais utiliser ici est la méthode Object.defineProperty . Cela fonctionne dans FireFox, GoogleChrome et - je pense - IE9. N'a pas testé d'autres navigateurs, mais puisque c'est de la théorie seulement ...

Quoi qu'il en soit, il accepte trois paramètres. Le premier paramètre est l'objet pour lequel vous souhaitez définir une nouvelle propriété, le second une chaîne ressemblant au nom de la nouvelle propriété et le dernier un "objet descripteur" fournissant des informations sur le comportement de la nouvelle propriété.

get et set sont deux descripteurs particulièrement intéressants. Un exemple ressemblerait à ce qui suit. Notez que l'utilisation de ces deux empêche l'utilisation des 4 autres descripteurs.

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;
        }
    });
}

Maintenant, en faire usage devient légèrement différent:

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

Je tiens à souligner que cela ne fonctionne que pour les navigateurs modernes.

Violon de travail: http://jsfiddle.net/Derija93/RkTMD/1/

24
Kiruse

Je pense que ma réponse sera plus technique, mais pas différente car les autres présentent la même chose en utilisant des techniques différentes.
Donc, tout d’abord, la solution à ce problème consiste à utiliser un motif de conception appelé "observateur", il vous permet de découpler vos données de votre présentation, de manière à ce que le changement d’une chose soit diffusé à leurs auditeurs, mais dans ce cas, c'est fait dans les deux sens.

Pour le DOM to JS way

Pour lier les données du DOM à l'objet js, vous pouvez ajouter un balisage sous la forme d'attributs data (ou de classes si vous avez besoin de compatibilité), comme ceci:

<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 cette manière, vous pouvez y accéder via js en utilisant querySelectorAll (ou le vieil ami getElementsByClassName pour compatibilité).

Maintenant, vous pouvez lier l'événement en écoutant les modifications de différentes manières: un écouteur par objet ou un gros écouteur au conteneur/document. La liaison au document/conteneur déclenchera l'événement pour chaque modification apportée à celui-ci ou à son enfant, il aura une empreinte mémoire plus petite mais générera des appels d'événement.
Le code ressemblera à quelque chose comme ceci:

//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);

Pour le JS do DOM

Vous aurez besoin de deux choses: un méta-objet qui contiendra les références de l’élément DOM est lié à chaque objet/attribut js et un moyen d’écouter les modifications apportées aux objets. C’est fondamentalement la même chose: vous devez avoir un moyen d’écouter les modifications apportées à l’objet, puis de le lier au nœud DOM, car votre objet ne peut pas avoir de métadonnées. que le nom de propriété mappe sur les propriétés de l'objet de métadonnées . Le code ressemblera à ceci:

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);

J'espère que j'ai été utile.

7
madcampos

Hier, j'ai commencé à écrire ma propre façon de lier des données.

C'est très amusant de jouer avec.

Je pense que c'est beau et très utile. Au moins sur mes tests utilisant Firefox et Chrome, Edge doit également fonctionner. Pas sûr des autres, mais s'ils supportent Proxy, je pense que cela fonctionnera.

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' />

Voici le code:

(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);
                }
            }
        }
    }
})();

Ensuite, pour définir, il suffit de:

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'
})

Pour l'instant, je viens d'ajouter la liaison de valeur HTMLInputElement.

Faites-moi savoir si vous savez comment l'améliorer.

7
ton

Il existe une très simple implémentation à nu de la liaison de données bidirectionnelle dans ce lien "Liaison de données bidirectionnelle facile en JavaScript"

Le lien précédent ainsi que les idées de knockoutjs, backbone.js et agility.js ont conduit à ce framework MVVM léger et rapide, ModelView.js basé sur jQuery qui joue bien avec jQuery et dont je suis l'auteur humble (ou peut-être pas si humble).

Reproduction de l'exemple de code ci-dessous (à partir de lien vers un article de blog ):

Exemple de code pour 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;
}

Pour ce qui concerne l'objet JavaScript, une implémentation minimale d'un Le modèle utilisateur pour cette expérience pourrait être le suivant:

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;
}

Maintenant, chaque fois que nous voulons lier la propriété d’un modèle à une partie de l’interface utilisateur, nous suffit de définir un attribut de données approprié sur le fichier .__ correspondant. Élément HTML:

// javascript
var user = new User( 123 );
user.set( "name", "Wolfgang" );

<!-- html -->
<input type="number" data-bind-123="name" />
6
Nikos M.

Lier n'importe quelle entrée HTML 

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

définir deux fonctions:

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);
}

utiliser les fonctions:

var myObject = proxify('element-to-bind')
bindValue(myObject);
2
o-t-w
<!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>
1
Anthony Newlineinfo

Un moyen simple de lier une variable à une entrée (liaison bidirectionnelle) consiste à accéder directement à l'élément input dans les méthodes getter et 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" />

Et à utiliser:

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());


Une manière plus sophistiquée de faire ce qui précède sans getter/setter:

var variable = function(element){

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

                    else return element.value;                                                  
                }

        }

Utiliser:

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

Je suis passé en revue quelques exemples javascript de base en utilisant les gestionnaires d’événements onkeypress et onchange pour rendre la vue obligatoire de nos js et js

Voici l'exemple 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>
1
macha devendher

Changer la valeur d'un élément peut déclencher un événement DOM . Les écouteurs qui répondent aux événements peuvent être utilisés pour implémenter la liaison de données en JavaScript.

Par exemple:

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;
  });
}

Ici est du code et une démonstration montrant comment les éléments DOM peuvent être liés les uns aux autres ou à un objet JavaScript.

1
amusingmaker

Voici une idée utilisant Object.defineProperty qui modifie directement la façon dont une propriété est accédée. 

Code:

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

Usage:

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

p.variable="yes"

violon: ici

0
Thornkey

Il s'agit d'une liaison de données bidirectionnelle très simple dans Vanilla javascript ....

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

<div id="name">

</div>

0
Subodh Gawade