Uncategorized

Knockout.js で Multiple View (Partial View) をエレガントに切り替える方法

環境 : Visual Studio 2013

こんにちは。

今日は小ネタです。でも、Visual Studio 2013 を活用してもらう上で大切なので、あえて詳説します。(前回投稿の続きです。)

前回の投稿「Visual Studio 2013 の Single Page Application (SPA) テンプレートを使った開発 (Knockout.js)」の最後に記載しましたが、Visual Studio 2013 が作成する Single Page Application (SPA) のテンプレートではエレガントなビューの切り替えをおこなっています。ページ自体のポストバックをおこなわずに、ログイン画面 (Views/Home/_Login.cshtml)、ユーザー登録画面 (Views/Home/_Register.cshtml)、アプリケーション本体 (Views/Home/Home.cshtml) などの画面の切り替えをおこないます。(こうした処理も、徹底的に Single Page Application のアーキテクチャを使って作成されているわけです。)
今日はその辺りをちゃんと解説 (補足) しておきたいと思います。

Single Page Application で複数のビューを動的に切り替えたい場合、すぐに思いつくのは、Node の表示・非表示 (visible) を変更して、その下の Element の表示を動的に切り替える方法ではないでしょうか ? しかし、Visual Studio 2013 の SPA のプロジェクト・テンプレートでは、knockout の特徴をうまく利用することで、もっとエレガントにこうした View の切り替えをおこなっています。

 

そもそも “with” は何者か ? (What is “with” region ?)

ここで紹介する knockout の “with” は、もともとは、Region (スコープ的なもの) を設定するために使用されます。

例えば、以下は、実行結果を見るまでもないと思いますが、Total が 200000、Count が 5 と表示されます。(これは、ごく一般的な knockout の記述です。)

<!DOCTYPE html><html><head>  <title></title>  @Scripts.Render("~/bundles/jquery")  @Scripts.Render("~/bundles/knockout")  <script>    $(document).ready(function () {      var ordersystemViewModel = {        products: {          total: ko.observable(200000),          count: ko.observable(5)        }      };      ko.applyBindings(ordersystemViewModel);    });  </script></head><body>  Total = <span data-bind="text: products.total"></span>  <br />  Count = <span data-bind="text: products.count"></span>  <br /></body></html>

これが、”with” を使うと、下記の通り記述できます。
今回は、HTML のコメントを使っていますが、”if”、”foreach” などと同じように、data-bind 属性を使って記述しても構いません。

. . .<body>  <!-- ko with: products -->  Total = <span data-bind="text: total"></span>  <br />  Count = <span data-bind="text: count"></span>  <br />  <!-- /ko --></body>. . .

“with” のもう 1 つの顔

さて、ここからが本題ですが、実は、この “with” は、null や undefined が明示的に指定された場合には、そのブロック内の要素を無視します。つまり、表示されません。

例えば、以下のサンプル・コードの場合、きっと、knockout のエラー (例外) が発生するか、あるいは、ページが表示されたとしても「Total = 」、「Count = 」と表示されるであろうと想像するでしょう。
しかし、それは間違いです。

<!DOCTYPE html><html><head>  <title></title>  @Scripts.Render("~/bundles/jquery")  @Scripts.Render("~/bundles/knockout")  <script>    $(document).ready(function () {      var ordersystemViewModel = {        products: {          total: ko.observable(200000),          count: ko.observable(5)        },        customers : null      };      ko.applyBindings(ordersystemViewModel);    });  </script></head><body>  <!-- ko with: customers -->  Total = <span data-bind="text: total"></span>  <br />  Count = <span data-bind="text: count"></span>  <br />  <!-- /ko --></body></html>

この実行結果は、下図の通り、「Total = 」、「Count = 」すら表示されません。
この “with” のブロック全体がスキップされているためです。

例えば、下記のコードでは、[Change !] ボタンを押すたびに、下図の通り画面の表示を切り替えることができます。

<!DOCTYPE html><html><head>  <title></title>  @Scripts.Render("~/bundles/jquery")  @Scripts.Render("~/bundles/knockout")  <script>    $(document).ready(function () {      var orgdata = {        total: ko.observable(200000),        count: ko.observable(5)      };      var ordersystemViewModel = {        products: ko.observable(orgdata)      };      ordersystemViewModel.toggle = function () {        if(this.products())          this.products(null);        else          this.products(orgdata);      };      ko.applyBindings(ordersystemViewModel);    });  </script></head><body>  <!-- ko with: products -->  Total = <span data-bind="text: total"></span>  <br />  Count = <span data-bind="text: count"></span>  <br />  <!-- /ko -->  <button data-bind="click: toggle">Change !</button></body></html>

 

美しい Multiple View のための実装

この仕組みと knockout.js の “computed” を組み合わせると、この投稿でテーマとしている Multiple View の切り替えがエレガントに実装できます。(“computed” については「3つのMVC系人気フレームワーク、Backbone.js/AngularJS/Knockout.js」を参照してください。)
通常、View は多数 (複数) 存在しますが、そのうち、どれか 1 つのみを表示し、他の View はすべて表示されないようにする必要があるためです。

例えば、”computed” を使って以下の通り実装することで、[View name] のテキスト ボックスに入力した View のみが動的に表示されます。

<!DOCTYPE html><html><head>  <title></title>  @Scripts.Render("~/bundles/jquery")  @Scripts.Render("~/bundles/knockout")  <script>    $(document).ready(function () {      function productViewModel() {        totalPrice = ko.observable(200000);      };      function customerViewModel() {        totalCustomer = ko.observable(6);      };      function supplierViewModel() {        totalPartner = ko.observable(58);      };      function ordersystemViewModel() {        var self = this;        self.currentView = ko.observable("product");        self.products = ko.computed(function () {          if (self.currentView() == "product")            return new productViewModel();          else            return null;        });        self.customers = ko.computed(function () {          if (self.currentView() == "customer")            return new customerViewModel();          else            return null;        });        self.suppliers = ko.computed(function () {          if (self.currentView() == "supplier")            return new supplierViewModel();          else            return null;        });      };      ko.applyBindings(new ordersystemViewModel());    });  </script></head><body>  <!-- ko with: products -->  This is Product View !  <br />  Total = <span data-bind="text: totalPrice"></span>  <br />  <!-- /ko -->  <!-- ko with: customers -->  This is Customer View !  <br />  Total = <span data-bind="text: totalCustomer"></span>  <br />  <!-- /ko -->  <!-- ko with: suppliers -->  This is Supplier View !  <br />  Total = <span data-bind="text: totalPartner"></span>  <br />  <!-- /ko -->  View name <input type="text" data-bind="value: currentView" /></body></html>

ここでは、上記の products、customers、suppliers のそれぞれについて “computed” を実装していますが、Visual Studio 2013 の SPA のテンプレートのように、これらの各 computed の処理を ordersystemViewModel のメンバーとして共通化すると良いでしょう。(すべて似たような処理なので。。。) また、上述の “with” を使った 3 つのビューは、ASP.NET MVC の Partial View を使って異なる .cshtml ファイルで実装できます。Visual Studio 2013 の SPA のテンプレートを是非参考にしてみてください。

上記のコードからもわかるように、この方式が優れている点は、ビューの分割をしやすくしているだけでなく、ViewModel のほうも分離できる点です。(“with” で region をわけているため。)

Visual Studio 2013 の SPA のテンプレートでは、Scripts/app/home.viewmodel.js の HomeViewModel に必要な View Model を追加し、Views/Home/_Home.cshtml の Partial View を構築することで、提供されているログイン処理などをそのまま活用した形で、アプリケーション本体を構築できるようになっています。

 

Categories: Uncategorized

Tagged as:

2 replies»

Leave a Reply