जावास्क्रिप्ट में DOM डेटा बाइंडिंग कैसे लागू करें


244

कृपया इस प्रश्न को कड़ाई से शैक्षिक मानें। मुझे अभी भी इसे लागू करने के लिए नए जवाब और विचारों को सुनने में दिलचस्पी है

tl; डॉ

मैं जावास्क्रिप्ट के साथ द्वि-दिशात्मक डेटा-बाध्यकारी कैसे लागू करूंगा?

DOM को डेटा बाइंडिंग

DOM से डेटा बाइंडिंग के लिए उदाहरण के लिए मेरा मतलब है, aएक संपत्ति के साथ एक जावास्क्रिप्ट ऑब्जेक्ट होना b। तब एक <input>DOM एलिमेंट होना (उदाहरण के लिए), जब DOM एलिमेंट बदलता है, aबदलता है और इसके विपरीत (अर्थात, मेरा मतलब है द्विदिशीय बाइंडिंग बाइंडिंग)।

यहाँ AngularJS का एक चित्र इस प्रकार दिखता है:

दो तरह से डेटा बाइंडिंग

तो मूल रूप से मेरे पास जावास्क्रिप्ट के समान है:

var a = {b:3};

फिर एक इनपुट (या अन्य रूप) जैसे तत्व:

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

मुझे इनपुट का मान पसंद है a.b(उदाहरण के लिए), और जब इनपुट पाठ बदलता है, तो मैं a.bभी बदलना चाहूंगा । जब a.bजावास्क्रिप्ट में परिवर्तन होता है, तो इनपुट बदल जाता है।

प्रश्न

सादे जावास्क्रिप्ट में इसे पूरा करने के लिए कुछ बुनियादी तकनीकें क्या हैं?

विशिष्ट रूप में, मैं एक अच्छा उत्तर देना चाहूंगा:

  • वस्तुओं के लिए बाध्यकारी कार्य कैसे होगा?
  • कैसे काम में बदलाव सुनने के लिए काम कर सकता है?
  • क्या यह सरल तरीके से संभव है कि केवल टेम्पलेट स्तर पर HTML संशोधित किया गया है? मैं स्वयं HTML दस्तावेज़ में बाइंडिंग का ट्रैक नहीं रखना चाहता, लेकिन केवल जावास्क्रिप्ट में (DOM इवेंट्स के साथ, और JavaScript DOM तत्वों के संदर्भ में रखते हुए)।

मैंने क्या कोशिश की है?

मैं मूंछों का बहुत बड़ा प्रशंसक हूं, इसलिए मैंने इसे टेम्प्लेट करने के लिए उपयोग करने की कोशिश की। हालाँकि, मैं मुद्दों में भाग गया जब डेटा को स्वयं बाध्यकारी करने की कोशिश कर रहा था क्योंकि मूंछें HTML को एक स्ट्रिंग के रूप में संसाधित करती हैं, इसलिए जब मुझे इसका परिणाम मिलता है तो मेरे पास कोई संदर्भ नहीं होता है कि मेरे दृष्टिकोण में ऑब्जेक्ट कहाँ हैं। केवल इसके लिए मैं सोच सकता था कि HTML स्ट्रिंग (या निर्मित DOM ट्री) को विशेषताओं के साथ संशोधित कर रहा था। मुझे अलग टेंपलेटिंग इंजन का उपयोग करने में कोई आपत्ति नहीं है।

मूल रूप से, मुझे एक मजबूत भावना मिली कि मैं इस मुद्दे को हाथ में ले रहा था और एक सरल उपाय है।

नोट: कृपया ऐसे उत्तर प्रदान न करें जो बाहरी पुस्तकालयों का उपयोग करते हैं, विशेष रूप से वे जो कोड की हजारों लाइनें हैं। मैंने AngularJS और KnockoutJS का उपयोग किया है (और पसंद है!)। मैं वास्तव में 'उपयोग रूपरेखा x' के रूप में उत्तर नहीं चाहता। वैकल्पिक रूप से, मैं एक भविष्य के पाठक को पसंद करूंगा, जो खुद को द्वि-दिशात्मक डेटा-बाइंडिंग को लागू करने के तरीके को समझने के लिए कई रूपरेखाओं का उपयोग करना नहीं जानता है। मैं एक पूर्ण उत्तर की उम्मीद नहीं करता हूं , लेकिन एक जिसे विचार मिलता है।


2
मैं बेंजामिन Gruenbaum के डिजाइन पर CrazyGlue आधारित है । यह SELECT, चेकबॉक्स और रेडियो टैग का भी समर्थन करता है। jQuery एक निर्भरता है।
जॉनज़

12
यह सवाल पूरी तरह से भयानक है। अगर यह कभी भी ऑफ-टॉपिक या कुछ अन्य मूर्खतापूर्ण बकवास के लिए बंद हो जाता है, तो मैं गंभीरता से टिक जाऊंगा।
OCDev

@JohnSz अपने CrazyGlue परियोजना का उल्लेख करने के लिए धन्यवाद। मैं एक लंबे समय के लिए एक सरल 2 रास्ता डेटा बाइंडर के लिए खोज रहा हूं। ऐसा लगता है कि आप ऑब्जेक्ट का उपयोग नहीं कर रहे हैं। इसलिए आपके ब्राउज़र का समर्थन बहुत अच्छा होना चाहिए। और आप मूंछें टेम्प्लेटिंग का उपयोग नहीं कर रहे हैं तो यह एकदम सही है।
गैविन

@ बैंजामिन आपने क्या किया?
जॉनी

@ मेरी राय में सही दृष्टिकोण जेएस में डोम को बनाने के लिए है (जैसे रिएक्ट) और इसके विपरीत नहीं। मुझे लगता है कि आखिरकार हम यही करेंगे।
बेंजामिन ग्रुएनबाम

जवाबों:


106
  • वस्तुओं के लिए बाध्यकारी कार्य कैसे होगा?
  • कैसे काम में बदलाव सुनने के लिए काम कर सकता है?

एक अमूर्त जो दोनों वस्तुओं को अद्यतन करता है

मुझे लगता है कि अन्य तकनीकें हैं, लेकिन अंततः मेरे पास एक ऑब्जेक्ट होगा जो संबंधित डोम तत्व का संदर्भ रखता है, और एक इंटरफ़ेस प्रदान करता है जो अपने स्वयं के डेटा और इसके संबंधित तत्व के अपडेट को समन्वयित करता है।

इसके .addEventListener()लिए एक बहुत अच्छा इंटरफ़ेस प्रदान करता है। आप इसे एक ऑब्जेक्ट दे सकते हैं जो eventListenerइंटरफ़ेस को लागू करता है, और यह thisमूल्य के रूप में उस वस्तु के साथ अपने हैंडलर को आमंत्रित करेगा ।

यह आपको तत्व और उससे संबंधित डेटा दोनों के लिए स्वचालित पहुंच प्रदान करता है।

अपनी वस्तु को परिभाषित करना

प्रोटोटाइप इनहेरिटेंस इसे लागू करने का एक अच्छा तरीका है, हालांकि कोर्स की आवश्यकता नहीं है। सबसे पहले आप एक कंस्ट्रक्टर बनाएंगे जो आपके तत्व और कुछ प्रारंभिक डेटा प्राप्त करता है।

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

इसलिए यहां कंस्ट्रक्टर नई वस्तु के गुणों पर तत्व और डेटा संग्रहीत करता है। यह किसी changeदिए गए कार्यक्रम को भी बांधता है element। दिलचस्प बात यह है कि यह एक फ़ंक्शन के बजाय नए ऑब्जेक्ट को दूसरे तर्क के रूप में पारित करता है। लेकिन यह अकेले काम नहीं करेगा।

eventListenerइंटरफ़ेस को लागू करना

इस काम को करने के लिए, आपके ऑब्जेक्ट को eventListenerइंटरफ़ेस को लागू करने की आवश्यकता है । इसे पूरा करने के लिए आवश्यक सभी वस्तु को एक handleEvent()विधि देना है।

यहीं से विरासत आती है।

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

ऐसे कई अलग-अलग तरीके हैं जिनसे इसे संरचित किया जा सकता है, लेकिन समन्वयन अपडेट के आपके उदाहरण के लिए, मैंने इस change()पद्धति को केवल एक मान स्वीकार करने का निर्णय लिया , और handleEventइवेंट ऑब्जेक्ट के बजाय उस मान को पास किया। इस तरह change()एक घटना के बिना भी लागू किया जा सकता है।

तो अब, जब changeघटना होती है, तो यह तत्व और .dataसंपत्ति दोनों को अपडेट करेगा । और ऐसा ही तब होगा जब आप .change()अपने जावास्क्रिप्ट प्रोग्राम में कॉल करेंगे ।

कोड का उपयोग करना

अब आप सिर्फ नई वस्तु बनाएँगे, और इसे अद्यतन करने देंगे। जेएस कोड में अपडेट इनपुट पर दिखाई देगा, और इनपुट पर परिवर्तन की घटनाएं जेएस कोड को दिखाई देंगी।

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

डेमो: http://jsfiddle.net/RkTMD/


5
+1 बहुत साफ-सुथरा दृष्टिकोण, बहुत ही सरलता से और सरल तरीके से लोगों को सीखने के लिए काफी कुछ, जो मेरे पास था, उससे कहीं ज्यादा साफ-सुथरा। वस्तुओं के विचारों का प्रतिनिधित्व करने के लिए एक सामान्य उपयोग का मामला कोड में टेम्पलेट्स का उपयोग कर रहा है। मैं सोच रहा था कि यह यहाँ कैसे काम कर सकता है? मूंछें जैसे इंजन में मैं कुछ करता हूं Mustache.render(template,object), यह मानकर कि मैं किसी वस्तु को टेम्पलेट (मूंछ के लिए विशिष्ट नहीं) के साथ समन्वयित रखना चाहता हूं, मैं उस पर कैसे जाऊंगा?
बेंजामिन ग्रुएनबाम

3
@BenjaminGruenbaum: मैंने क्लाइंट-साइड टेम्प्लेट का उपयोग नहीं किया है, लेकिन मुझे लगता है कि मूंछें सम्मिलन बिंदुओं की पहचान करने के लिए कुछ सिंटैक्स हैं, और उस सिंटैक्स में एक लेबल शामिल है। इसलिए मुझे लगता है कि टेम्पलेट के "स्थिर" भागों को एक एरे में संग्रहीत HTML के विखंडन में प्रस्तुत किया जाएगा, और गतिशील भाग उन विखंडों के बीच जाएंगे। तब सम्मिलन बिंदुओं पर लेबल का उपयोग ऑब्जेक्ट गुण के रूप में किया जाएगा। फिर अगर कुछ inputको उन बिंदुओं में से एक को अपडेट करना है, तो इनपुट से उस बिंदु तक एक मैपिंग होगी। मैं देखूंगा कि क्या मैं एक त्वरित उदाहरण के साथ आ सकता हूं।

1
@BenjaminGruenbaum: हम्म् ... मैंने दो अलग-अलग तत्वों को कैसे साफ़ किया जाए, इस बारे में नहीं सोचा है। यह पहले की तुलना में थोड़ा अधिक शामिल है। मैं हालांकि उत्सुक हूं, इसलिए मुझे थोड़ी देर बाद इस पर काम करने की आवश्यकता हो सकती है। :)

2
आप देखेंगे कि एक प्राथमिक Templateकंस्ट्रक्टर है जो पार्सिंग करता है, विभिन्न MyCtorवस्तुओं को रखता है , और अपने पहचानकर्ता द्वारा प्रत्येक को अपडेट करने के लिए एक इंटरफ़ेस प्रदान करता है। यदि तुम्हारे सवाल हों तो मुझे बताओ। :) संपादित करें: ... इस लिंक का उपयोग करें ... मैं भूल गया था कि जेएस अपडेट प्रदर्शित करने के लिए मुझे हर 10 सेकंड में इनपुट मूल्य में एक घातीय वृद्धि हुई थी। यह इसे सीमित करता है।

2
... पूरी तरह से संस्करण के अलावा मामूली सुधार टिप्पणी की

36

इसलिए, मैंने अपना समाधान बर्तन में फेंकने का फैसला किया। यहाँ एक काम कर रहे बेला है । ध्यान दें कि यह बहुत ही आधुनिक ब्राउज़रों पर चलता है।

यह क्या उपयोग करता है

यह कार्यान्वयन बहुत आधुनिक है - इसके लिए एक (बहुत) आधुनिक ब्राउज़र और उपयोगकर्ताओं को दो नई तकनीकों की आवश्यकता है:

  • MutationObserverएस डोम में परिवर्तन (घटना श्रोताओं में अच्छी तरह से उपयोग किया जाता है) का पता लगाने के
  • Object.observeऑब्जेक्ट में परिवर्तन और डोम को सूचित करने के लिए। खतरे, चूंकि इस उत्तर को ओओ लिखा गया है और ईसीएमएस्क्रिप्टस्क्रिप्ट टीसी के खिलाफ चर्चा और निर्णय लिया गया है, एक पॉलीफिल पर विचार करें

यह काम किस प्रकार करता है

  • तत्व पर, एक domAttribute:objAttributeमानचित्रण डालें - उदाहरण के लिएbind='textContent:name'
  • डेटाबिंड फ़ंक्शन में पढ़ें। तत्व और वस्तु दोनों के परिवर्तनों को देखें।
  • जब कोई परिवर्तन होता है - संबंधित तत्व को अपडेट करें।

समाधान

यहां dataBindफ़ंक्शन है, ध्यान दें कि यह कोड की सिर्फ 20 लाइनें है और यह छोटी हो सकती है:

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

यहाँ कुछ उपयोग है:

HTML:

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

जावास्क्रिप्ट:

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

यहाँ एक काम कर रहे बेला है । ध्यान दें कि यह समाधान बहुत सामान्य है। Object.observe और उत्परिवर्तन पर्यवेक्षक shimming उपलब्ध है।


1
मैं सिर्फ मनोरंजन के लिए इसे (es5) लिखने के लिए हुआ था, अगर किसी को यह उपयोगी लगता है - अपने आप को jsfiddle.net/P9rMm
बेंजामिन Gruenbaum

1
ध्यान रखें कि जब obj.nameएक सेटर है तो इसे बाहरी रूप से नहीं देखा जा सकता है, लेकिन यह प्रसारित करना चाहिए कि यह सेटर के भीतर से बदल गया है - html5rocks.com/en/tutorials/es7/observe/#toc-notifications - थोरा कामों में एक रिंच बढ़ाता है Oo () के लिए यदि आप अधिक जटिल, अन्योन्याश्रित व्यवहार का उपयोग करना चाहते हैं। इसके अलावा, जब obj.nameयह कॉन्फ़िगर करने योग्य नहीं होता है , तो इसे फिर से परिभाषित करना (अधिसूचना जोड़ने के लिए विभिन्न ट्रिक्स के साथ) भी अनुमति नहीं है - इसलिए ओओ () के साथ जेनरिक उस विशिष्ट मामले में पूरी तरह से स्क्रैप हो जाते हैं।
नोलो

8
Object.observe को सभी ब्राउज़रों से हटा दिया जाता है: caniuse.com/#feat=object-observe
JvdBerg

1
एक प्रॉक्सी का उपयोग Object.observe, या github.com/anywhichway/proxy-observe या gist.github.com/ebidel/1b553d571f924da2da06 या पुराने पॉलीफ़िल्स के बजाय github @JvdBerg
jimmont

29

मैं अपने प्रीपोस्टर में जोड़ना चाहूंगा। मैं थोड़ा अलग दृष्टिकोण सुझाता हूं जो आपको एक विधि का उपयोग किए बिना बस अपनी वस्तु को एक नया मूल्य प्रदान करने की अनुमति देगा। यह ध्यान दिया जाना चाहिए कि यह विशेष रूप से पुराने ब्राउज़रों द्वारा समर्थित नहीं है और IE9 को अभी भी एक अलग इंटरफ़ेस के उपयोग की आवश्यकता है।

सबसे विशेष रूप से यह है कि मेरा दृष्टिकोण घटनाओं का उपयोग नहीं करता है।

गेटर्स एंड सेटर्स

मेरा प्रस्ताव गेटर्स और सेटर के अपेक्षाकृत युवा फीचर का उपयोग करता है , विशेष रूप से केवल सेटर करता है। सामान्यतया, म्यूटेटर हमें व्यवहार को "अनुकूलित" करने की अनुमति देते हैं कि कैसे कुछ गुणों को एक मान दिया जाता है और पुनः प्राप्त किया जाता है।

एक कार्यान्वयन जो मैं यहां उपयोग कर रहा हूं वह है Object.defineProperty विधि। यह FireFox, GoogleChrome में काम करता है और - मुझे लगता है - IE9। अन्य ब्राउज़रों का परीक्षण नहीं किया गया है, लेकिन चूंकि यह केवल सिद्धांत है ...

वैसे भी, यह तीन मापदंडों को स्वीकार करता है। पहला पैरामीटर वह वस्तु है जिसके लिए आप एक नई संपत्ति को परिभाषित करना चाहते हैं, दूसरी नई संपत्ति के नाम से संबंधित एक स्ट्रिंग और अंतिम एक "वर्णनात्मक वस्तु" जो नई संपत्ति के व्यवहार के बारे में जानकारी प्रदान करती है।

दो विशेष रूप से दिलचस्प वर्णनकर्ता हैं getऔर set। एक उदाहरण निम्नलिखित की तरह कुछ दिखेगा। ध्यान दें कि इन दोनों का उपयोग करने से अन्य 4 डिस्क्रिप्टर का उपयोग प्रतिबंधित है।

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

अब इसका उपयोग करना थोड़ा अलग हो गया है:

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

मैं इस बात पर जोर देना चाहता हूं कि यह केवल आधुनिक ब्राउज़रों के लिए काम करता है।

वर्किंग फ़िडल: http://jsfiddle.net/Derija93/RkTMD/1/


2
यदि केवल हमारे पास हार्मोनी Proxyऑब्जेक्ट्स होते हैं :) तो सेटर्स एक अच्छे विचार की तरह प्रतीत होते हैं, लेकिन क्या हमें वास्तविक वस्तुओं को संशोधित करने की आवश्यकता नहीं होगी? इसके अलावा, एक साइड नोट पर - Object.createयहां इस्तेमाल किया जा सकता है (फिर से, आधुनिक ब्राउज़र मानकर जो दूसरे पैरामीटर के लिए अनुमति देता है)। इसके अलावा, सेटर / गेट्टर का उपयोग वस्तु और DOM तत्व के लिए एक अलग मूल्य ':) के लिए किया जा सकता है। मैं सोच रहा था कि क्या आपके पास भी टेम्प्लेटिंग पर कोई अंतर्दृष्टि है, जो यहां एक वास्तविक चुनौती की तरह लगता है, विशेष रूप से अच्छी तरह से संरचना करने के लिए :)
बेंजामिन ग्रुएनबाम

मेरे पूर्व-पालक की तरह, मैं भी क्लाइंट-साइड टेंपलेटिंग इंजनों के साथ बहुत काम नहीं करता, क्षमा करें। :( लेकिन आपको वास्तविक वस्तुओं को संशोधित करने से क्या मतलब है ? और मैं आपके विचारों को समझना चाहता हूं कि आपको कैसे समझ में आया कि सेटर / गेट्टर का उपयोग किया जा सकता है ... यहां गेटर्स / सेटर कुछ भी नहीं के लिए उपयोग किए जाते हैं। लेकिन मूल तत्व से मूल रूप से Proxy(जैसे आपने कहा;) वस्तु से सभी इनपुट और पुनर्प्राप्ति को पुनर्निर्देशित करते हुए ; मैंने दो अलग-अलग गुणों को सिंक्रनाइज़ रखने के लिए चुनौती को समझा। मेरा तरीका दोनों में से एक को खत्म करता है।
Kiruse

Proxyगेटर्स / सेटर का उपयोग करने की आवश्यकता को समाप्त कर देगा, आप यह जानकर बिना तत्वों को बांध सकते हैं कि उनके पास क्या गुण हैं। मेरा मतलब है, यह है कि गेटर्स bindTo.value से अधिक बदल सकते हैं उनमें तर्क हो सकते हैं (और शायद एक टेम्पलेट भी)। सवाल यह है कि इस तरह के द्विदिशीय बंधन को एक टेम्पलेट के साथ कैसे बनाए रखा जाए? आइए कहते हैं कि मैं अपनी वस्तु को एक फॉर्म में मैप कर रहा हूं, मैं तत्व और फॉर्म दोनों को सिंक करना चाहता हूं और मैं सोच रहा हूं कि मैं उस तरह के बारे में कैसे कहूंगा। आप यह देख सकते हैं कि नॉकआउट में कैसे काम करता है सीखें ।knockoutjs.com/#/?tutorial=intro उदाहरण के लिए
बेंजामिन

@BenjaminGruenbaum गोछा। मैं इसे एक रूप दूँगा।
कियूरस

@BenjaminGruenbaum मैं देख रहा हूँ कि आप क्या समझने की कोशिश कर रहे हैं। मन में टेम्प्लेट के साथ यह सब सेट करना थोड़ा और कठिन हो जाता है। मैं थोड़ी देर के लिए इस स्क्रिप्ट पर काम कर रहा हूं (और लगातार इसे रीबेस कर सकता हूं )। लेकिन अभी के लिए, मैं एक ब्रेक ले रहा हूं। मेरे पास वास्तव में इसके लिए समय नहीं है।
Kiruse

7

मुझे लगता है कि मेरा उत्तर अधिक तकनीकी होगा, लेकिन अलग-अलग नहीं क्योंकि अन्य अलग-अलग तकनीकों का उपयोग करके एक ही चीज पेश करते हैं।
तो, पहली बात, इस समस्या का समाधान "पर्यवेक्षक" के रूप में जाना जाने वाला एक डिज़ाइन पैटर्न का उपयोग है, तो चलिए आप अपनी प्रस्तुति से अपने डेटा को कम कर देते हैं, जिससे एक चीज़ में परिवर्तन उनके श्रोताओं को प्रसारित किया जा सकता है, लेकिन इस मामले में यह दो तरह से बना है।

DOM से JS तरीके के लिए

DOM से js ऑब्जेक्ट में डेटा को बाँधने के लिए आप dataविशेषताओं के रूप में मार्कअप जोड़ सकते हैं (या यदि आपको अनुकूलता की आवश्यकता है तो कक्षाएं), इस तरह:

<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=""/>

इस तरह इसे js querySelectorAll(या getElementsByClassNameसंगतता के लिए पुराने मित्र ) का उपयोग करके एक्सेस किया जा सकता है ।

अब आप ईवेंट सुनने के तरीकों में बदलाव के लिए बाध्य कर सकते हैं: एक श्रोता प्रति वस्तु या एक बड़ा श्रोता कंटेनर / दस्तावेज़ में। दस्तावेज़ / कंटेनर में बांधने से इसमें किए गए प्रत्येक परिवर्तन के लिए ईवेंट को ट्रिगर किया जाएगा या यह बच्चा होगा, यह एक छोटा मेमोरी फ़ुटप्रिंट विघटित करेगा, लेकिन ईवेंट कॉल को प्रायोजित करेगा।
कोड कुछ इस तरह दिखेगा:

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

JS के लिए DOM तरीका है

आपको दो चीजों की आवश्यकता होगी: एक मेटा-ऑब्जेक्ट जो डायन के संदर्भ को धारण करेगा DOM तत्व प्रत्येक js ऑब्जेक्ट / विशेषता से बंधा हुआ है और वस्तुओं में परिवर्तन को सुनने का एक तरीका है। यह मूल रूप से एक ही तरीका है: आपके पास ऑब्जेक्ट में परिवर्तनों को सुनने और उसके बाद इसे DOM नोड में बाँधने का एक तरीका है, क्योंकि आपकी ऑब्जेक्ट में "मेटाडेटा" नहीं हो सकता है, आपको एक अन्य ऑब्जेक्ट की आवश्यकता होगी जो एक तरह से मेटाडेटा रखता है वह संपत्ति का नाम मेटाडेटा ऑब्जेक्ट के गुणों के लिए मैप करता है। कोड कुछ इस तरह होगा:

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

मुझे आशा है कि मैं मदद की थी।


.observer का उपयोग करने के साथ तुलनात्मक समस्या नहीं है?
मोहसेन साकिबा

अभी इसके लिए एक शिम या पॉलीफिल की जरूरत है Object.observeक्योंकि समर्थन अभी के लिए क्रोम में मौजूद है। caniuse.com/#feat=object-observe
मैडकैंपोस

9
ऑब्जेक्ट.बॉब मर चुका है। बस मैंने सोचा कि यहाँ ध्यान दें।
बेंजामिन ग्रुएनबाम

@BenjaminGruenbaum अभी क्या उपयोग करना सही है, क्योंकि यह मर चुका है?
जॉनी

1
@ जॉनी अगर मैं गलत नहीं हूँ तो यह प्रॉक्सी ट्रैप होगा क्योंकि वे एक वस्तु के साथ और अधिक दानेदार नियंत्रण की अनुमति दे सकते हैं, लेकिन मुझे इसकी जाँच करनी होगी।
14 अक्टूबर को madcampos

7

कल, मैंने डेटा को बाइंड करने का अपना तरीका लिखना शुरू कर दिया।

इसके साथ खेलना बहुत मज़ेदार है।

मुझे लगता है कि यह सुंदर और बहुत उपयोगी है। कम से कम फ़ायरफ़ॉक्स और क्रोम का उपयोग करके मेरे परीक्षणों पर, एज को भी काम करना चाहिए। दूसरों के बारे में निश्चित नहीं है, लेकिन अगर वे प्रॉक्सी का समर्थन करते हैं, तो मुझे लगता है कि यह काम करेगा।

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

यहाँ कोड है:

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

फिर, सेट करने के लिए, बस:

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

अभी के लिए, मैंने अभी-अभी HTMLInputElement value bind जोड़ा है।

अगर आप इसे सुधारना जानते हैं तो मुझे बताएं।


6

इस लिंक में 2-तरह से डेटा-बाइंडिंग का एक बहुत ही सरल नंगे भाग है "जावास्क्रिप्ट में आसान टू-वे डेटा बाइंडिंग"

पिछले लिंक नॉकआउट , बैकबोन.जेएस और एजिलिटी.जेएस के विचारों के साथ, इस लाइट-वेट और फास्ट एमवीवीएम फ्रेमवर्क, मॉडल व्यू.जेएस के लिए नेतृत्व किया jQuery के आधार पर जो jQuery के साथ अच्छी तरह से खेलता है और जिनमें से मैं विनम्र हूं (या शायद इतना विनम्र नहीं) लेखक।

नीचे नमूना कोड पुन: प्रस्तुत करना ( ब्लॉग पोस्ट लिंक से ):

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

जावास्क्रिप्ट ऑब्जेक्ट की चिंता के लिए, इस प्रयोग के लिए उपयोगकर्ता मॉडल का न्यूनतम कार्यान्वयन निम्नलिखित हो सकता है:

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

अब, जब भी हम किसी मॉडल की संपत्ति को UI के एक टुकड़े से बांधना चाहते हैं, तो हमें बस संबंधित HTML तत्वों पर एक उचित डेटा विशेषता सेट करना होगा:

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

<!-- html -->
<input type="number" data-bind-123="name" />

हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन उत्तर के आवश्यक भागों को शामिल करना बेहतर है और संदर्भ के लिए लिंक प्रदान करना है। लिंक-केवल उत्तर अमान्य हो सकते हैं यदि लिंक किए गए पृष्ठ बदल जाते हैं।
सैम हेनले

@sphanley, ने कहा, मैं शायद तब अपडेट करूंगा जब मेरे पास अधिक समय होगा, क्योंकि यह उत्तर पोस्ट के लिए एक लंबा कोड है
निकोस एम।

@ शेंलेली, संदर्भित लिंक से उत्तर पर नमूना कोड पुन: प्रस्तुत (हालांकि मैं इसे पतली बनाता हूं, वैसे भी ज्यादातर समय डुप्लिकेट सामग्री बनाता है)
निकोस एम।

1
यह निश्चित रूप से डुप्लिकेट सामग्री बनाता है, लेकिन वह बिंदु है - ब्लॉग लिंक अक्सर समय के साथ टूट सकते हैं, और यहां प्रासंगिक सामग्री को डुप्लिकेट करके यह सुनिश्चित करता है कि यह भविष्य के पाठकों के लिए उपलब्ध और उपयोगी होगा। जवाब अब बहुत अच्छा लग रहा है!
सैम हैनली

3

किसी तत्व के मान को बदलना एक DOM ईवेंट को ट्रिगर कर सकता है । घटनाओं पर प्रतिक्रिया देने वाले श्रोताओं का उपयोग जावास्क्रिप्ट में डेटा बाइंडिंग को लागू करने के लिए किया जा सकता है।

उदाहरण के लिए:

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

यहां कोड और एक डेमो है जो दिखाता है कि डोम तत्व एक दूसरे के साथ या जावास्क्रिप्ट ऑब्जेक्ट के साथ कैसे बाध्य हो सकते हैं।


3

किसी भी html इनपुट को बांधें

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

दो कार्यों को परिभाषित:

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

कार्यों का उपयोग करें:

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

3

यहां एक विचार है Object.definePropertyजिसका उपयोग करके सीधे किसी संपत्ति तक पहुंचने के तरीके को संशोधित किया जाता है।

कोड:

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

उपयोग:

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

p.variable="yes"

बेला: यहाँ


2

मैं अपने js और js को देखने के लिए बाध्यकारी दृश्य बनाने के लिए onkeypress और onchange घटना संचालकों का उपयोग करके कुछ मूल जावास्क्रिप्ट उदाहरण से गुजरा हूँ

यहाँ उदाहरण प्लंकर 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>

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>

2

एक चर को एक इनपुट (टू-वे बाइंडिंग) से बांधने का एक सरल तरीका है सीधे इनपुट तत्व को गेट्टर और सेटर में पहुँचाना:

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

HTML में:

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

और उपयोग करने के लिए:

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


बिना गेट्टर / सेटर के उपरोक्त कार्य करने का एक कट्टर तरीका:

var variable = function(element){

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

                    else return element.value;                                                  
                }

        }

काम में लाना:

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

1

यह वेनिला जावास्क्रिप्ट में डेटा बाइंडिंग के लिए बहुत ही सरल तरीका है ...।

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

<div id="name">

</div>


2
निश्चित रूप से यह केवल onkeyup घटना के साथ काम करेगा? यानी अगर आपने एक अजाक्स अनुरोध किया है, और फिर जावास्क्रिप्ट के माध्यम से
Zach स्मिथ

1

पार्टी के लिए देर से, विशेष रूप से जब से मैं 2 महीने से संबंधित वर्षों / वर्षों पहले लिखा है, मैं उन्हें बाद में उल्लेख करेंगे, लेकिन अभी भी मेरे लिए प्रासंगिक लग रहा है। इसे वास्तव में छोटा बिगाड़ने के लिए, मेरी पसंद की तकनीकें हैं:

  • Proxy मॉडल के अवलोकन के लिए
  • MutationObserver DOM के ट्रैकिंग परिवर्तनों के लिए (बाध्यकारी कारणों के लिए, मूल्य परिवर्तन नहीं)
  • मूल्य परिवर्तन (मॉडल प्रवाह को देखें) को नियमित addEventListenerहैंडलर के माध्यम से नियंत्रित किया जाता है

IMHO, ओपी के अलावा, यह महत्वपूर्ण है कि डेटा बाध्यकारी कार्यान्वयन होगा:

  • अलग-अलग ऐप जीवनचक्र मामलों (एचटीएमएल पहले, फिर जेएस, जेएस पहले फिर एचटीएमएल, डायनामिक विशेषता परिवर्तन आदि) को संभालें
  • मॉडल के गहरे बंधन की अनुमति दें, ताकि कोई बाँध सके user.address.block
  • एक मॉडल के रूप में सरणियों को सही ढंग से समर्थित होना चाहिए ( और shift, spliceसमान)
  • ShadowDOM को संभालें
  • जितना संभव हो उतना प्रौद्योगिकी प्रतिस्थापन के लिए आसान होने का प्रयास, इस प्रकार कोई भी अस्थायी उप-भाषा एक गैर-भविष्य-परिवर्तन-अनुकूल दृष्टिकोण है क्योंकि यह फ्रेमवर्क के साथ बहुत भारी है।

उन सभी को ध्यान में रखते हुए, मेरी राय में जेएस लाइनों के कुछ दर्जनों को फेंकना असंभव है। मैंने इसे परिवाद के बजाय एक पैटर्न के रूप में करने की कोशिश की है - मेरे लिए काम नहीं किया।

इसके बाद, Object.observeहटा दिया गया है, और अभी तक यह देखते हुए कि मॉडल का अवलोकन महत्वपूर्ण हिस्सा है - इस पूरे भाग को एक और परिवाद के लिए अलग किया जाना चाहिए। अब मैंने इस समस्या को कैसे लिया - प्रिंसिपल के बिंदु पर - जैसा कि ओपी ने पूछा:

मॉडल (JS हिस्सा)

मॉडल अवलोकन के लिए मेरा टेक प्रॉक्सी है , यह काम करने का एकमात्र एकमात्र तरीका है, IMHO। पूरी तरह से चित्रित observerयोग्य है यह स्वयं का पुस्तकालय है, इसलिए मैंने object-observerउस एकमात्र उद्देश्य के लिए पुस्तकालय विकसित किया है ।

मॉडल / s को कुछ समर्पित एपीआई के माध्यम से पंजीकृत किया जाना चाहिए, यही वह बिंदु है जहां POJO Observableएस में बदल जाते हैं , यहां कोई शॉर्टकट नहीं देख सकता है। DOM तत्वों को एक बाउंड व्यू माना जाता है (नीचे देखें), पहले मॉडल के मानों के साथ अपडेट किया जाता है और फिर प्रत्येक डेटा परिवर्तन पर।

दृश्य (HTML part)

बंधन को व्यक्त करने का सबसे साफ तरीका IMHO, विशेषताओं के माध्यम से है। बहुतों ने पहले भी ऐसा किया है और कई बाद करेंगे, इसलिए यहाँ कोई खबर नहीं है, यह सिर्फ एक सही तरीका है। मेरे मामले में मैं निम्नलिखित सिंटैक्स के साथ गया हूँ: <span data-tie="modelKey:path.to.data => targerProperty"></span>लेकिन यह कम महत्वपूर्ण है। क्या है मेरे लिए यह महत्वपूर्ण है, HTML में कोई जटिल पटकथा वाक्य रचना - यह गलत IMHO है, फिर से,।

एक बाध्य विचार होने के लिए निर्दिष्ट सभी तत्वों को पहले एकत्र किया जाएगा। यह मुझे मॉडल और विचारों के बीच कुछ आंतरिक मानचित्रण का प्रबंधन करने के लिए एक प्रदर्शन पक्ष से अपरिहार्य लगता है, एक सही मामला लगता है जहां स्मृति + कुछ प्रबंधन को रनटाइम लुकअप और अपडेट को बचाने के लिए बलिदान किया जाना चाहिए।

जैसा कि हमने कहा, मॉडल पहले से उपलब्ध हैं और बाद में मॉडल में बदलाव होने पर विचार अपडेट किए जाते हैं। अभी तक, पूरे डोम को MutationObserverगतिशील रूप से जोड़े / हटाए / बदले गए तत्वों पर प्रतिक्रिया करने के लिए (बाइंड / अनइंड) करने के लिए देखा जाना चाहिए । इसके अलावा, यह सब, छायाबद्ध (खुला एक, निश्चित रूप से) में दोहराया जाना चाहिए ताकि अनबाउंड ब्लैक होल न छोड़े।

बारीकियों की सूची वास्तव में आगे बढ़ सकती है, लेकिन वे मेरी राय में मुख्य प्रिंसिपल हैं जो एक तरफ से सुविधा पूर्णता और दूसरी तरफ से सादगी के एक अच्छे संतुलन के साथ लागू डेटा बाइंडिंग करेंगे।

और इस प्रकार, object-observerऊपर वर्णित के अलावा , मैंने वास्तव में data-tierपुस्तकालय भी लिखा है , जो उपरोक्त अवधारणाओं के साथ डेटा बाइंडिंग को लागू करता है।


0

पिछले 7 वर्षों में चीजें बहुत बदल गई हैं, अब हमारे पास अधिकांश ब्राउज़रों में देशी वेब घटक हैं। IMO समस्या का मूल तत्व तत्वों के बीच स्थिति साझा कर रहा है, एक बार जब आपके पास राज्य को बदलने और इसके विपरीत ui को अपडेट करने के लिए इसका तुच्छ हो।

तत्वों के बीच डेटा साझा करने के लिए आप एक StateObserver वर्ग बना सकते हैं, और उसी से अपने वेब घटकों का विस्तार कर सकते हैं। एक न्यूनतम कार्यान्वयन कुछ इस तरह दिखता है:

// create a base class to handle state
class StateObserver extends HTMLElement {
	constructor () {
  	super()
    StateObserver.instances.push(this)
  }
	stateUpdate (update) {
  	StateObserver.lastState = StateObserver.state
    StateObserver.state = update
    StateObserver.instances.forEach((i) => {
    	if (!i.onStateUpdate) return
    	i.onStateUpdate(update, StateObserver.lastState)
    })
  }
}

StateObserver.instances = []
StateObserver.state = {}
StateObserver.lastState = {}

// create a web component which will react to state changes
class CustomReactive extends StateObserver {
	onStateUpdate (state, lastState) {
  	if (state.someProp === lastState.someProp) return
    this.innerHTML = `input is: ${state.someProp}`
  }
}
customElements.define('custom-reactive', CustomReactive)

class CustomObserved extends StateObserver {
	connectedCallback () {
  	this.querySelector('input').addEventListener('input', (e) => {
    	this.stateUpdate({ someProp: e.target.value })
    })
  }
}
customElements.define('custom-observed', CustomObserved)
<custom-observed>
  <input>
</custom-observed>
<br />
<custom-reactive></custom-reactive>

इधर उधर होना

मुझे यह तरीका पसंद है क्योंकि:

  • data-गुण खोजने के लिए कोई डोम ट्रैवर्सल नहीं
  • कोई वस्तु नहीं। संरक्षित (पदावनत)
  • कोई प्रॉक्सी नहीं (जो किसी भी तरह हुक प्रदान करता है लेकिन संचार तंत्र नहीं है)
  • कोई निर्भरता नहीं, (आपके लक्ष्य ब्राउज़रों के आधार पर पॉलीफ़िल के अलावा)
  • यह यथोचित केंद्रीकृत और मॉड्यूलर है ... HTML में राज्य का वर्णन करता है, और हर जगह श्रोताओं को बहुत जल्दी गड़बड़ हो जाएगा।
  • यह एक्स्टेंसिबल है। यह मूल कार्यान्वयन कोड की 20 लाइनें हैं, लेकिन आप आसानी से कुछ सुविधा, अपरिवर्तनीयता, और राज्य आकार जादू का निर्माण कर सकते हैं, जिससे काम करना आसान हो।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.