2010年12月18日土曜日

Razor Helper的なForEachでテンプレート

今年も終わりですね。タイトル意味分かんないですね。

来週から夏休み&年末年始休暇に入るので、今年の仕事終了です。てへ。ブログサイトがあるのをすっかり忘れるくらい追い込まれる毎日。よくもまぁ、こんな状態が続くもんだと感心するばかりです。え?きれてねーよ。

そんなことはいいんですけどね、ちょっとここ見てみてくださいよ。

Razor, Nested Layouts and Redefined Sections - Marcin On ASP.NET - Site Home - MSDN Blogs

ナイスエントリーにも程があるデスヨ。

Layout用のビューを直接指している、子供ビューのセクションはみえるけど、多段Layoutになってて孫ビューのセクションはスコープの範囲外でエラーになっちゃうっていうエントリー。

実際に試してみたらたしかに動かない。Section not defined。そ

foreach

りゃそうだ。それを克服するために中間LayoutでRedefineSectionで孫のセクションを生成(WebPageBase.DefineSection)してしまおうよっていう強引さには度肝を抜かれますがなによりそこじゃないんです。

その部分より何よりセクション初期値をヘルパーに渡す部分ですよ!

Func<object, HelperResult> defaultContent

これを知っておくと何が出来るかというと...

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@helper ForEach(IEnumerable<int> values, Func<object, HelperResult> template){
    var partialHtml = new System.Text.StringBuilder();
    foreach(var value in values){
        partialHtml.Append( template(value));
    }

    Write(MvcHtmlString.Create(partialHtml.ToString()));
}
<div>
<ul>
@ForEach(Enumerable.Range(1,5),
    @<li>@item</li>
)
</ul>
</div>

↑こんなコード書けるってことですよ。ヘルパーにRazor構文のテンプレートを渡してるんですよ。いいでしょ?ん~、もんどりうつ。

追記:もっと簡単でよかった。

@helper ForEach(IEnumerable<int> items, Func<object, HelperResult> template){
    foreach(var item in items){
        Write(template(item));
    }
}

@helperだとGenericメソッドに出来なさそうなので、もうちょっと調べてみたら、普通に@functionsで実装するサンプルをAndrew君が...。

VibrantCode - Inside Razor - Part 3 – Templates

この場合は↓こうね。

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
@functions{
    static IHtmlString ForEachGeneric<T>(IEnumerable<T> items, Func<T, HelperResult> template)
    {
        var sb = new System.Text.StringBuilder();
        foreach(var item in items){
            sb.Append(template(item));
        }
        
        return MvcHtmlString.Create(sb.ToString());
    }
}

<div>
<ul>
@(ForEachGeneric<int>(Enumerable.Range(1,5),
    @<li>@item</li>
))
</ul>
</div>

 

foreach2

小っちゃ!
クリックして拡大してみてね。

Razorのコンパイル結果を見てみると分かるけど、ヘルパーに渡すテンプレートブロックにはobject型のitemが必ずわたりまする。