Backbone.js में आरंभिक और रेंडरिंग सबव्यूएँ कैसे संभालें?


199

मेरे पास एक दृश्य और इसके साक्षात्कारों को शुरू करने और प्रस्तुत करने के तीन अलग-अलग तरीके हैं, और उनमें से प्रत्येक में अलग-अलग समस्याएं हैं। मैं यह जानने के लिए उत्सुक हूं कि क्या कोई बेहतर तरीका है जो सभी समस्याओं को हल करता है:


परिदृश्य एक:

माता-पिता के प्रारंभिक कार्य में बच्चों को आरंभिक करें। इस तरह, सब कुछ रेंडर में फंस नहीं जाता है ताकि रेंडरिंग में कम अवरोधन हो।

initialize : function () {

    //parent init stuff

    this.child = new Child();
},

render : function () {

    this.$el.html(this.template());

    this.child.render().appendTo(this.$('.container-placeholder');
}

समस्याये:

  • सबसे बड़ी समस्या यह है कि दूसरी बार माता-पिता को रेंडर करने से चिल्ड इवेंट की सभी बाइंडिंग दूर हो जाएगी। (यह jQuery के $.html()काम करने के तरीके के कारण है ।) इसके this.child.delegateEvents().render().appendTo(this.$el);बजाय कॉल करके इसे कम किया जा सकता है , लेकिन फिर पहला और सबसे अधिक बार मामला, आप अनावश्यक रूप से अधिक काम कर रहे हैं।

  • बच्चों को जोड़कर, आप रेंडर फ़ंक्शन को माता-पिता की डोम संरचना के बारे में जानकारी देने के लिए मजबूर करते हैं ताकि आपको वह ऑर्डर मिल सके जो आप चाहते हैं। जिसका मतलब है कि एक टेम्पलेट को बदलने के लिए एक दृश्य के रेंडर फ़ंक्शन को अपडेट करने की आवश्यकता हो सकती है।


परिदृश्य दो:

माता-पिता के initialize()अभी भी बच्चों को प्रारंभिक रूप से प्रशिक्षित करें , लेकिन अपील करने के बजाय, setElement().delegateEvents()बच्चे को माता-पिता के टेम्पलेट में एक तत्व पर सेट करने के लिए उपयोग करें।

initialize : function () {

    //parent init stuff

    this.child = new Child();
},

render : function () {

    this.$el.html(this.template());

    this.child.setElement(this.$('.placeholder-element')).delegateEvents().render();
}

समस्या:

  • यह delegateEvents()अब आवश्यक बनाता है , जो कि पहले परिदृश्य में बाद की कॉल पर आवश्यक होने पर थोड़ा नकारात्मक है।

परिदृश्य तीन:

render()इसके बजाय माता-पिता की विधि में बच्चों को शुरू करें ।

initialize : function () {

    //parent init stuff
},

render : function () {

    this.$el.html(this.template());

    this.child = new Child();

    this.child.appendTo($.('.container-placeholder').render();
}

समस्या:

  • इसका मतलब है कि रेंडर फंक्शन को अब इनिशियलाइजेशन लॉजिक के साथ ही जोड़ा जाना चाहिए।

  • यदि मैं बच्चे के विचारों में से एक को संपादित करता हूं, और फिर माता-पिता को प्रस्तुत करता हूं, तो एक पूरी तरह से नया बच्चा बनाया जाएगा और उसकी वर्तमान स्थिति खो जाएगी। जो यह भी लगता है कि यह मेमोरी लीक के लिए पासा कर सकता है।


अपने लोगों को इस पर ले जाने के लिए वास्तव में उत्सुक हैं। आप किस परिदृश्य का उपयोग करेंगे? या वहाँ एक चौथा जादुई है जो इन सभी समस्याओं को हल करता है?

क्या आपने कभी किसी दृश्य के लिए प्रदान की गई स्थिति का ट्रैक रखा है? एक renderedBeforeझंडा कहो ? वास्तव में जानदार लगता है।


1
मैं आमतौर पर माता-पिता के विचारों पर बच्चे के विचारों का संदर्भ नहीं देता हूं क्योंकि अधिकांश संचार मॉडल / संग्रह और घटनाओं के माध्यम से होता है जो इन परिवर्तनों के कारण होता है। हालाँकि मैं जो सबसे ज्यादा इस्तेमाल कर रहा हूं, उसमें तीसरा मामला सबसे करीब है। केस 1 जहां समझ में आता है। इसके अलावा, अधिकांश समय आपको पूरे दृश्य को फिर से प्रस्तुत नहीं करना चाहिए, बल्कि जो हिस्सा बदल गया है
टॉम तू

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

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

1
उच्च-स्तरीय, संबंधित चर्चा के लिए, देखें: stackoverflow.com/questions/10077185/…
बेन रॉबर्ट्स

2
परिदृश्य दो में, क्यों आवश्यक कॉल करने के लिए है delegateEvents()के बाद setElement()? डॉक्स के अनुसार: "... और पुराने तत्व से दृश्य की प्रत्यायोजित घटनाओं को नए में स्थानांतरित करें", setElementविधि को स्वयं घटनाओं को फिर से सौंपना चाहिए।
rdamborsky

जवाबों:


260

यह एक बड़ा सवाल है। बैकबोन महान है क्योंकि यह मान्यताओं की कमी के कारण होता है, लेकिन इसका मतलब यह है कि आपको खुद को इस तरह की चीजों को लागू करना है (कैसे तय करें)। अपने स्वयं के सामान के माध्यम से देखने के बाद, मुझे लगता है कि मैं (तरह का) परिदृश्य 1 और परिदृश्य 2 के मिश्रण का उपयोग करता हूं। मुझे नहीं लगता कि एक 4 वां जादुई परिदृश्य मौजूद है, क्योंकि बस, पर्याप्त है, जो कुछ भी आप परिदृश्य 1 और 2 में करते हैं। किया हुआ।

मुझे लगता है कि यह स्पष्ट करना आसान होगा कि मैं इसे एक उदाहरण के साथ कैसे संभालना चाहता हूं। कहो कि मेरे पास यह सरल पृष्ठ निर्दिष्ट विचारों में टूट गया है:

पेज टूटना

कहो HTML है, गाया जाने के बाद, कुछ इस तरह:

<div id="parent">
    <div id="name">Person: Kevin Peel</div>
    <div id="info">
        First name: <span class="first_name">Kevin</span><br />
        Last name: <span class="last_name">Peel</span><br />
    </div>
    <div>Phone Numbers:</div>
    <div id="phone_numbers">
        <div>#1: 123-456-7890</div>
        <div>#2: 456-789-0123</div>
    </div>
</div>

उम्मीद है कि यह बहुत स्पष्ट है कि HTML आरेख के साथ कैसे मेल खाता है।

ParentView2 बच्चे दृश्य, रखती है InfoViewऔर PhoneListViewसाथ ही साथ कुछ अतिरिक्त divs, जिनमें से एक, #name, कुछ बिंदु पर निर्धारित करने की आवश्यकता। PhoneListViewअपने स्वयं के बच्चे के विचारों को रखता है, PhoneViewप्रविष्टियों की एक सरणी ।

तो अपने वास्तविक प्रश्न पर। मैं शुरुआती प्रकार को देखता हूं और दृश्य प्रकार के आधार पर अलग-अलग प्रतिपादन करता हूं। मैं अपने विचारों को दो प्रकारों, Parentविचारों और Childविचारों में तोड़ता हूं ।

उनके बीच का अंतर सरल है, Parentविचार बच्चे के विचारों को Childरखते हैं जबकि विचार नहीं करते हैं। तो मेरी उदाहरण में, ParentViewऔर PhoneListViewकर रहे हैं Parent, विचार करते हुए InfoViewऔर PhoneViewप्रविष्टियों Childबार देखा गया।

जैसा कि मैंने पहले उल्लेख किया है, इन दो श्रेणियों के बीच सबसे बड़ा अंतर है जब उन्हें रेंडर करने की अनुमति दी जाती है। एक आदर्श दुनिया में, मैं Parentकेवल एक बार प्रस्तुत करना चाहता हूं । मॉडल (नों) में बदलाव होने पर किसी भी री-रेंडरिंग को संभालना उनके बच्चे के विचारों पर निर्भर करता है। Childदूसरी ओर, मैं किसी भी समय उन्हें फिर से प्रस्तुत करने की अनुमति देता हूं क्योंकि उनके पास उन पर भरोसा करने वाले कोई अन्य विचार नहीं हैं।

कुछ और बातें करने के लिए Parentमेरे initializeकार्यों को पसंद करने वाले विचारों के लिए, थोड़ा और विस्तार से :

  1. मेरे अपने दृष्टिकोण को आरम्भिक करें
  2. मेरे अपने विचार प्रस्तुत करें
  3. किसी भी बच्चे के विचार बनाएं और आरंभ करें।
  4. असाइन करें कि प्रत्येक बच्चे को मेरे विचार में एक तत्व दिखाई देता है (जैसे InfoViewअसाइन किया जाएगा #info)।

चरण 1 बहुत आत्म व्याख्यात्मक है।

चरण 2, प्रतिपादन, ऐसा किया जाता है कि बच्चे द्वारा देखे जाने से पहले ही कोई भी तत्व जिस पर भरोसा करता है, वह पहले से ही मौजूद है। ऐसा करने से, मुझे पता है कि सभी बच्चे eventsसही ढंग से सेट हो जाएंगे, और मैं उनके ब्लॉकों को कई बार फिर से प्रस्तुत कर सकता हूं, जैसा कि मैं चाहता हूं कि किसी भी चीज को फिर से सौंपने की चिंता किए बिना। मैं वास्तव में यहां renderकिसी भी बच्चे के विचार नहीं करता हूं , मैं उन्हें अपने भीतर ऐसा करने की अनुमति देता हूं initialization

चरण 3 और 4 वास्तव में उसी समय को संभाला जाता है जब मैं elबच्चे को देखने के दौरान पास करता हूं । मुझे यहां एक तत्व पास करना पसंद है क्योंकि मुझे लगता है कि माता-पिता को यह निर्धारित करना चाहिए कि अपने स्वयं के दृश्य में बच्चे को अपनी सामग्री डालने की अनुमति कहां है।

प्रतिपादन के लिए, मैं इसे Parentविचारों के लिए बहुत सरल रखने की कोशिश करता हूं । मैं चाहता हूं कि renderफंक्शन पैरेंट व्यू को रेंडर करने से ज्यादा कुछ नहीं करे। कोई घटना प्रतिनिधि, बच्चे के विचारों का कोई प्रतिपादन, कुछ नहीं। बस एक साधारण रेंडर।

हालांकि यह हमेशा काम नहीं करता है। उदाहरण के लिए ऊपर मेरे उदाहरण में, #nameतत्व को किसी भी समय मॉडल परिवर्तन के नाम से अपडेट करना होगा। हालाँकि, यह ब्लॉक ParentViewटेम्प्लेट का हिस्सा है और एक समर्पित Childदृश्य द्वारा नियंत्रित नहीं किया जाता है , इसलिए मैं इसके चारों ओर काम करता हूं। मैं कुछ प्रकार के subRenderफ़ंक्शन बनाऊंगा जो केवल#name तत्व की सामग्री को प्रतिस्थापित करता है , और पूरे #parentतत्व को कचरा नहीं करना पड़ता है । यह एक हैक की तरह लग सकता है, लेकिन मैंने वास्तव में पाया है कि यह पूरे डोम और रीटचिंग तत्वों और इस तरह के पुन: प्रतिपादन के बारे में चिंता करने से बेहतर काम करता है। अगर मैं वास्तव में इसे साफ करना चाहता था, तो मैं एक नया Childदृश्य (उसी के समान InfoView) बनाऊंगा जो #nameब्लॉक को हैंडल करेगा ।

अब Childविचारों के लिए, विचारों के initializationसमान सुंदर है Parent, बस किसी भी आगे के Childविचारों के निर्माण के बिना । इसलिए:

  1. मेरे विचार को प्रारंभ करें
  2. मैं जिस मॉडल की परवाह करता हूं, उसके बदलाव के लिए सेटअप बाइंडिंग सुनता है
  3. मेरे विचार का प्रतिपादन करें

Childदेखें प्रतिपादन भी बहुत सरल है, बस प्रस्तुत करना और मेरी सामग्री सेट करना el। फिर, प्रतिनिधिमंडल के साथ कोई खिलवाड़ या ऐसा कुछ नहीं।

यहाँ कुछ उदाहरण कोड है जो मेरे ParentViewजैसा दिख सकता है:

var ParentView = Backbone.View.extend({
    el: "#parent",
    initialize: function() {
        // Step 1, (init) I want to know anytime the name changes
        this.model.bind("change:first_name", this.subRender, this);
        this.model.bind("change:last_name", this.subRender, this);

        // Step 2, render my own view
        this.render();

        // Step 3/4, create the children and assign elements
        this.infoView = new InfoView({el: "#info", model: this.model});
        this.phoneListView = new PhoneListView({el: "#phone_numbers", model: this.model});
    },
    render: function() {
        // Render my template
        this.$el.html(this.template());

        // Render the name
        this.subRender();
    },
    subRender: function() {
        // Set our name block and only our name block
        $("#name").html("Person: " + this.model.first_name + " " + this.model.last_name);
    }
});

आप subRenderयहाँ मेरा कार्यान्वयन देख सकते हैं । के subRenderबजाय बाध्य परिवर्तन होने से render, मुझे नष्ट करने और पूरे ब्लॉक के पुनर्निर्माण के बारे में चिंता करने की ज़रूरत नहीं है।

यहाँ InfoViewब्लॉक के लिए उदाहरण कोड है :

var InfoView = Backbone.View.extend({
    initialize: function() {
        // I want to re-render on changes
        this.model.bind("change", this.render, this);

        // Render
        this.render();
    },
    render: function() {
        // Just render my template
        this.$el.html(this.template());
    }
});

बाँध यहाँ का महत्वपूर्ण हिस्सा हैं। अपने मॉडल से जुड़कर, मुझे कभी भी renderखुद को फोन करने की चिंता नहीं करनी चाहिए। यदि मॉडल बदलता है, तो यह ब्लॉक किसी अन्य दृश्य को प्रभावित किए बिना खुद को फिर से प्रस्तुत करेगा।

के PhoneListViewसमान होगा ParentView, आपको संग्रह को संभालने के लिए बस अपने initializationऔर renderकार्यों दोनों में थोड़ा और तर्क की आवश्यकता होगी । आप संग्रह को कैसे संभालते हैं यह वास्तव में आप पर निर्भर करता है, लेकिन आपको कम से कम संग्रह की घटनाओं को सुनना होगा और यह निर्णय लेना होगा कि आप कैसे प्रस्तुत करना चाहते हैं (जोड़ना / हटाना, या पूरे ब्लॉक को फिर से प्रस्तुत करना)। मुझे व्यक्तिगत रूप से नए विचारों को जोड़ना और पुराने लोगों को हटाना पसंद है, न कि पूरे दृश्य को फिर से प्रस्तुत करना।

PhoneViewलगभग समान हो जाएगा InfoView, केवल मॉडल परिवर्तन इसके बारे में परवाह करता है को सुन।

उम्मीद है कि इसने थोड़ी मदद की है, कृपया मुझे बताएं कि क्या कुछ भ्रामक है या पर्याप्त विस्तृत नहीं है।


8
वास्तव में पूरी तरह से स्पष्टीकरण धन्यवाद। प्रश्न हालांकि, मैंने सुना है और इस बात से सहमत हूं कि विधि के renderअंदर कॉल initializeकरना एक बुरा अभ्यास है, क्योंकि यह आपको उन मामलों में अधिक प्रदर्शन करने से रोकता है, जहां आप तुरंत प्रस्तुत नहीं करना चाहते हैं। आप इस बारे में क्या सोचते हैं?
इयान स्टॉर्म टेलर

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

5
किसी पेरेंटव्यू को फिर से प्रस्तुत करने में सक्षम नहीं होना एक बड़ी सीमा है। मेरे पास एक स्थिति है जहां मेरे पास मूल रूप से एक टैब नियंत्रण है, जहां प्रत्येक टैब एक अलग पेरेंटव्यू दिखाता है। मैं मौजूदा पेरेंटव्यू को फिर से प्रस्तुत करना चाहूंगा जब एक टैब को दूसरी बार क्लिक किया जाता है, लेकिन ऐसा करने से चाइल्ड व्यू में होने वाली घटनाओं का कारण बनता है (मैं बच्चों को init बनाता हूं, और उन्हें रेंडर में रेंडर करता हूं)। मुझे लगता है कि मैं 1) रेंडर करने के बजाय 1) छिपाता / दिखाता हूं, 2) चाइल्ड व्यूज़ के रेंडर में डेलिगेट, या 3) रेंडर में बच्चे पैदा करते हैं, या 4) पूर्ण के बारे में चिंता नहीं करते हैं। इससे पहले कि यह एक समस्या है और हर बार पेरेंट व्यू बनाएं।
हम्मम

मुझे कहना चाहिए कि यह मेरे मामले में एक सीमा है। यह समाधान ठीक काम करेगा यदि आप माता-पिता को फिर से प्रस्तुत करने की कोशिश नहीं करते हैं :) मेरे पास एक अच्छा जवाब नहीं है लेकिन मेरे पास ओपी के समान प्रश्न है। फिर भी उचित 'बैकबोन' रास्ता खोजने की उम्मीद है। इस बिंदु पर, मैं छिपाने / दिखाने के बारे में सोच रहा हूं और फिर से प्रस्तुत करना मेरे लिए जाने का तरीका नहीं है।
पॉल होनेके

1
आप बच्चे में संग्रह कैसे पास करते हैं PhoneListView?
त्रि गुयेन

6

मुझे यकीन नहीं है कि यह सीधे आपके सवाल का जवाब देता है, लेकिन मुझे लगता है कि यह प्रासंगिक है:

http://lostechies.com/derickbailey/2011/10/11/backbone-js-getting-the-model-for-a-clicked-element/

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


6

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

डेरिक के रूप में भी मैं पूरी तरह से आश्वस्त नहीं हूं अगर यह सीधे आपके सवाल का जवाब देता है, लेकिन मुझे लगता है कि यह इस संदर्भ में कम से कम उल्लेख के लायक हो सकता है।

इसे भी देखें: Backbone में एक Eventbus का उपयोग


3

केविन पील एक शानदार जवाब देता है - यहाँ मेरा tl; डॉ संस्करण है:

initialize : function () {

    //parent init stuff

    this.render(); //ANSWER: RENDER THE PARENT BEFORE INITIALIZING THE CHILD!!

    this.child = new Child();
},

2

मैं इस तरह के विचारों के बीच युग्मन से बचने की कोशिश कर रहा हूं। आमतौर पर दो तरीके हैं:

एक राउटर का उपयोग करें

मूल रूप से, आप अपने राउटर फ़ंक्शन को माता-पिता और बच्चे के दृश्य को आरंभ करने देते हैं। इसलिए दृश्य को एक दूसरे का ज्ञान नहीं है, लेकिन राउटर यह सब संभालता है।

दोनों दृष्टियों से समान एल पास

this.parent = new Parent({el: $('.container-placeholder')});
this.child = new Child({el: $('.container-placeholder')});

दोनों को एक ही डोम का ज्ञान है, और आप उन्हें वैसे भी ऑर्डर कर सकते हैं, जैसा आप चाहते हैं।


2

मैं जो कर रहा हूं वह प्रत्येक बच्चों को एक पहचान दे रहा है (जो बैकबोन पहले ही आपके लिए कर चुका है: cid)

जब कंटेनर रेंडर करता है, तो 'cid' और 'tagName' का उपयोग करके प्रत्येक बच्चे के लिए एक प्लेसहोल्डर तैयार किया जाता है, इसलिए टेम्पलेट में बच्चों को यह पता नहीं होता है कि यह कंटेनर द्वारा कहाँ रखा जाएगा।

<tagName id='cid'></tagName>

से आप का उपयोग कर सकते हैं

Container.render()
Child.render();
this.$('#'+cid).replaceWith(child.$el);
// the rapalceWith in jquery will detach the element 
// from the dom first, so we need re-delegateEvents here
child.delegateEvents();

किसी निर्दिष्ट प्लेसहोल्डर की आवश्यकता नहीं है, और कंटेनर केवल बच्चों के डोम संरचना के बजाय प्लेसहोल्डर उत्पन्न करता है। Cotainer और बच्चे अभी भी केवल एक बार DOM तत्व पैदा कर रहे हैं।


1

यहाँ सबव्यू बनाने और रेंडर करने के लिए एक लाइट वेट मिक्सिन है, जो मुझे लगता है कि इस थ्रेड में सभी मुद्दों को संबोधित करता है:

https://github.com/rotundasoftware/backbone.subviews

इस प्लग द्वारा लिया गया दृष्टिकोण पहली बार मूल दृश्य प्रदान किए जाने के बाद साक्षात्कार बना और प्रस्तुत करना है। फिर, मूल दृश्य के बाद के रेंडरर्स पर, सबव्यू तत्वों को $ .detach करें, अभिभावक को फिर से प्रस्तुत करें, फिर उप-तत्व तत्वों को उपयुक्त स्थानों पर डालें और उन्हें फिर से प्रस्तुत करें। इस तरह से साक्षात्कार के बाद के रेंडर पर वस्तुओं का पुन: उपयोग किया जाता है, और घटनाओं को फिर से सौंपने की कोई आवश्यकता नहीं है।

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.