平々毎々(アーカイブ)

はてなダイアリーのアーカイブです。

Web Forms + jQuery UI = Juice UI

One ASP.NET Advent Calendar 2012 の 14 日分の記事です。
前日(13日)の記事はxin9leさんの「DynamicなTempData」でした。
翌日(15日)の記事は aoki1210さんの「IIS Application InitializationでASP.NETアプリの起動を高速化(ウォームアップ)」です。

Juice UIとは

Juice UIとは、ASP.NET Web FormsでjQuery UIを使うときの、何で俺はこんな選択してしまったんだ素直にASP.NET MVCを使っておけばよかったじゃないかクソックソッといった感じの呪詛を軽減できる、たぶん軽減される、軽減するんじゃないかな、ま、ちょっと覚悟はしておけ、なものです。

http://juiceui.com/

もうちょっと具体的に書くと、

まあそんなような面倒なことを隠してくれるのがJuice UIです。

OSSで、ライセンスはMIT/GPLv2。参考までに、かるあさんの紹介記事も合わせてどうぞ。

さわってみて気になったこと

そんなわけで、Web Formsが嫌いでなければ、Juice UIもいいかな、と思うのですが、ちょっと触ってみて、あれれ?と思ったことがあったので、メモしておきます。なお、さわってみたのはバージョン1.1.1です。

インストールして動かすまで

NuGetで取得できます。jQueryjQuery UI、AmplifyJS、json2が一緒についてきます。

なんだけど、動かしてみたらjQueryがロードされなくていきなりJavaScriptエラーが。Juice UIが出力している<script>タグのsrcは"jquery-1.8.2.js"と"jquery-ui-1.9.0.js"なんですが、NuGetで取得されたのがjQuery 1.8.3とjQuery UI 1.9.2だったという。

Global.asaxのApplication_Startでリソースマッピングを変更するのがいいかと思います。合わせてCSSテーマも設定しておきましょう。

ScriptManager.ScriptResourceMapping.AddDefinition("jquery", new ScriptResourceDefinition
{
    Path = "~/Scripts/jquery-1.8.3.min.js"
});
ScriptManager.ScriptResourceMapping.AddDefinition("jquery-ui", new ScriptResourceDefinition
{
    Path = "~/Scripts/jquery-ui-1.9.2.min.js"
});
Juice.Framework.CssManager.CssResourceMapping.AddDefinition("juice-ui", new Juice.Framework.CssResourceDefinition
{
    Path = "~/Content/themes/Supercharged/jquery-ui-1.9.0.custom.css"
});

上のコードを見ても気づくかもしれませんが、Juice UIを使うにはaspx上にScriptManagerが必須ですのでご注意を。
それと、Juice UIのアセンブリ名は"JuiceUI"、コントロールやエクステンダの名前空間は"Juice" なので、aspxの先頭の方には

<%@ Register Assembly="JuiceUI" Namespace="Juice" TagPrefix="Juice" %>

を書いてください。

Accordion

カスタムコントロール。割と普通に使えるし、AutoPostBackプロパティをtrueにしておくと切り替え時にポストバックされてActivePanelChangedイベントが走る……はずだったんだけど環境によってはポストバックされずに変な動きをするかも。.NET 4.5が怪しいんだけど追いきれてません。(追記)分かった。Strictモードでは動かないスクリプト吐いてる。IE9以前じゃないと動かない。クソックソッ。

Button

エクステンダーなのでrunat="server"なコントロールに適用する……んだけど、自分自身のクラス名がButtonなのでasp:Buttonに適用できないアホなバグがあった。パッチのpull requestを送っておいたけど、受け入れられるまでは派生クラスを作って、

[TargetControlType(typeof(System.Web.UI.WebControls.Button))]

属性をつけて使うか、自分でソースに手を入れてください。

Datepicker

これもエクステンダー。runat="server"なinput要素かasp:TextBoxに適用する。ただしデフォルトだと英語カレンダーになります。DateFormatとかDayNamesといったプロパティに設定して日本語カレンダーにしてもいいんだけど、公式のやりかたに則って"jquery.ui.datepicker-ja.js"を読み込ませたい場合は派生クラスを作って

protected override IEnumerable<ScriptReference> GetScriptReferences()
{
    var datepickerJa = new ScriptReference { Path = "~/Scripts/jquery.ui.datepicker-ja.js" };
    return base.GetScriptReferences().Concat(new[] { datepickerJa });
}

とやるのがいいと思います。<head>に<script>書くとjQueryより先にロードされてエラーになるよ。

Dialog

カスタムコントロールかと思ったらエクステンダー。<div>タグにrunat="server"をつけたHTMLコントロールに対して適用する。
サーバーサイドでいろんなプロパティに値を設定することができるのはいいけど、Positionプロパティへの設定値は注意が必要です。jQuery UIのAPI仕様上はStringまたはArrayを指定することができるので、Juice UIも描画するときにエスケープしたりクォートしたりしていない。つまるところ、たとえば "top" という文字列を設定したければ、

<Juice:Dialog runat="server" ID="Dialog1" TargetControlID="Div1" AutoOpen="false" Position="'top'" />

みたいに自分でクォートする必要があります。
また、ダイアログの表示/非表示をJavaScriptで制御するときには、結局jQuery UIを使うことになります。漏れのある抽象化ってやつだ。よくある話ではあるけど。
ちょっと興味を惹かれた点は、ダイアログの表示位置やサイズをユーザー操作で変更すると、それをローカルストレージか何かに記憶するようで、ポストバック後に再表示しても同じ位置・同じ大きさで表示されることぐらい。

Menu

カスタムコントロール。<a>タグやasp:LinkButtonを格納できるし、入れ子にもできるし、まあ割と普通に使える。jQuery UIのアイコンを使いたい場合は、<a>タグやasp:LinkButtonの中に空の<span>要素を置いて、それにアイコン用CSSクラスを適用するとよいです。
注意点は、DataSourceプロパティは持ってないこと(なんだけど、HierarchicalDataBoundControlは面倒で嫌いだから、個人的には困らない)。

ProgressBar

カスタムコントロール。入れものとしての<div>要素が描画され、その中に値を表す<div>要素が描画されるイメージなので、CSSを追加で当てるときはそれを知っておく必要があります。
これも、JavaScriptで制御するときには結局jQuery UIを使うことになります。
っていうかそもそもWebのプログレスバーは鬼門。1つのまとまった処理が複数のリクエスト(ポストバックを繰り返すのでも、AJAXで非同期にWeb APIを呼ぶのでもいいけど)で構成される状況ならまだ実装しやすいんだけど、時間のかかる長い処理をサーバーサイドで実行していて、その進捗をポーリングしてブラウザに表示させたい、とか言い出すと割と死ねる。暇が作れたらAdvent Calendar 2周目のネタにしてもいいけど、ASP.NETとはあんまり関係ない話かな……

Slider

カスタムコントロール。割と普通に使えるし、AutoPostBackプロパティをtrueにしておくと値の変更時にポストバックされてValueChangedイベントが走る。もちろんAutoPostBackじゃなくて、ボタンコントロールとかで明示的にポストバックしてもOKです。
……ただし、Rangeプロパティをtrueに設定して、範囲指定できるように設定しておくと、ポストバック時にはValueChangedイベントが発火しないんだな……。や、範囲値はValuesプロパティで取得するので、実際にValueプロパティは変化してないんだけど、なんだかなあ。

Spinner

エクステンダー。asp:TextBoxか、runat="server"なinputコントロールに適用する。AutoPostBackプロパティをtrueにしておくと値の変更時にポストバックされてValueChangedイベントが走……らない!バグっぽいけど、時間がなかったので修正方法も回避策も見つけられてません。asp:TextBoxのTextChangedイベントなら当然ながら動くし、それでいいか。

Tabs

カスタムコントロール。AutoPostBackプロパティをtrueにしておくと値の変更時にポストバックされてActiveTabChangedイベントが走……る前にHttpRequestValidationException。おいこら!
ValidateRequestを切ればAutoPostBackも動くけど、なんだかなあ。

ToolTip

エクステンダー。TargetControlIDで指定したコントロール、およびその内側のHTMLにtitle属性があれば、ぜんぶjQuery UIのきれいなツールチップとして表示されます。WebControlのToolTipプロパティもtitle属性として描画されるから、もちろん対象となります。割と普通に使える。

最後に

Draggableとかの他のコントロールは時間がなくて試せませんでした。すみません。

まだまだ完成度が低い部分もありますが、OSSなんで手直しして使えばいいかと思います。ASP.NET Web FormsでjQuery UIを使うなんていう物好きはね!