ほんじゃーねっと

おっさんがやせたがったり食べたがったりする日常エッセイ

jQuery UI.Layout Plugin を使って単一ページUIを作る

Google App Engineを使うようになって、リクエスト時間の節約のために
重いページ(HTML)のロードは最初の1回だけにして、以降はページ遷移なしで
AjaxJSONのやりとりをしてビューを変更するような方法をとることが多くなった。


UIについても毎回デザインを考えるのも面倒だしロクなものができなかったので、
jQueryUIとjQuery UI.Layout Pluginを利用するようになった。
この2つを利用するだけでアプリケーション風のUIが実現できて、
UI関連の悩みをほぼ解消してくれた。


jQueryUIはボタンやタブ、アコーディオンメニューなどのUIウィジェットを、
jQueryUIのサイト上で作成したデザインテーマに合わせて表示してくれるので、
統一感のあるデザインがてっとり早くできる。
自分でカスタマイズした設定を再編集できるようにしてくれたらもっと便利なのだけど。
http://jqueryui.com/


jQuery UI.Layout Plugin はjQueryUIのプラグインで、ページを上下左右中央に
分割してアプリケーション風に表示できるようにしてくれる。入れ子にしたり
細かくオプションを設定して調整できるので、便利。
http://layout.jquery-dev.net/


今後も上記の構成でAppEngine用アプリを作ると思うので、
再利用できる雛形を作っておく。


まず、CSSとJSのライブラリは、下記のものを読み込んでおく。
blueprintは必要ない気がしてきてる。
jQueryとjQueryUIのライブラリはGoogleのSDNを使用する。
作成したデザインテーマのCSSとイメージフォルダを配置してCSSを読み込んでおけば、
ちゃんとデザインテーマは反映される。

<link rel="stylesheet" href="/css/blueprint/screen.css" type="text/css" media="screen, projection" />
<link rel="stylesheet" href="/css/blueprint/print.css" type="text/css" media="print" />
<!--[if IE]><link rel="stylesheet" href="/css/blueprint/ie.css" type="text/css" media="screen, projection" /><![endif]-->
<link rel="stylesheet" href="/css/jquery-ui-1.8.custom.css" type="text/css" />
{% block sublayoutcss %}{% endblock %}
{% block extracss %}{% endblock %}
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript"> google.load("jquery", "1.4.2"); </script>
<script type="text/javascript"> google.load("jqueryui", "1.8.0"); </script>
<script type="text/javascript" src="/js/jquery/jquery.layout.min.js"></script>
<script type="text/javascript" src="/js/main.js"></script>


HTMLのコンテンツは、jQuery UI.Layout Plugin用にdivを作成しておく。

よく使うパターンは、
上にヘッダー(.ui-layout-north)を配置して、
左にメニュー(.ui-layout-west)、
中央に切り替わるメインコンテンツ(.ui-layout-center)、
フッターなし、
という形。

左のメニューはさらに分割して、
中央(.west-center)にメニュー表示エリア、
下部(.west-south)に広告用エリアを作成しておく。

中央のメインコンテンツもさらに分割するが、
ここでは様々なレイアウトを切り替えるので、個々のビュー用にdiv(.center-view)を
作成してその中に個別のレイアウトを作成する。
今回の例では、
初期表示用ビュー(#index-view)は上部(.center-north)と中央(.center-center)に分割し、
ヘルプビュー(#help-view)は上部(.center-north)と中央(.center-center)、下部(.center-south)に分割
している。

<div class="ui-layout-north">
</div>
<div class="ui-layout-west">
<div class="west-center">
<a class="ui-state-default ui-corner-all index-btn" href="#"><span class="ui-icon ui-icon-home"></span>インデックス</a>
<a class="ui-state-default ui-corner-all help-btn" href="#"><span class="ui-icon ui-icon-check"></span>ヘルプ</a>
</div>
<div class="west-south">
</div>
</div>
<div class="ui-layout-center">
<div class="center-view" id="index-view">
<div class="center-north">
<h3 class="ui-widget-header">インデックス</h3>
</div>
<div class="center-center">
</div>
</div>
<div class="center-view" id="help-view">
<div class="center-north">
<h3 class="ui-widget-header">ヘルプ</h3>
</div>
<div class="center-center">
</div>
<div class="center-south">
</div>
</div>
</div>


最後にJavascriptでレイアウトを設定する。
ビュー毎にJavascriptのオブジェクトを使用するようなフレームワークにしてみた。
prototypeを使うべき場面と使うべきでない場面は正直よく分かってない。

(function ($) {
$.tsPlatform = function (options) {
return new Platform(options);
};
var Platform = function (options) {
this.init(options);
};
$.extend(Platform.prototype, {
init: function (options) {
var self = this;
self.options = $.extend({
"doc_layout": null,
"west_layout": null,
"view_map": {
"index": new IndexView(self),
"help": new HelpView(self)
},
"current_view": null
}, options || {});
self.setup_layouts();
self.setup_events();
self.load_view("index");
},
setup_layouts: function () {
var self = this;
self.options.doc_layout = $('body').layout({
name: "mainLayout",
defaults: {
},
north: {
resizable: true,
slidable: false,
closable: false,
spacing_open: 0,
size: 60
},
west: {
size: 200,
resizable: false,
slidable: false,
closable: false,
spacing_open: 0,
},
center: {
}
});
self.options.west_layout = $("div.ui-layout-west").layout({
center: {
paneSelector: ".west-center"
},
south: {
paneSelector: ".west-south",
resizable: false,
slidable: false,
closable: false,
spacing_open: 0,
size: 100
}
});
},
setup_events: function () {
var self = this;
$(".index-btn").click(function (e) {
e.preventDefault();
self.load_view("index");
});
$(".help-btn").click(function (e) {
e.preventDefault();
self.load_view("help");
});
},
get_current_view: function () {
var self = this;
if (self.options.current_view) {
return self.options.current_view;
} else {
return new BaseView();
}
},
load_view: function (view_code) {
var self = this;
self.get_current_view().unload();
$.each(self.options.view_map, function (key, view) {
if (key === view_code) {
view.load();
self.options.current_view = view;
}
});
}
});
/* Abstract Base View Class */
var BaseView = function () {};
$.extend(BaseView.prototype, {
init: function (platform, options) {},
load: function () {},
unload: function () {},
update_view: function () {}
});
/* Index View Class */
var IndexView = function (platform, options) {
this.init(platform, options);
};
$.extend(IndexView.prototype, BaseView.prototype, {
init: function (platform, options) {
var self = this;
self.platform = platform;
self.options = $.extend({
"center_layout": null,
"view_node": "#index-view"
}, options || {});
self.view_node = $(self.options.view_node);
self.setup_events();
},
load: function () {
var self = this;
$(self.view_node).show();
if (!self.options.center_layout) {
self.setup_layouts();
}
self.update_view();
},
unload: function () {
var self = this;
$(self.view_node).hide();
},
update_view: function (note_key) {
var self = this;
},
setup_layouts: function () {
var self = this;
self.options.center_layout = $(self.view_node).layout({
north: {
paneSelector: ".center-north",
resizable: false,
slidable: false,
spacing_open: 0
},
center: {
paneSelector: ".center-center"
}
});
},
setup_events: function () {
var self = this;
}
});
/* Help View Class */
var HelpView = function (platform, options) {
this.init(platform, options);
};
$.extend(HelpView.prototype, BaseView.prototype, {
init: function (platform, options) {
var self = this;
self.platform = platform;
self.options = $.extend({
"center_layout": null,
"view_node": "#help-view"
}, options || {});
self.view_node = $(self.options.view_node);
self.setup_events();
},
load: function () {
var self = this;
$(self.view_node).show();
if (!self.options.center_layout) {
self.setup_layouts();
}
self.update_view();
},
unload: function () {
var self = this;
$(self.view_node).hide();
},
update_view: function (note_key) {
var self = this;
},
setup_layouts: function () {
var self = this;
self.options.center_layout = $(self.view_node).layout({
north: {
paneSelector: ".center-north",
resizable: false,
slidable: false,
spacing_open: 0
},
center: {
paneSelector: ".center-center"
},
south: {
paneSelector: ".center-south"
}
});
},
setup_events: function () {
var self = this;
}
});
})(jQuery);
$(document).ready(function () {
var platform = $.tsPlatform({});
});


今後はこのフレームワークを改良していこう。