jquery-mobileとknockoutjsを使用してWebアプリケーションを構築する方法


88

私はhtml / cssとJavaScriptだけから作成されたモバイルアプリを構築したいと考えています。JavaScriptを使用してWebアプリを構築する方法についてはかなりの知識がありますが、jquery-mobileなどのフレームワークを調べたほうがいいと思いました。

最初は、jquery-mobileはモバイルブラウザーをターゲットとするウィジェットフレームワークにすぎないと思っていました。jquery-uiによく似ていますが、モバイルの世界向けです。しかし、jquery-mobileはそれ以上のものであることに気付きました。多数のアーキテクチャが付属しており、宣言型のHTML構文でアプリを作成できます。したがって、最も簡単に考えられるアプリの場合、JavaScriptを1行で自分で書く必要はありません(これはクールです。

宣言的なhtml構文を使用してアプリを作成するアプローチをサポートするには、jquery-mobileとknockoutjsを組み合わせるのがよいでしょう。Knockoutjsは、WPF / Silverlightから知られているMVVMの超能力をJavaScriptの世界にもたらすことを目的としたクライアント側のMVVMフレームワークです。

私にとってMVVMは新しい世界です。私はすでにそれについてたくさん読んだことがありますが、私は実際にそれを実際に使ったことはありません。

したがって、この投稿は、jquery-mobileとknockoutjsを一緒に使用してアプリを構築する方法に関するものです。私のアイデアは、数時間見てから思いついたアプローチを書き留め、コメントするjquery-mobile / knockout yodaを用意して、最初にプログラミングがうまくいかない理由と、プログラミングを行うべきではない理由を説明することでした場所 ;-)

html

jquery-mobileは、ページの基本的な構造モデルを提供する優れた機能を果たします。後でページをajax経由でロードできることは十分承知していますが、すべてのページを1つのindex.htmlファイルに保持することにしました。この基本的なシナリオでは、2つのページについて話しているので、物事を把握するのはそれほど難しくありません。

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

それでは、JavaScriptをお楽しみください。

アプリの階層化について考え始めたとき、いくつかのこと(たとえば、テスト容易性、疎結合)を念頭に置いていました。私がファイルを分割することにした方法を示し、移動中になぜ他のものを選択したのかなどについてコメントします...

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は、私のアプリのエントリポイントです。Appオブジェクトを作成し、ビューモデルの名前空間を提供します(近日提供予定)。jquery-mobileが提供するmobileinitイベントをリッスンします。

ご覧のとおり、私は何らかのajaxサービス(後で説明します)のインスタンスを作成して、変数「service」に保存しています。

また、サービスインスタンスが渡されるviewModelのインスタンスを作成するホームページのpagecreateイベントをフックします。この点は私にとって不可欠です。誰かが考えているなら、これは違ったやり方で行われるべきです、あなたの考えを共有してください!

重要なのは、ビューモデルはサービス(GetTour /、SaveTourなど)を操作する必要があるということです。しかし、ViewModelにそれ以上の情報を知らせたくありません。したがって、たとえば、私たちのケースでは、バックエンドがまだ開発されていないため、モックされたajaxサービスを渡すだけです。

私が言及すべきもう1つのことは、ViewModelには実際のビューに関する知識がないことです。そのため、pagecreateハンドラー内からko.applyBindings(viewModel、this)を呼び出しています。テストを容易にするために、ビューモデルを実際のビューから分離したままにしました。

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)

オブジェクトリテラル構文を使用したほとんどのknockoutjsビューモデルの例を見つけることができますが、私は「セルフ」ヘルパーオブジェクトで従来の関数構文を使用しています。基本的に、それは好みの問題です。ただし、1つの監視可能なプロパティで別のプロパティを参照する場合は、オブジェクトリテラルを一度に書き込めないため、対称性が低くなります。これが、別の構文を選択する理由の1つです。

次の理由は、前述したようにパラメーターとして渡すことができるサービスです。

このビューモデルには、正しい方法を選択したかどうかわからないことがもう1つあります。サーバーから結果を取得するために定期的にajaxサービスをポーリングしたいと思います。そのため、startServicePolling / stopServicePollingメソッドを実装することを選択しました。アイデアは、pageshowでポーリングを開始し、ユーザーが別のページに移動したときにポーリングを停止することです。

サービスのポーリングに使用される構文は無視できます。それはRxJSの魔法です。Subscribe(function(statistics){..})の部分で確認できるように、それをポーリングし、返された結果で監視可能なプロパティを更新していることを確認してください。

App.MockedStatisticsService.js

わかりました。あと1つだけお見せしましょう。実際のサービスの実装です。ここでは詳しく説明しません。getStatisticsが呼び出されたときにいくつかの数値を返すのは単なるモックです。アプリの実行中にブラウザーのjsコンソールを介して新しい値を設定するために使用する別のメソッドmockStatisticsがあります。

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

初めに書こうと思っていたので、もっとたくさん書いた。指が痛くて、犬が散歩に連れて行ってくれと言ってきました。ここには足りないものがたくさんあると思いますし、私はたくさんのタイプミスと文法の間違いを犯しました。不明な点がある場合は私に怒鳴ってください。投稿は後で更新します。

投稿は質問のようには見えないかもしれませんが、実際はそうです!私のアプローチについてのあなたの考えと、それが良いか悪いか、または私が物を見落としているかどうかを教えてください。

更新

この投稿の人気が高まったことと、何人かから依頼があったため、この例のコードをgithubに配置しました。

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

暑いうちにゲット!


7
人々が対処するための十分に具体的な質問があるかどうかはわかりません。私はあなたがここに持っている詳細が好きですが、それはそれ自体が議論に移るのに向いているようです。少ない言葉で: "Nice blog";)
Bernhard Hofmann '23

気に入ってもらってうれしいです。私があまりにも多くのことを書いて、人々が短い答えを書くのを恐れるのを少し心配していました。ただし、いかなる議論も歓迎します。そして、stackoverflowがディスカッションを開始するのに不適切な
Christoph

こんにちはクリストフ、このアプローチはどのようにうまくいきましたか?
2011

実際、私はより素晴らしいAngularJSフレームワークに移動しました;-)
Christoph

1
最初の数段落だけを質問として残し、残りを自己回答に移動した場合、これはより良い方法です。
rjmunro 2012

回答:


30

注: jQuery 1.7以降、この.live()メソッドは非推奨になりました。.on()イベントハンドラーをアタッチするために使用します。古いバージョンのjQueryのユーザーは.delegate()、を優先して使用する必要があります.live()

私は同じことに取り組んでいます(ノックアウト+ jqueryモバイル)。私は学んだことについてブログ投稿を書こうとしていますが、ここではいくつかの指針を示します。私はまた、ノックアウト/ jqueryモバイルを学習しようとしていることを思い出してください。

ビューモデルとページ

jQuery Mobileページごとに1つのビューモデルオブジェクトのみを使用します。そうしないと、複数回トリガーされるクリックイベントで問題が発生する可能性があります。

モデルを表示してクリック

ビューモデルのクリックイベントには、ko.observable-fieldsのみを使用してください。

ko.applyBindingを1回

可能であれば、ページごとに1回だけko.applyBindingを呼び出し、ko.applyBindingを複数回呼び出すのではなく、ko.observableを使用します。

pagehideおよびko.cleanNode

ページ非表示の一部のビューモデルをクリーンアップすることを忘れないでください。ko.cleanNodeはjQuery Mobileのレンダリングを妨害するようです-これによりHTMLが再レンダリングされます。ページでko.cleanNodeを使用する場合は、data-roleを削除して、レンダリングされたjQuery Mobile 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');
});

私の質問は非常に具体的ではなく、あなたが答えに最も多くの作業を入れたのはあなたなので、私はあなたが受け入れた答えにします。
Christoph

これを理解したことはありますか?私はKOとJQMを統合するのに時間を費やしていますが、それを行う方法に関する適切なガイド(またはjsFiddleがエンドツーエンドのデモを示す)はありません。
kamranicus

1
いいえ、AngularJSフレームワークに移動しました。私はそれがKOよりも優れていることを発見しました。:そして永遠にAngularJS / jqm最高の友達を作るためにかなり良いアダプター・プロジェクトがありgithub.com/tigbro/jquery-mobile-angular-adapterは、私がこれまでそのアダプタを使用するためにやり過ぎのようだったもののために、しかし。結局のところ、jqmのhtml / cssを使用して、コントロールをAngularディレクティブに変換するのは非常に簡単です:jsfiddle.net/zy7Rg/7
Christoph

ここで定義した構造を作成できます。この方法でアプリケーションを完全に制御できると確信しています。
Muhammad Raheel 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.