プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2018/07/03

Razor 内で JavaScript のコードを出力する

event_note2018/07/03 0:32

Razor 構文内で JavaScript のコードを出力したい場合に結構はまったので、まとめてみました。

環境

  • Visual Studio 2017
  • ASP.NET Core 2.1

Razor 内でのコンテンツ出力の基本

通常、Razor 内に記述されたコードは、HTML タグなどをもとにそれがコンテンツなのかどうか判別されるようです。
しかし、場合によっては自動で判別されないことがあります。

例えば、以下のコードでは、for 文の外の test<br /> はコンテンツ部分だと正しく判別されてそのまま出力されますが、for 文内の test<br /> の部分ではエラーとなります。

エラーとなる例

<p>
    test<br />
    @for (int i = 0; i < 5; i++)
    {
        test<br />
    }
</p>

この場合、@: または <text> で、コンテンツであることを明示する必要があります。

正しいコード例

<p>
    test<br />
    @for (int i = 0; i < 5; i++)
    {
        @:test<br />
    }
</p>

または

<p>
    test<br />
    @for (int i = 0; i < 5; i++)
    {
        <text>test<br /></text>
    }
</p>

出力結果

<p>
    test<br />
        test<br />
        test<br />
        test<br />
        test<br />
        test<br />
</p>

<text> の文字は出力されません。

JavaScript コードの出力

以上を踏まえた上で、Razor 内で JavaScript のコードを動的に出力したい場合、以下のように記述します。

コード例

@{
    var items = new string[] { "abc", null, "def" };
}

<script>
var array = [];

@foreach (var item in items)
{
    @:array.push('@item');
}

$(function () {
    console.log(array);
});
</script>

出力結果

<script>
var array = [];

    array.push('abc');
    array.push('');
    array.push('def');

$(function () {
    console.log(array);
});
</script>

foreach 内で JavaScript のコードを出力する場合には @: (または <text>) が必要です。
また、その中で変数を参照したい場合は @ を指定します。

出力内容を動的に変えたい場合

個人的にこれでかなりはまりました。

例えば、上述のコードをもとに、出力を以下のように変更したい場合、item に対する条件判定が必要になります。
(itemnull の場合は、null を出力したい)

得たい出力

<script>
var array = [];

        array.push('abc');
        array.push(null);
        array.push('def');

$(function () {
    console.log(array);
});
</script>

愚直に if 文を使えば以下のように書けます。

if 文を使った例

@{
    var items = new string[] { "abc", null, "def" };
}

<script>
var array = [];

@foreach (var item in items)
{
    if(item == null) {
        @:array.push(null);
    }
    else {
        @:array.push('@item');
    }
}

$(function () {
    console.log(array);
});
</script>

しかし、こういう Null 判定はワンライナーでシンプルに書きたいものです。
とりあえずいろいろ試行錯誤した結果、以下のように書けば目的の出力が得られました。

三項演算子を使った例

@{
    var items = new string[] { "abc", null, "def" };
}

<script>
var array = [];

@foreach (var item in items)
{
    @:array.push(@Html.Raw(item != null ? $"'{item}'" : "null"));
}

$(function () {
    console.log(array);
});
</script>

思ったほどシンプルになりませんでしたが、私の頭ではこれが限界でした。
他にもっとシンプルな書き方があれば教えて頂きたいです。

上記の要点(というか悩んだ部分)は以下です。

  • Razor が出力するのは文字なので、null も文字列として "null" と書けばいい
  • 同様に変数の中身も文字列として $"'{item}'" と書けばいい
  • しかし、単純に $"'{item}'" と書くと、シングルクォートがエンコードされてしまう
  • Html.Raw でエンコードを無効化する必要がある