Jquery-mobile ve knockoutjs kullanarak bir web uygulaması nasıl yapılandırılır


88

Html / css ve JavaScript dışında hiçbir şeyden oluşturulmuş bir mobil uygulama oluşturmak istiyorum. JavaScript ile bir web uygulamasının nasıl oluşturulacağına dair iyi bir bilgim olsa da jquery-mobile gibi bir çerçeveye bakabileceğimi düşündüm.

İlk başta, jquery-mobile'ın mobil tarayıcıları hedefleyen bir widget çerçevesinden başka bir şey olmadığını düşündüm. Jquery-ui'ye çok benzer, ancak mobil dünya için. Ancak jquery-mobile'ın bundan daha fazlası olduğunu fark ettim. Bir grup mimari ile birlikte gelir ve bildirim temelli html sözdizimi ile uygulamalar oluşturmanıza izin verin. Dolayısıyla, en kolay düşünülebilir uygulama için, tek bir JavaScript satırı yazmanıza gerek kalmaz (bu harika, çünkü hepimiz daha az çalışmayı seviyoruz, değil mi?)

Bildirim temelli bir html sözdizimi kullanarak uygulama oluşturma yaklaşımını desteklemek için jquery-mobile ile knockoutj'leri birleştirmenin iyi bir yaklaşım olduğunu düşünüyorum. Knockoutjs, WPF / Silverlight'tan bilinen MVVM süper güçlerini JavaScript dünyasına getirmeyi amaçlayan istemci tarafı bir MVVM çerçevesidir.

Benim için MVVM yeni bir dünya. Bu konuda zaten çok şey okumuş olsam da, aslında daha önce hiç kullanmadım.

Yani bu gönderi, jquery-mobile ve knockoutj'leri birlikte kullanarak bir uygulamanın nasıl yapılandırılacağıyla ilgili. Benim fikrim, birkaç saat boyunca ona baktıktan sonra ortaya attığım yaklaşımı yazmak ve yorumlamak için biraz jquery-mobile / knockout yoda almak, neden berbat olduğunu ve neden ilk başta programlama yapmamam gerektiğini göstermekti. yer ;-)

Html

jquery-mobile, sayfaların temel yapı modelini sağlayarak iyi bir iş çıkarır. Sayfalarımın daha sonra ajax aracılığıyla yüklenebileceğinin farkında olsam da, hepsini bir index.html dosyasında tutmaya karar verdim. Bu temel senaryoda, iki sayfadan bahsediyoruz, böylece konuların üstesinden gelmek çok zor olmasa gerek.

<!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>

JavaScript

Hadi eğlenceli kısma gelelim - JavaScript!

Uygulamayı katmanlandırmayı düşünmeye başladığımda, aklımda birkaç şey vardı (örneğin test edilebilirlik, gevşek bağlantı). Size dosyalarımı nasıl bölmeye karar verdiğimi göstereceğim ve giderken neden bir şeyi diğerine tercih ettim gibi şeyler ...

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, uygulamamın giriş noktasıdır. App nesnesini oluşturur ve görünüm modelleri için bir ad alanı sağlar (yakında çıkacaktır). Jquery- mobile'ın sağladığı mobileinit olayını dinler .

Gördüğünüz gibi, bir çeşit ajax servisinin bir örneğini yaratıyorum (ki bunu daha sonra inceleyeceğiz) ve "servis" değişkenine kaydediyorum.

Ayrıca, içinde iletilen hizmet örneğini alan viewModel örneğini oluşturduğum ana sayfa için pagecreate olayını da bağlarım. Bu nokta benim için önemlidir. Herhangi biri bunun farklı yapılması gerektiğini düşünüyorsa, lütfen düşüncelerinizi paylaşın!

Mesele şu ki, görünüm modelinin bir hizmet (GetTour /, SaveTour vb.) Üzerinde çalışması gerekir. Ama ViewModel'in bunun hakkında daha fazla bilgi sahibi olmasını istemiyorum. Örneğin, bizim durumumuzda, sahte bir ajax hizmetinden geçiyorum çünkü arka uç henüz geliştirilmemiştir.

Bahsetmem gereken bir diğer şey de ViewModel'in gerçek görünüm hakkında sıfır bilgisine sahip olmasıdır. Bu yüzden pagecreate işleyicisinden ko.applyBindings'i (viewModel, this) çağırıyorum . Görünüm modelini, test etmeyi kolaylaştırmak için gerçek görünümden ayrı tutmak istedim.

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)

Bir nesne değişmez sözdizimi kullanarak çoğu nakavt görünüm modeli örneklerini bulacak olsanız da, geleneksel işlev sözdizimini bir 'öz' yardımcı nesnelerle kullanıyorum. Temelde bu bir zevk meselesi. Ancak bir diğerine başvurmak için bir gözlemlenebilir özelliğe sahip olmak istediğinizde, nesneyi tek seferde birebir yazamazsınız, bu da onu daha az simetrik yapar. Farklı bir sözdizimi seçmemin nedenlerinden biri bu.

Bir sonraki neden, daha önce bahsettiğim gibi parametre olarak aktarabileceğim hizmettir.

Bu görüş modelinde doğru yolu seçip seçmediğimden emin olmadığım bir şey daha var. Sonuçları sunucudan almak için ajax hizmetini periyodik olarak yoklamak istiyorum. Bu yüzden, bunu yapmak için startServicePolling / stopServicePolling yöntemlerini uygulamayı seçtim . Buradaki fikir yoklamayı sayfa gösterisinde başlatmak ve kullanıcı farklı bir sayfaya gittiğinde onu durdurmaktır.

Hizmeti yoklamak için kullanılan sözdizimini göz ardı edebilirsiniz. Bu RxJS büyüsü. Abone ol (işlev (istatistikler) {..}) bölümünde görebileceğiniz gibi onu yokladığımdan ve gözlemlenebilir özellikleri döndürülen sonuçla güncellediğimden emin olun .

App.MockedStatisticsService.js

Tamam, size gösterecek tek bir şey kaldı. Gerçek hizmet uygulamasıdır. Burada fazla ayrıntıya girmiyorum. Bu sadece getStatistics çağrıldığında bazı sayılar döndüren bir taklittir . Uygulama çalışırken tarayıcıların js konsolu aracılığıyla yeni değerler ayarlamak için kullandığım başka bir mockStatistics yöntemi daha var.

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

Tamam, başlangıçta yazmayı planladığım gibi çok daha fazlasını yazdım. Parmağım ağrıyor, köpeklerim benden onları yürüyüşe çıkarmamı istiyor ve kendimi bitkin hissediyorum. Eminim burada birçok şey eksiktir ve bir sürü yazım hatası ve gramer hatası koydum. Bir şey net değilse bana bağır ve gönderiyi daha sonra güncelleyeceğim.

Gönderi bir soru gibi görünmeyebilir ama aslında öyle! Yaklaşımım ve bunun iyi ya da kötü olduğunu düşünüyorsanız ya da bir şeyleri kaçırdığım konusunda düşüncelerinizi paylaşmanızı istiyorum.

GÜNCELLEME

Bu gönderinin büyük popülaritesi nedeniyle ve birkaç kişi benden bunu yapmamı istediği için, bu örneğin kodunu github'a koydum:

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

Sıcakken alın!


7
İnsanların ele alabileceği yeterince spesifik bir soru olduğundan emin değilim. Burada sahip olduğunuz ayrıntıyı beğendim, ancak tartışmaya açılacak gibi görünüyor. Daha az kelime ile: "Güzel blog";)
Bernhard Hofmann

Beğendiğine sevindim. O kadar çok yazdığım için endişeleniyordum ki insanlar kısa bir cevap yazmaktan korkuyorlar. Bununla birlikte, her türlü tartışmaya açığız. Ve stackoverflow bir tartışma başlatmak için yanlış yerse
Christoph

Merhaba Christoph, bu yaklaşım senin için nasıl sonuçlandı?
hkon

Aslında, daha muhteşem AngularJS çerçevesine geçtim ;-)
Christoph

1
Bu, soru olarak yalnızca ilk birkaç paragrafı tutup geri kalanını kendi kendine yanıta taşıdıysanız daha iyi olabilir.
rjmunro

Yanıtlar:


30

Not: jQuery 1.7'den itibaren, .live()yöntem kullanımdan kaldırılmıştır. .on()Olay işleyicileri eklemek için kullanın . JQuery eski sürümlerini kullananlar kullanmalıdır .delegate()tercih için .live().

Aynı şey üzerinde çalışıyorum (nakavt + jquery mobil). Öğrendiklerim hakkında bir blog yazısı yazmaya çalışıyorum ama bu arada işte bazı ipuçları. Ayrıca nakavt / jquery mobile öğrenmeye çalıştığımı da unutmayın.

Görünüm Modeli ve Sayfa

JQuery Mobile sayfası başına yalnızca bir (1) görünüm modeli nesnesi kullanın. Aksi takdirde, birden çok kez tetiklenen tıklama olaylarıyla ilgili sorunlar yaşayabilirsiniz.

Modeli Görüntüle ve tıklayın

View-models tıklama olayları için yalnızca ko.observable-fields'ı kullanın.

ko.applyBinding bir kez

Mümkünse: her sayfa için yalnızca ko.applyBinding'i bir kez çağırın ve ko.applyBinding'i birden çok kez çağırmak yerine ko.observable's kullanın.

pagehide ve ko.cleanNode

Sayfa gizlemesinde bazı görünüm modellerini temizlemeyi unutmayın. ko.cleanNode jQuery Mobiles oluşturmayı bozuyor ve html'yi yeniden oluşturmasına neden oluyor. Bir sayfada ko.cleanNode kullanıyorsanız, veri rollerini kaldırmanız ve işlenmiş jQuery Mobile html'sini kaynak koduna eklemeniz gerekir.

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

pagehide ve click

Tıklama olaylarına bağlıyorsanız - .ui-btn-active'i temizlemeyi unutmayın. Bunu gerçekleştirmenin en kolay yolu şu kod pasajını kullanmaktır:

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

Sorum çok belirsiz olduğundan ve cevaba en çok emek veren siz olduğunuz için, sizinkini kabul edilen cevap yapacağım.
Christoph

Bunu hiç çözdün mü? KO ve JQM'yi entegre etmek için çok zamanım var ve bunun nasıl yapılacağına dair iyi bir kılavuz yok (veya uçtan uca demoyu gösteren bir jsFiddle).
kamranicus

1
Hayır, AngularJS çerçevesine geçtim. Bunu KO'ya üstün buldum. Ve AngularJS / jqm'yi sonsuza dek en iyi arkadaşlar haline getirmek için oldukça iyi bir adaptör projesi var: github.com/tigbro/jquery-mobile-angular-adapter Bununla birlikte, şimdiye kadar yaptığım şey için bu adaptörü kullanmak çok abartılı görünüyordu. Ne de olsa jqm'nin html / css'sini kullanmak ve kontrolleri bir Angular yönergeye dönüştürmek oldukça kolay: jsfiddle.net/zy7Rg/7
Christoph

Burada tanımladığım bir yapıyı oluşturabilirsiniz . Bu şekilde uygulama üzerinde tam kontrole sahip olacağınızdan eminim.
Muhammad Raheel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.