😀

【ASP .NET】Todoリスト風の画面を作りながらrazor構文の基礎を確認する

2024/01/13に公開

Razor構文とは

ASP .NETやASP .NET Coreで使われるマークアップ構文です。
htmlとC#(あるいはVB)を織り交ぜたような記述が可能で、razorエンジンによってhtmlにコンパイルされます。主にcshtmlというファイルに記述され、基本はhtmlの文法に従い、C#の記述を用いたい場合にはrazor構文によってC#のコードブロックを作ります。

本記事の環境は、ASP .NET MVC 5です。
(Core じゃないですごめんなさい)

Razor構文入門

razor構文は@で始めることができて、ざっくりと以下のような使用イメージです。

  • @値を埋め込む
<p>@Model.var</p>
  • @()式を埋め込む
<p>@(Model.var1 + Model.var2)</p>
  • @{}複数行にわたる記述
<p>@Model.var1</p>
<p>@Model.var2</p>
  • @<キーワード>各種ディレクティブ
キーワードによっていろいろな動作をさせられる

次項からは、簡易なTodoリストの初期表示を通して、各種ディレクティブを確認していきます。

(※ @パターンは、実際には暗黙的に解釈させるだけなので、コンパイラから見てコードブロックの範囲が明らかなら式も書けます。)

@model ディレクティブ

ASP .NET MVCではViewはControllerからModelを一つ受け取ります。その際に、どのModelクラスを受け取るのか宣言するための構文です。
まず、TodoListを返すだけのModelを用意して、Controllerでそのまま渡してみます。

Model

    public class TodoListModel
    {
        public readonly TodoTask todoTask = new TodoTask( "taskA", true , new DateTime(2023, 12, 16)) ;
    }
    public class TodoTask
    {
        // タスク名
        public string title;
        // 完了したかどうか
        public bool isComplete;
        // 完了日
        public DateTime? completeDate;

        public TodoTask(string title, bool isComplete, DateTime? completeDate = null)
        {
            this.title = title;
            this.isComplete = isComplete;
            this.completeDate = completeDate;
        }
    }

Controller

    public class TodoListController : Controller
    {
        // GET: TodoList
        public ActionResult Index()
        {
            // TodoリストのModelを返す
            return View(new TodoListModel());
        }
    }

そしてViewで@model 構文を用いると、受け取ったインスタンスをそのまま参照できます。

@model WebApplication1.Models.TodoListModel

<body>
    <h2>Todo List</h2>

    <ul>
        <li>
            <strong>@Model.todoTask.title</strong> -
            <span>Completed (@Model.todoTask.completeDate.Value.ToShortDateString())</span>
        </li>
    </ul>
</body>

こんな感じ
image.png

開発者ツールから実際のhtmlを見てみると、モデルのプロパティがrazorエンジンによって実際の値で埋め込まれていることがわかります。

            <strong>taskA</strong> -
            <span>Completed (2023/12/16)</span>

@helper ディレクティブ

HTMLのコードを再利用可能にするための仕組みです。モダンなフレームワークで見かけるコンポーネントのようなリッチな概念ではありません。純粋なHTMLの断片を切り出す仕組みです。
引数を受け取ることができますが、サーバサイドでレンダリングされるので値は静的にバインドされます。

例えば先ほどのViewの場合は、li要素をこんな風に切り出して再利用できます。

<body>
    <h2>Todo List</h2>

    @using WebApplication1.Models
    @helper DisplayTask(TodoTask todotask){
        <li>
            <strong>@todotask.title</strong> -
            <span>Completed (@todotask.completeDate.Value.ToShortDateString())</span>
        </li>
    }

    <ul>
        // li要素が展開される
        @DisplayTask(Model.todoTask)
    </ul>
</body>

@for ディレクティブ

繰り返しを記述するrazor構文です。
これまでの例では、そもそもModel側にタスクが一つしかなかったので、まずこれをリストにします。

Model

    public class TodoListModel
    {
        public readonly List<TodoTask> taskList = new List<TodoTask>
            {
                new TodoTask( "taskA", true , new DateTime(2023, 12, 16)),
                new TodoTask( "taskB", true , new DateTime(2023, 12, 17)),
            };
    }

こうするとViewは以下のように記載できます。for文自体は普段のC#と同じ文法です。ここではforeachを使ってますがforもあります。

View

    <ul>
        @foreach (var task in Model.taskList)
        {
            @DisplayTask(task)
        }
    </ul>

先ほど@usingディレクティブで、DisplayTask()として切り出したli要素を繰り返しています。htmlのべた書きや素のjsのDOM操作よりは簡潔に見通し良く書けますね。

こんな感じ
image.png

@if

もちろん条件分岐もあります。Todoリストですから、未完のタスクにも対応できるようにします。
まず、Modelにcompleteじゃないタスクを追加しましょう。

Model

    public class TodoListModel
    {
        public readonly List<TodoTask> taskList = new List<TodoTask>
            {
                new TodoTask( "taskA", true , new DateTime(2023, 12, 16)),
                new TodoTask( "taskB", true , new DateTime(2023, 12, 17)),
                // 未完了なタスクを追加
                new TodoTask( "taskC", false ),
                new TodoTask( "taskD", false ),
                new TodoTask( "taskE", false )
            };
    }

次にhelperクラスを、isCompleteの状態に応じて分岐させます。
View

<body>
    <h2>Todo List</h2>


    @using WebApplication1.Models
    @helper DisplayTask(TodoTask todotask){
        <li>
            <strong>@todotask.title</strong> -
            // 分岐を追加
            @if (todotask.isComplete)
            {
                <span>Completed (todotask.completeDate.Value.ToShortDateString())</span>
            }
            else
            {
                <span>Pending</span>
            }
        </li>
    }

    <ul>
        @foreach (var task in Model.taskList)
        {
            @DisplayTask(task)
        }
    </ul>
</body>

こんな感じ
image.png

その他

razor構文の紹介をしてきましたが、これらはあくまで、htmlの世界とC#の世界を行き来するための構文です。
C#のブロックに入れば、LINQなども自由に使えます。
最後に、LINQを用いてリストを完了済みのものと、未完了なもので分別してみます。

    <h3>Complet Task</h3>
    <ul>
        // 完了済みのアイテムだけ抽出して並べる
        @foreach (var task in Model.taskList.Where(task => task.isComplete))
        {
            @DisplayTask(task)
        }
    </ul>

    <h3>Incomplete Task</h3>
    <ul>
        // 未完了のアイテムだけ抽出して並べる
        @foreach (var task in Model.taskList.Where(task => !task.isComplete))
        {
            @DisplayTask(task)
        }
    </ul>

image.png

最後に

以上、razor構文の基本でした。
まだ学び始めたばかりで知らないことが多いですが、使っていて楽しいです。razor自体はそこそこ年季の入った技術ですが、今も十分にパワフルで便利だと思います。ドキュメントも充実してますし、やっぱり.NETは良いですね。

ちなみに、本記事の環境であるASP.NET MVC 5ですが、.NET Framework系のフレームワークなので、機能追加などは基本的にないです(多分)。
新規に自由な環境で学習する場合は、.NET Core系のASP.NET Core MVCがおすすめです。razor構文のページを少し見るだけでも、便利なものがいろいろ追加されてそうです。
https://learn.microsoft.com/ja-jp/aspnet/core/mvc/views/razor

GitHubで編集を提案

Discussion