Jquery-mobile और knockoutjs का उपयोग करके वेबप को कैसे आर्किटेक्चर करें


88

मैं एक मोबाइल ऐप बनाना चाहता हूं, जो html / css और JavaScript से अधिक नहीं है। जबकि मेरे पास जावास्क्रिप्ट के साथ एक वेब ऐप बनाने का एक अच्छा ज्ञान है, मैंने सोचा कि मैं jquery-mobile जैसे ढांचे में देख सकता हूं।

सबसे पहले, मैंने सोचा था कि jquery-mobile कुछ और नहीं तो एक विजेट फ्रेमवर्क था जो मोबाइल ब्राउज़रों को लक्षित करता है। बहुत कुछ jquery-ui के समान है लेकिन मोबाइल दुनिया के लिए। लेकिन मैंने देखा कि jquery-mobile इससे कहीं अधिक है। यह आर्किटेक्चर के एक समूह के साथ आता है और चलो आप एक घोषणात्मक HTML सिंटैक्स के साथ ऐप बनाते हैं। तो सबसे आसान विचारशील ऐप के लिए, आपको अपने आप से जावास्क्रिप्ट की एक पंक्ति लिखने की आवश्यकता नहीं होगी (जो शांत है, क्योंकि हम सभी कम काम करना पसंद करते हैं, क्या हम नहीं?)

एक घोषणात्मक HTML सिंटैक्स का उपयोग करके ऐप्स बनाने के दृष्टिकोण का समर्थन करने के लिए, मुझे लगता है कि नॉकआउट के साथ jquery-mobile को संयोजित करना एक अच्छा है। नॉकआउटज क्लाइंट-साइड एमवीवीएम फ्रेमवर्क है जिसका उद्देश्य एमवीवीएम सुपर शक्तियों को WPF / सिल्वरलाइट से जावास्क्रिप्ट दुनिया में लाना है।

मेरे लिए MVVM एक नई दुनिया है। जबकि मैंने इसके बारे में पहले ही बहुत कुछ पढ़ा है, मैंने वास्तव में इसे पहले कभी खुद इस्तेमाल नहीं किया है।

तो यह पोस्टिंग यह है कि jquery-mobile और knockoutjs का उपयोग करके एक ऐप को कैसे आर्किटेक्चर किया जाए। मेरा विचार उस दृष्टिकोण को लिखना था जिसे मैं कई घंटों तक देखने के बाद आया था, और यह टिप्पणी करने के लिए कुछ jquery- मोबाइल / नॉकआउट योडा है, मुझे दिखा रहा है कि यह क्यों बेकार है और मुझे पहले प्रोग्रामिंग क्यों नहीं करनी चाहिए स्थान ;-)

एचटीएमएल

jquery-mobile पृष्ठों की एक बुनियादी संरचना मॉडल प्रदान करने के लिए एक अच्छा काम करता है। जबकि मुझे अच्छी तरह से पता है कि मैं अपने पेज को बाद में ajax के माध्यम से लोड कर सकता था, मैंने बस उन सभी को एक index.html फ़ाइल में रखने का निर्णय लिया। इस मूल परिदृश्य में हम दो पृष्ठों के बारे में बात कर रहे हैं ताकि चीजों के शीर्ष पर बने रहना कठिन न हो।

<!DOCTYPE html> 
<html> 
  <head> 
  <title>Page Title</title> 
  <link rel="stylesheet" href="libs/jquery-mobile/jquery.mobile-1.0a4.1.css" />
  <link rel="stylesheet" href="app/base/css/base.css" />
  <script src="libs/jquery/jquery-1.5.0.min.js"></script>
  <script src="libs/knockout/knockout-1.2.0.js"></script>
  <script src="libs/knockout/knockout-bindings-jqm.js" type="text/javascript"></script>
  <script src="libs/rx/rx.js" type="text/javascript"></script>
  <script src="app/App.js"></script>
  <script src="app/App.ViewModels.HomeScreenViewModel.js"></script>
  <script src="app/App.MockedStatisticsService.js"></script>
  <script src="libs/jquery-mobile/jquery.mobile-1.0a4.1.js"></script>  
</head> 
<body> 

<!-- Start of first page -->
<div data-role="page" id="home">

    <div data-role="header">
        <h1>Demo App</h1>
    </div><!-- /header -->

    <div data-role="content">   

    <div class="ui-grid-a">
        <div class="ui-block-a">
            <div class="ui-bar" style="height:120px">
                <h1>Tours today (please wait 10 seconds to see the effect)</h1>
                <p><span data-bind="text: toursTotal"></span> total</p>
                <p><span data-bind="text: toursRunning"></span> running</p>
                <p><span data-bind="text: toursCompleted"></span> completed</p>     
            </div>
        </div>
    </div>

    <fieldset class="ui-grid-a">
        <div class="ui-block-a"><button data-bind="click: showTourList, jqmButtonEnabled: toursAvailable" data-theme="a">Tour List</button></div>  
    </fieldset>

    </div><!-- /content -->

    <div data-role="footer" data-position="fixed">
        <h4>by Christoph Burgdorf</h4>
    </div><!-- /header -->
</div><!-- /page -->

<!-- tourlist page -->
<div data-role="page" id="tourlist">

    <div data-role="header">
        <h1>Bar</h1>
    </div><!-- /header -->

    <div data-role="content">   
        <p><a href="#home">Back to home</a></p> 
    </div><!-- /content -->

    <div data-role="footer" data-position="fixed">
        <h4>by Christoph Burgdorf</h4>
    </div><!-- /header -->
</div><!-- /page -->

</body>
</html>

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

तो चलिए मज़ेदार हिस्से पर आते हैं - जावास्क्रिप्ट!

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

App.js

var App = window.App = {};
App.ViewModels = {};

$(document).bind('mobileinit', function(){
    // while app is running use App.Service.mockStatistic({ToursCompleted: 45}); to fake backend data from the console
    var service = App.Service = new App.MockedStatisticService();    

  $('#home').live('pagecreate', function(event, ui){
        var viewModel = new App.ViewModels.HomeScreenViewModel(service);
        ko.applyBindings(viewModel, this);
        viewModel.startServicePolling();
  });
});

App.js मेरे ऐप का प्रवेश बिंदु है। यह ऐप ऑब्जेक्ट बनाता है और व्यू मॉडल (जल्द ही आने वाले) के लिए एक नाम स्थान प्रदान करता है। यह मोबाइलिनिट घटना के लिए सुनता है जो jquery-mobile प्रदान करता है।

जैसा कि आप देख सकते हैं, मैं कुछ प्रकार की अजाक्स सेवा का एक उदाहरण बना रहा हूं (जिसे हम बाद में देखेंगे) और इसे "सेवा" चर में सहेजें।

मैं होम पेज के लिए पेजक्रिएट ईवेंट को भी हुक करता हूं जिसमें मैं एक उदाहरण बनाता हूं जो कि सर्विस इंस्टेंस को पास कर देता है। यह बिंदु मेरे लिए आवश्यक है। यदि कोई सोचता है, तो यह अलग तरह से किया जाना चाहिए, कृपया अपने विचार साझा करें!

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

एक और बात जो मुझे बतानी चाहिए, वह यह है कि ViewModel को वास्तविक दृश्य के बारे में शून्य ज्ञान है। इसीलिए पेजक्रिएट हैंडलर के भीतर से मैं ko.applyBindings (viewModel, यह) कह रहा हूं । मैं दृश्य मॉडल को वास्तविक दृश्य से अलग रखना चाहता था ताकि इसे जांचना आसान हो सके।

App.ViewModels.HomeScreenViewModel.js

(function(App){
  App.ViewModels.HomeScreenViewModel = function(service){
    var self = {}, disposableServicePoller = Rx.Disposable.Empty;

    self.toursTotal = ko.observable(0);
    self.toursRunning = ko.observable(0);
    self.toursCompleted = ko.observable(0);
    self.toursAvailable = ko.dependentObservable(function(){ return this.toursTotal() > 0; }, self);
    self.showTourList = function(){ $.mobile.changePage('#tourlist', 'pop', false, true); };        
    self.startServicePolling = function(){  
        disposableServicePoller = Rx.Observable
            .Interval(10000)
            .Select(service.getStatistics)
            .Switch()
            .Subscribe(function(statistics){
                self.toursTotal(statistics.ToursTotal);
                self.toursRunning(statistics.ToursRunning); 
                self.toursCompleted(statistics.ToursCompleted); 
            });
    };
    self.stopServicePolling = disposableServicePoller.Dispose;      

    return self; 
  };
})(App)

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

अगला कारण वह सेवा है जिसे मैं एक पैरामीटर के रूप में पारित कर सकता हूं जैसा मैंने पहले उल्लेख किया था।

इस दृश्य मॉडल के साथ एक और बात है जो मुझे यकीन नहीं है कि मैंने सही तरीका चुना है। मैं समय-समय पर सर्वर से परिणाम लाने के लिए अजाक्स सेवा का चुनाव करना चाहता हूं। तो, मैं startServicePolling / stopServicePolling विधियों को लागू करने के लिए चुना है । यह विचार पृष्ठशो पर मतदान शुरू करने के लिए है, और जब उपयोगकर्ता अलग-अलग पृष्ठ पर जाता है तो इसे रोक दें।

आप सिंटैक्स को अनदेखा कर सकते हैं जिसका उपयोग सेवा को प्रदूषित करने के लिए किया जाता है। यह RxJS जादू है। बस सुनिश्चित करें कि मैं इसे मतदान कर रहा हूं और लौटे परिणाम के साथ अवलोकन योग्य गुणों को अपडेट कर सकता हूं जैसा कि आप सदस्यता लें (फ़ंक्शन (आंकड़े) {..}) भाग में देख सकते हैं ।

App.MockedStatisticsService.js

ठीक है, आपको दिखाने के लिए सिर्फ एक चीज बची है। यह वास्तविक सेवा कार्यान्वयन है। मैं यहाँ विस्तार में नहीं जा रहा हूँ। यह सिर्फ एक मॉक है जो गेटस्टैटिस्टिक्स के कुछ नंबर लौटाता है है। एक अन्य विधि mockStatistics है, जिसका उपयोग मैं एप्लिकेशन के चलने के दौरान ब्राउज़र js कंसोल के माध्यम से नए मान सेट करने के लिए करता हूं।

(function(App){
    App.MockedStatisticService = function(){
        var self = {},
        defaultStatistic = {
            ToursTotal: 505,
            ToursRunning: 110,
            ToursCompleted: 115 
        },
        currentStatistic = $.extend({}, defaultStatistic);;

        self.mockStatistic = function(statistics){
            currentStatistic = $.extend({}, defaultStatistic, statistics);
        };

        self.getStatistics = function(){        
            var asyncSubject = new Rx.AsyncSubject();
            asyncSubject.OnNext(currentStatistic);
            asyncSubject.OnCompleted();
            return asyncSubject.AsObservable();
        };

        return self;
    };
})(App)

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

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

अपडेट करें

प्रमुख लोकप्रियता के कारण इस पोस्टिंग को फायदा हुआ और क्योंकि कई लोगों ने मुझसे ऐसा करने के लिए कहा, मैंने इस उदाहरण का कोड जीथब पर रखा है:

https://github.com/cburgdorf/stackoverflow-knockout-example

इसे गर्म होने पर प्राप्त करें!


7
मुझे यकीन नहीं है कि लोगों को संबोधित करने के लिए पर्याप्त विशिष्ट प्रश्न है। मुझे आपके द्वारा यहां दिया गया विवरण पसंद है, लेकिन यह खुद को चर्चा के लिए आगे बढ़ने के लिए उधार देता है। कम शब्दों में: "अच्छा ब्लॉग";)
बर्नहार्ड हॉफमैन

मुझे खुशी है कि आपने इसे पसंद किया। मुझे थोड़ी चिंता हो रही थी कि मैंने इतना लिख ​​दिया कि लोगों को एक छोटा सा उत्तर लिखने से डर लगता है। हालाँकि, किसी भी चर्चा का स्वागत है। और यदि चर्चा शुरू करने के लिए स्टैकओवरफ़्लो
क्रिस्टोफ़

हाय क्रिस्टोफ, इस दृष्टिकोण ने आपके लिए कैसे काम किया?
हकोन

वास्तव में, मैं और अधिक भयानक AngularJS फ्रेमवर्क पर चले गए ;-)
क्रिस्टोफ

1
यह बेहतर हो सकता है यदि आपने सवाल के रूप में पैराग्राफ के पहले जोड़े को रखा, और शेष को स्व-उत्तर में स्थानांतरित कर दिया।
15

जवाबों:


30

नोट: jQuery 1.7 के रूप में, .live()विधि पदावनत है। .on()ईवेंट हैंडलर संलग्न करने के लिए उपयोग करें । JQuery के पुराने संस्करणों के उपयोगकर्ताओं को .delegate()वरीयता में उपयोग करना चाहिए .live()

मैं एक ही चीज पर काम कर रहा हूं (नॉकआउट + jquery मोबाइल)। मैं जो कुछ भी सीखा है उसके बारे में एक ब्लॉग पोस्ट लिखने की कोशिश कर रहा हूं, लेकिन इस बीच कुछ संकेत हैं। याद रखें कि मैं नॉकआउट / jquery मोबाइल सीखने की कोशिश कर रहा हूं।

देखें-मॉडल और पेज

केवल jQuery के मोबाइल पेज पर एक (1) व्यू-मॉडल ऑब्जेक्ट का उपयोग करें। अन्यथा आप कई बार ट्रिगर होने वाली क्लिक-ईवेंट की समस्याएँ प्राप्त कर सकते हैं।

देखें-मॉडल और क्लिक करें

दृश्य-मॉडल क्लिक-ईवेंट के लिए केवल ko.observable-फ़ील्ड का उपयोग करें।

ko.applyBinding एक बार

यदि संभव हो: केवल ko.applyBinding हर पृष्ठ के लिए एक बार कॉल करें और कई बार कॉल करने के बजाय ko.observable का उपयोग करें।

पेजहाइड और ko.cleanNode

पेजहाइड पर कुछ दृश्य-मॉडल को साफ करना याद रखें। ko.cleanNode को jQuery के मोबाइल्स रेंडरिंग में गड़बड़ी लगती है - जिससे HTML को फिर से प्रस्तुत करना पड़ता है। यदि आप किसी पृष्ठ पर ko.cleanNode का उपयोग करते हैं, तो आपको डेटा-भूमिका को हटाने और स्रोत कोड में प्रदान किए गए jQuery के मोबाइल HTML को सम्मिलित करना होगा।

$('#field').live('pagehide', function() {
    ko.cleanNode($('#field')[0]);
});

पेजहाइड और क्लिक करें

यदि आप क्लिक-इवेंट्स के लिए बाध्य हैं - तो .ui-btn-active को साफ़ करना याद रखें। इसे पूरा करने का सबसे आसान तरीका इस कोड स्निपेट का उपयोग करना है:

$('[data-role="page"]').live('pagehide', function() {
    $('.ui-btn-active').removeClass('ui-btn-active');
});

जैसा कि मेरा प्रश्न बहुत ही अनिर्णायक था और आप वही हैं जिसने सबसे अधिक काम को एक उत्तर में रखा है, मैं आपका स्वीकृत उत्तर दूंगा।
क्रिस्टोफ

क्या आपको इसका कभी पता चला? मुझे केओ और JQM को एकीकृत करने का एक समय चल रहा है और इसे कैसे करना है (या jsFiddle एंड-टू-एंड डेमो प्रदर्शित करता है) पर कोई अच्छा गाइड नहीं है।
कामरानिकस

1
नहींं, मैं एंगुलरजेएस फ्रेमवर्क पर चला गया। मैंने पाया कि कोए को सुपरआईयर होना। और हमेशा के लिए AngularJS / jqm को सबसे अच्छा दोस्त बनाने के लिए एक काफी अच्छा अडैप्टर प्रोजेक्ट है: github.com/tigbro/jquery-mobile-angular-adapter हालांकि, मैंने अभी तक जो भी किया था उसके लिए उस एडेप्टर का उपयोग करना बहुत अच्छा लग रहा था। सब के बाद, बस jqm के html / css का उपयोग करना आसान है और एक कोणीय निर्देश में नियंत्रण चालू करें: jsfiddle.net/zy7Rg/7
क्रिस्टोफ

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