2009年3月11日水曜日

Futuresに含まれるMvc Controls

RC2のFuturesアセンブリ(ソースでも)には、Mvc Controlsが含まれてます。System.Web.UI.Controlクラスの派生クラスとして作成されてるので、WebFormsで使うサーバーコントロールと同じですね。HTML+CSS+JavaScriptで作成するようなクライアントサイドのコントロールじゃなくてASPXのPage Lifecycleの中でコントロールツリーとして生成されるサーバーコントロールです。

Futuresに含まれるコントロールは以下の7つ。

  • Label
  • TextBox
  • Password
  • Hidden
  • DropDownList
  • ActionLink
  • Repeater

はて、ナゼMVCにこれらサーバーコントロールが含まれてるんだろう(Futuresだけど)。ポストバック(ASP.NETの)もViewStateも無いので、ポストバックされた後のコントロールツリー構築なんてことは発生しない(そもそもポストバック先がPageじゃなくてControllerなんだから)し、かといって、積極的にコントロールツリーを構築してからレンダリングするなんてことは全然見通しのいい完全制御出来るHTMLとは言い難い(オレはサーバーコントロールのレンダリング結果を完全に把握してるぜ!という話じゃなくてデス)。なので、考えられる理由は、<%~%>での埋め込みコードを減らして、見やすくしようという意図なのかな~、と推測してます。理由なんてどーでもいいんですけど。

ASP.NET MVC Release Candidate 2: I declare myself to be declarative! - Eilon Lipton's Blog

↑こちらでしっかりと紹介されてます。

試しに使って見ましょう。MVCのプロジェクトを新規に作成し、プロジェクトの参照設定にMicrosoft.Web.Mvcを追加しましょう。続いて、web.configの設定をしておきます。

<system.web>
 …
 <pages>
   <controls>
     <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
     <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
     <add tagPrefix="mvc" namespace="Microsoft.Web.Mvc.Controls" assembly="Microsoft.Web.Mvc"/>
    </controls>
   <namespaces>
     <add namespace="System.Web.Mvc"/>
     <add namespace="System.Web.Mvc.Ajax"/>
     <add namespace="System.Web.Mvc.Html"/>
     <add namespace="System.Web.Routing"/>
     <add namespace="System.Linq"/>
     <add namespace="System.Collections.Generic"/>
     <add namespace="Microsoft.Web.Mvc"/>
    </namespaces>
 </pages>
 …
</system.web>
上記太字の部分、サーバーコントロールのプレフィックス登録と、ページでのネームスペース登録。これをやっておかないと記入が面倒なことになるので忘れずに。

HomeControllerのIndexアクションで出力用のデータをViewDataにセットしておきます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Mvc.RC2.Controllers
{
 [HandleError]
 public class HomeController : Controller
 {
   public ActionResult Index()
   {
     ViewData["Message"] = "Welcome to ASP.NET MVC!";
      ViewData["TextBox"] = "テキストボックスに表示するメッセージ";
     return View(new { Label = "ラベルに表示するメッセージ" });
    }

   public ActionResult About()
   {
     return View();
   }
 }
}

ViewDataにテキストを入れて渡す方法と、Viewへ匿名クラスを直接渡す方法を書いておきます。匿名クラスも簡単に取り出せるのを覚えておくとちょっと便利です。

これらを表示するタメにViews/Home/Index.aspxにコードを追加します。

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
   Home Page
</asp:Content>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
   <h2><%= Html.Encode(ViewData["Message"]) %></h2>
   <p>
       To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
   </p>
  
    <p>
     <mvc:TextBox runat="server" Name="TextBox"></mvc:TextBox>
     <mvc:Label runat="server" Name="Label"></mvc:Label>
   </p>
</asp:Content>

サーバーコントロールなのでrunat="server"を忘れずに。Nameで指定したのがエレメントのName属性にセットされるのと同時にViewDataから同名の値を取得して展開してくれます。 なので、これで出力されるHTMLは↓こうなります。

    <h2>Welcome to ASP.NET MVC!</h2>
   <p>
       To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
   </p>
  
   <p>
     <input name="TextBox" type="text" value="テキストボックスに表示するメッセージ" />
     ラベルに表示するメッセージ
   </p>

匿名クラスの値がそのまま展開されてますね。これは内部でViewData.Eval(データーキー)を呼び出してるからです。なので、 MVC Controls特有の動きというわけではなく、自分でも普通に同じ方法で値を取得出来ます。Viewに以下のようなコードを書いてみます。

    <p>
     <div><%= ViewData.Eval("TextBox") %></div>
     <div><%= ViewData.Eval("Label") %></div>
   </p>

これは以下のようなHTMLとして展開されます。

    <p>
     <div>テキストボックスに表示するメッセージ</div>
     <div>ラベルに表示するメッセージ</div>
   </p>

匿名クラスの場合、Modelのクラスが不明なので、インテリセンスでの取得は出来ないですけど、Evalを使う事で取得は出来ます。例えば、RenderPartialするユーザーコントロール(ascx)に、匿名クラスで値を渡す時(View Modelクラスを書くほどでも無いけどViewDataをそのまま参照するのもコード見にくい、なんて時)でもユーザーコントロール内でViewData.Eval()で取得出来るので、何かと便利だったりします。

ちなみにこのmvc:LabelコントロールにはTruncateLengthとTruncateTextなんていうプロパティがあって、コレを指定することで自動で長い文字列をカットしてくれます。例えば先ほどのLabelの部分を以下のように書き換える。

    <p>
     <mvc:TextBox runat="server" Name="TextBox"></mvc:TextBox>
     <mvc:Label runat="server" Name="Label" TruncateLength="5" TruncateText="~"></mvc:Label>
   </p>

そうすると5文字にカットしてサフィックスをくっつけてくれます。HTMLは↓こうなります。

    <p>
     <input name="TextBox" type="text" value="テキストボックスに表示するメッセージ" />
     ラベルに表~
   </p>

TruncateTextは初期値として"..."がセットされてるので、何も指定しなければ今回の例でいうと「ラベルに表…」となります。

RepeaterはWeb FormsのRepeaterと同じように動くので、繰り返しをforeachで書きたくないなんて場合は利用するのもいいんじゃないかな。DataSource指定やDataBind()はしないのでコードビハインドも不要です。今後いろんなコントロールが出てくるかもしれないね。

最近のお気に入り

IMG_0457 IMG_0460 

Bluetoothスピーカー。

キラキラボディーでお気に入り。PC本体のスピーカーだとシャリシャリ感が気になるし、あんまり音も大きくならないもんね。ヘッドホンも聞きやすくていいんだけど、いちいち線をつなげて、耳に当ててとかっていうのが面倒。移動するとき外すのも面倒だし。音楽を聴いたりするわけじゃないから、音質はそんなに気にならないよ。ハンズフリーのプロファイル(?)は削除しとかないとなんか調子悪かった。

Amazon.co.jp: Bluetooth ステレオスピーカー BIT-STB2825S シルバー A2DP・HFP・HSP・ワンセグ音声SCMS-T: 家電・カメラ

IMG_0467 IMG_0468

でろ~ん。スライムじゃないよ~。キーボードを掃除するCyber Cleanっていう製品(スライムだな...)。こないだハンズで売ってるのを見かけて買っちゃった。ベトベトしないし、ちゃんとゴミを吸着してくれるし、なんか楽しいし。

Amazon.co.jp: アイリスオーヤマ サイバークリーン 135gプラスチックボトル入り PCP-135: 家電・カメラ

2009年3月8日日曜日

第2戦(Brass Division)

前回の反省を生かし、今回の試合では前半から強気の攻めで、試合の流れを持ってくることを心がけよう。そんな目標を事前に立てての試合。試合前にはメンバーが全然集まって無くて唖然としたけど、試合始まる直前にはなんとか2セット揃ってホッとした。目標を立てたのに人がいないことにアワアワし過ぎて、すっかり目標のことを忘れちゃってたよ。 前半からガンガンがんばれ作戦っていうこともあって、セットの組み合わせも1stタケ・ミズ・セト、2ndオグ・シゴ・ミチと今期から参加のシンタローと組むことになり、走りまくらなきゃいけない予感。前回の試合で走らなすぎたら見方からのブーイングで心折れそうになったのを思いだし、今回はちゃんと最初から攻め気も見せつつプレーしなきゃね。 珍しくイマイちゃんとユミちゃんがいないおかげで5メンズ1ウーマン。オニャノコいないと、ブラスではブーイングがでちゃうS40なんだけど...。 まぁ、そんなこと心配してもしょうがね。 試合開始するまで全然気がつかなかったけど、開始2分で3点...。おかしい。いや、得点できることはいいんだけど、いくらなんでもおかしい。そんなアッサリ目標通り得点できるなんておかしすぎる。相手ゴーリーをよくよく見てみると、なんかスケーティングがゴーリーのそれとは違う。横移動も前後移動もままならない感じで、ほぼゴールライン上で微動だにしてない。もしや、プレーヤーゴーリー?ちゃんとオスギに確認しとけば良かったけど、試合は始まっちゃってるし、相手ベンチに「ゴーリーの人プレーヤー?」なんて聞けるわけもなく。 そのまま、前半から攻め続けて、と言うか我守らずを全員がモットーとしてるんじゃないのかってくらいの守備放棄。カネコさんが初戦に引き続いての鉄壁っぷりを発揮してくれてたからよかったものの。そんな流れで8-0で前半終了。どうも外野からのブーイングが気になる...。 後半も相変わらずノーガード。流石に守りきれず目の前でリバウンドを叩かれて失点。自分の目の前っていうのが責任感じる。守ってたのに守ってなかったんじゃないのかっていう失点の仕方だったのが余計にたちが悪い。それでも、攻めに攻めて得点を重ねるんだけど、終了間際にまたしても自分がリンクに乗ってるときに失点...。なんかもう、プレーオフの時からそうだけど、こうも終了間際の失点が続くと切なくなるね。 試合終了後、オスギに確認したら、急遽ゴーリーがこれなくなったそうで、プレーヤーがゴーリーをしてたんだそうな。単純に得点しまくってたのはそんな理由からですよ。17-2と勝ったはいいけど全然勝った気もせず。失点には常に絡む自分を励ましたい。 今回の試合では自陣ベンチからのブーイングがなかったものの、外野からのブーイングが炸裂。日曜に戸塚に行っても「昨日アメージングに鬼が出た」だの「悪魔っているんだね」だの、さんざんな言われよう。これからは演技派を目指そう。必死のプレーでゴール前でこけるとか、シュートをカラぶるとか、見てて一生懸命なのは伝わるけど、コレといって活躍しないっていう...。もう、それしか、自分を守る方法が思いつかない。ぎゃふん。

2009年3月4日水曜日

ASP.NET MVC RC2リリース

when its going to be released - ASP.NET Forums

↑ここで発見。最後の発言が...。まぁ、そうですけど...みたいな。

いろいろ気になるところがあったんでしょうね。安易にRTMを出さず、品質上げるためにRC2リリースの決断に敬意を表します。

ASP.NET MVC Release Candidate 2

とはいえ、RC1との違いが単にインストーラだけなわけ無いですよね。しっかりとリリースノートを確認しましょう。

CodePlexにソース/Futuresも上がってるんですが、その下にDataAnnotationsを使ったModelBinderのサンプルが...。もっと早く出してくれれば、自分でサンプル書かなくても良かったのに...。

このDataAnnotationsModelBinderの実装を確認してみると、その内容がまた超かっこいいです。IDataErrorInfo使わずに、DefaultModelBinderを派生させて、OnPropertyValidatingのオーバーライドメソッド内でValidationAttribute属性の取得・実行とModelStateへのエラー投入を行ってる。入力検証の集約というより、DataAnnotationsはそれだけで検証を完結させておき、モデル自体の検証や、ビジネスルールの検証は切り離して行うという設計。IDataErrorInfoならErrorのオーバーライドでモデル検証も集約できるから、どっちが分かりやすいコード(このサンプルだとモデル検証は別途モデル層に実装しましょうということになる)になるかは、アプリケーション設計者の好みでどうぞ、ってことですかね。

リリースノートに書かれてる変更点(それほど多くない)をさらっと確認してみると、以下のような感じです。

  • .NET Framework 3.5SP1必須です。入れといてね。
  • サーバーインストールモードを用意しました。VS関係のインストールがないのでデブロイ環境へのインストールはこれを使いましょう。 msiexec /i AspNetMVC1-RC2.msi /l*v .\mvc.log MVC_SERVER_INSTALL="YES"
  • GACに入れるよ。
  • AntiForgeryのCookie出力時にパスを指定できるようにしました。なので、デプロイ環境(ルートからとかサブフォルダあるとか)に合わせてパスを調整できます。
  • DefaultModelBinderが出力するエラーメッセージをローカライズできるようにしました。resxファイルを独自に定義して分かりやすいエラーメッセージを登録しておきましょう(InvalidPropertyValueとPropertyValueRequiredの2個だけ)。
  • ValidationSummaryのオーバーロードが増えたよ。メッセージリストをul/liタグで展開する前にspanタグでのタイトル表示できるなり。
  • jQuery1.3.1にしたよ。
  • あとバグフィックス。DropDownListの例外。web.configのauthenticationの値をLogOnに。Site.Masterとかで使ってるheadタグがrunat="server"をちゃんと動くように。checkboxとradiobuttonをModelStateからちゃんと復元。ルートのDefault.aspx(ルーティングでコントローラ+アクション指定してるとルートアクセスでデフォルトのコントローラ+アクションが実行されるけど、これにOutputCacheを指定しても効かない)でOutputCacheがちゃんと効くように。

基本的にRC1のコードならそのまま動くっぽいですね。

追記:2009/03/04 16:50

System.Web.AbstractionsとSystem.Web.Routingの2つのアセンブリがSP1のものを利用するように変わったんですね。なので、System.Web.Mvcだけ(Futuresを利用するならMicrosoft.Web.Mvcも)を配布すればよくなりました。グッジョブ!

2009年3月2日月曜日

ASP.NET MVCの新しいサンプル&チュートリアル

ASP.NET MVC Sample Application at www.ASP.net/MVC

ASP.NET MVCでの新しいサンプル。チュートリアルにそって順に良くなっていく。

これはMVCerはみとくべき。

イテレ-ション1 Entity Frameworkをモデルにして、アドレス帳サンプルを作成。この手順はオーソドックスな最初の作り方。これでMVCでの作成ってこれまでとどう違うのかが分かるはず。

イテレーション2 デザインギャラリーからデザインテンプレートを取得したり、自分でデザインしたものを1で作ったものに適用させて、見た目を綺麗にしましょう。ヘルパーなんかも書いたりしてデザインしやすくしておきましょう。

イテレ-ション3 フォームの入力検証を実装。シンプルな検証方法で、DataAnnotationsなどを使うわけではないです。どこで検証を実施すればいいのか、検証結果がどうViewに反映されるのかをしっかり押さえておきます。

イテレーション4 機能を疎結合にしましょう。モデル層はRepositoryに任せ、ロジック層はServiceに移動させる。それぞれインターフェイスと実装を分けて書きましょう。

イテレーション5 ユニットテスト。これまで書いたコードにたいするテストコードを書きましょう。モックを使うとテストコードを楽にかけるのでお気に入りのモックを使いましょう。ここではMoqを使います。他にもRhino MocksやTypeMockなんかもあるよ。テストの初期化時にインターフェースのインスタンスを生成し、コンストラクタインジェクション。 Service層だけじゃなく、コントローラもテストコードを書きましょう。

イテレーション6 5でテストコードを書いたけど、ここからはテストドリブンな開発にしましょう。Group機能を追加する様子をテストを書くところから始めます。その後でテストをパスするコントローラコードを書き、繰り返して機能を実装していく。一通り出来たら、テストコードもあることだし、リファクタリングしてコードをService層に移動したり、Repository層に移動して、テストをパスしないコードが発生しないようにしましょう。

イテレーション7 最後にアプリケーションをAjax化して、使いやすさも向上させましょう。ページの部分更新をしたりするので、共通部分はコントロール化して、Ajaxでの更新時にPartialViewでページ全体じゃなく、必要な部分だけを返すようなコードにしましょう。

テーブルも2個しかないし、規模としては凄く小さいけど、これがASP.NET MVCの開発スタイルの基本になると思います。しっかり身につけて、テストもはしょったりしないようにしましょう(自分で言っときながら耳が痛い...)。

カスタムViewEngineを試す

ASP.NET MVCに限った話ではないんですが、JavaScriptをページで使う場合、外部ファイル(scriptタグのsrc属性で指定する)にするか、インラインでページに直接書く(scriptタグ内にコードを書く)かどっちかになる。

今時の作り方ならJavaScriptはページの最後で外部ファイルを取り込むのが、パフォーマンス的にもよろし、ということになってますね(ハイパフォーマンスWebサイト)。

ASP.NET MVCでも、もちろん外部JSファイルを使ったアプリケーションを作るわけですが、ここで少し悩みが出てきました。

処理コード(静的)としてのJavaScriptは簡単に外部に出せるから問題にはならないんですが、サーバーサイドで生成したデータを元に処理するコードの場合、そのデータをどうやって外部ファイルで使えばいいでしょうか。分かりにくい説明ですが、例えばサーバー上でデータベースから名前一覧を取得してViewDataに入れておいたとします(こればっかりは動的)。そのViewDataを外部JSファイルではもちろんそのまま参照できません。と、いうのもscriptタグで取り込んだ外部JSファイルとページそのもののリクエスト(MvcHandler)は別物だから、いくらコントローラのアクションでViewDataに値を入れたとしても外部JSファイルを取得したリクエスト(StaticFileHandler)では参照できないというのと、そもそもJSファイルが<%=~%>を解析して処理してくれないから。あたりまえですね~。

じゃぁ、どうすればデータ(サーバーサイドで動的生成)をコード(JavaScriptを静的に取得)に簡単に渡せるんだろう。

ページ専用のJavaScriptコードはどうしても、アクションで生成されるデータを使った動的コードにしたくなってしまう。でも、それだと外部ファイルじゃなくViewにscriptタグを書いてしまうことになって、なんかスッキリしない。

  1. 外部JSファイルを拡張子JSじゃなくASPXで作成し、JavaScript用のコントローラを作成(レスポンスのコンテンツタイプをapplication/x-javascriptに変更)し、動的にJSコードを生成するようにして(ViewファイルにJavaScriptコードを書く)、scriptタグでsrc指定。
  2. 外部JSファイルにはコードだけを書き、Viewに出力されるHTMLにscriptタグを書いておいて適当な変数にデータ(JSON)で入れておいて、外部JSファイルからはこれを参照する。

1の方法をとる場合、凄く分かりにくくなるのがコントローラが違うからViewとJavaScriptそれぞれでデータ取得のロジック(もちろんJavaScript用のコントローラではクライアントで必要なデータのみですが)を書かなきゃいけないからコードが散らばるうえに、VSで開発してるにも関わらずコードハイライトもインテリセンスも効かなくなる。

例えば...

// CharaControllerのViewアクション
public ActionResult Character() {
return View();
}

// JsControllerのJavaScriptアクション
public ActionResult Character() {
ViewData["chara"] = ToJson(new {firstName="ルフィ",lastName="モンキー"});
return View();
}

// ビュー
<body>
<!-- viewの定義 -->
</body>
<script type="text/javascript" src="JsController/Character"></script>

// Js/Character.aspx(JavaScript)
var chara = <% = ViewData["chara"] %>;
// 以降charaを使った処理
これだと2つのコントローラが必要になるし、ViewDataの生成と利用が離れすぎ。

綺麗にコードとデータを分離するなら、2の方法が正解ですよね。その場合Viewページにscriptタグを書く必要がありますが、あくまで処理コードは外部に分離できる。

例えば...

// CharaControllerのViewアクション
public ActionResult Character() {
ViewData["chara"] = ToJson(new {firstName="ルフィ",lastName="モンキー"});
return View();
}

// ビュー
<body>
<!-- viewの定義 -->
</body>
<script type="text/javascript">
var viewData = <% = ViewData["chara"] %>;
</script>
<script type="text/javascript" src="chara.js"></script>

// chara.js
var chara = viewData;
// 以降charaを使った処理

Viewアクション内でJavaScriptで利用するデータを生成しておき、外部スクリプトでのデータ参照はグローバル(この場合ならviewData)を見る。

どうも1の方法に固執しすぎてて、これを解決するためにカスタムのViewEngineを作ればいいんじゃないの?というおかしな路線に走ってしまって...。結局は2の方法にすることでViewEngine作る必要は無かったことに気がついたんだけど、その過程で今ネットで見つかるViewEngineの作り方が少し古いやり方な事に気がついたので、無駄にしないために、ココにメモとして残しておきます。

ASP.NET MVC Tip #25 – Unit Test Your Views without a Web Server Maarten Balliauw {blog} - Creating a custom ViewEngine for the ASP.NET MVC framework SingingEels : Creating a Custom View Engine in ASP.NET MVC Brad Wilson: Partial Rendering View Engines in ASP.NET MVC

ViewEngineを作るといっても、単にViewパスの検索場所を変更するためだけの目的もあれば、テンプレートエンジンを置き換えてしまう目的もあると思います。

パスを変えるだけならIViewEngineの実装はせず、PhilさんのAreasデモソース(Grouping Controllers with ASP.NET MVC)のようにWebFormViewEngineを派生させてViewLocationFormatsとMasterLocationFormatsをセットして、IViewは標準のWebFormViewを使えばいいですね。

テンプレートエンジンを変えてしまいたい場合は、IViewEngineとIViewそれぞれを実装することになりますが、今回は少し楽をしてIViewEngineにはVirtualPathProviderViewEngineを使うことにします。

何を作るかというと、拡張子jsの中に/$Key$/という形でテンプレートを入れとくと、ViewDataCollection内の同名Key値を埋め込むというテンプレート。これならJavaScriptのインテリセンスもコードハイライトも有効。

IViewEngineの実装。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Hosting;

namespace Sample.Libraries
{
public class JavaScriptViewEngine : VirtualPathProviderViewEngine
{
public JavaScriptViewEngine()
{
  MasterLocationFormats = new string[0];

  ViewLocationFormats = new[]{
   "~/ViewScripts/{0}.js",
   "~/ViewScripts/Shared/{0}.js"
  };

  PartialViewLocationFormats = ViewLocationFormats;
}

protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
  return CreateView(controllerContext, partialPath, null);
}

protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
  return new JavaScriptView(viewPath);
}
}
}

IViewの実装。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.IO;
using System.Web;
using System.Text.RegularExpressions;

namespace Sample.Libraries
{
public class JavaScriptView : IView
{
private string _templatePath;

public JavaScriptView(string templatePath)
{
  _templatePath = templatePath;
}

public void Render(ViewContext viewContext, System.IO.TextWriter writer)
{
  var appPath = viewContext.HttpContext.Request.PhysicalApplicationPath;
  var filePath = VirtualPathUtility.ToAbsolute(_templatePath).Substring(1).Replace("/", "\\");
  var fullPath = Path.Combine(appPath, filePath);

  if (!File.Exists(fullPath))
    throw new InvalidOperationException("not exits javascript template file.");

  var template = File.ReadAllText(fullPath);

  writer.Write(Parse(template, viewContext.ViewData));
}

public string Parse(string contents, ViewDataDictionary viewData)
{
  return Regex.Replace(contents, @"\$\/(.+)\/\$", m => GetMatch(m, viewData));
}

protected virtual string GetMatch(Match m, ViewDataDictionary viewData)
{
  if (m.Success)
  {
    string key = m.Result("$1");
    if (viewData.ContainsKey(key))
      return viewData[key].ToString();
  }
  return String.Empty;
}
}
}

置換部分の処理はまるっきりStephenさんのコードです...。

拡張子jsのファイルはViewScriptsフォルダに入れておくようにしたものです。コントローラ名もフォマットに含めようと思ったんですが、そこはルーティングの登録を以下のようにしておくことでとりあえず必要無いな、と。でも、ViewScriptsフォルダ内はコントローラ名フォルダ/スクリプト名.jsでファイルを入れておきます。

      routes.MapRoute(
    "ViewScripts",
    "ViewScripts/{*path}",
    new { controller = "ViewScripts", action = "Index" }
  );

ViewEngineの登録も忘れずに。

ViewEngines.Engines.Add(new JavaScriptViewEngine());

あとはViewScriptsControllerを書くだけ。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

namespace Sample.Controllers
{
public class ViewScriptsController : Controller
{
//
// GET: /ViewScripts/

public ActionResult Index(string path)
{
  ViewData["test"] = "{data:'sample'}";

  return View(path);
}

}
}

まずは動くコードを、という簡単なサンプルです。

テスト用にViewScripts/Test.jsを作成。

// test.js
var viewData = /$Test$/;
alert(viewData);

あとは、通常のViewでこれをインクルード。

<script type="text/javascript" src="/ViewScripts/Test.js"></script>

これで一応動くものが出来たわけですが、ここで2の方法でいいじゃん、と思い直してコードを破棄...。しかもこの方法だとViewScriptsControllerでいろんなViewDataを入れるためのコードが必要になって、面倒なことに。もっと早い段階で気がつけば良かったけど、カスタムViewEngineを書いてみる勉強になったから良しとします。

ちなみにカスタムViewEngineで最高のサンプルは今書いたこんな中途半端なコードじゃなくてStringTemplate Template Engineじゃないかと思われます。作ってみたい方は是非そちらを参照してみてください。

string-template-view-engine-mvc - Google Code

2009年3月1日日曜日

Crystalized Intelligence

結晶性知性。 【コラム】IT資本論 (26) 8つのパラドクス - 学習パラドクス(4) 持続学習 | 経営 | マイコミジャーナル これを読んでいて、流動性知性はPCやネットで補完できる(外部記憶や計算能力)が、結晶性知性は「コンピュータによって模倣できない」と書かれてる。 専門知や経験知からしか発達していかないインテリジェンスだから、それもそうだろうとは思うけど(画期的なAIっていうドラえもん級の夢のソフトウェアが出れば別)、外部知性というかたちでPCとネットで有効活用出来るとは思う。 Wikiとかナレッジベースとかっていう形での実装ではなくて、もっとリアルタイムに近い形で。 久しぶりにネガティブレガシーと出会いました。 工業経済至上主義を未だ貫き(それはいい)、統計を完全否定し、ネットを不安視(あえて無視)することで現時点での自分の正当性を閉じた世界で証明しようとするその姿勢が面白かった。

dotnetConf2015 Japan

https://github.com/takepara/MvcVpl ↑こちらにいろいろ置いときました。 参加してくださった方々の温かい対応に感謝感謝です。