コードでcshtmlのタグを出力すると、asp-for が動作しない - ASP.NET Core

コードでcshtmlのタグを出力すると、asp-for が無効になる現象を紹介します。

正しく動作する例

コード

下記のRazorPagesを作成します。
StaticBindTextBox.cshtml
@page "/StaticBindTextBox"
@model DynamicGenerateElement.Pages.StaticBindTextBoxModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
<head>

</head>
<body>
  <h2>テスト動作確認用</h2>
  <form method="post">
    <input type="text" asp-for="TextBoxString" /><br />
    <input type="submit" value="button1" />
  </form>
</body>
</html>
StaticBindTextBox.cshtml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace DynamicGenerateElement.Pages
{
  public class StaticBindTextBoxModel : PageModel
  {
    [BindProperty]
    public string TextBoxString { get; set; }

    public void OnGet()
    {
    }

    public IActionResult OnPost()
    {
      return RedirectToPage("/StaticBindTextBoxResult", new { text = TextBoxString });
    }
  }
}
StaticBindTextBoxResult.cshtml
@page "/StaticBindTextBoxResult/{text?}"
@model DynamicGenerateElement.Pages.StaticBindTextBoxResultModel
@{
}
<html>
<head>

</head>
<body>
  <h2>テスト動作結果</h2>
  <div>Name : @Model.TextString</div>
</body>
</html>
StaticBindTextBoxResult.cshtml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace DynamicGenerateElement.Pages
{
    public class StaticBindTextBoxResultModel : PageModel
    {
    public string TextString;

    public void OnGet(string text = "")
    {
      TextString = text;
    }
  }
}
Statup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace DynamicGenerateElement
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddRazorPages();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapRazorPages();
      });
    }
  }
}

解説

StaticBindTextBox ページでテキストボックスに文字列を入力し、ボタンをクリックすると、URLのパラメーターにテキストボックスに入力した文字を設定し、StaticBindTextBoxResult ページにリダイレクトします。Razor Pagesでテキストボックスの内容を取得する方法の詳細はこちらの記事を参照してください。
StaticBindTextBoxResult ページでは、URLのパラメーターの文字列を取得しページ内に表示します。

実行結果

上記のプロジェクトを実行します。(アプリケーションルート)/StaticBindTextBox のURLにアクセスします。下図のページが表示されます。


テキストボックスに文字列を入力します。入力後[button1]ボタンをクリックします。


(アプリケーションルート)/StaticBindTextBoxResult/(テキストボックスの値) のURLにリダイレクトします。ページにテキストボックスに入力した 文字列が表示されます。

現象の確認

以下のRazorPagesを作成します。

コード

DynamicGenerateBindTextBoxNC.cshtml
@page
@model DynamicGenerateElement.Pages.DynamicGenerateBindTextBoxNC
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
<head>

</head>
<body>
  <h2>動的出力のデモ</h2>
  <form method="post">
    @Html.Raw(Model.OutputHTML)
    <input type="submit" value="button1" />
  </form>
</body>
</html>
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace DynamicGenerateElement.Pages
{
  public class DynamicGenerateBindTextBoxNC : PageModel
  {
    public string OutputHTML { get; set; }

    [BindProperty]
    public string TextBoxString { get; set; }

    public void OnGet()
    {
      OutputHTML = "<input type=\"text\" asp-for=\"TextBoxString\" /><br />";
    }

    public IActionResult OnPost()
    {
      return RedirectToPage("/StaticBindTextBoxResult", new { text = TextBoxString });
    }
  }
}

解説

Razor Pagesにアクセスしたタイミングで、Razor Pagesのテキストボックスのタグ <input type="text" asp-for="TextBoxString" /><br /> をページに出力し、動的にページにテキストボックスを生成しています。

実行結果

上記のプロジェクトを実行します。(アプリケーションルート)/DynamicGenerateBindTextBoxNC のURLにアクセスします。下図のページが表示されます。


テキストボックスに文字列を入力します。入力後[button1]ボタンをクリックします。


URLはリダイレクトされ、(アプリケーションルート)/StaticBindTextBoxResult ページが表示されますが、テキストボックスに入力した文字列はパラメータとして渡されず、ページにもテキストボックスの内容は表示されません。

HTMLの比較

正常に動作した場合と、OnGetでページにタグを出力した場合のHTMLを比較します。
Razor Pages にタグを記述した場合(正常に動作する場合)
<html>
<head>
</head>
<body>
  <h2>テスト動作確認用</h2>
  <form method="post">
    <input type="text" id="TextBoxString" name="TextBoxString" value="" /><br />
    <input type="submit" value="button1" />
  <input name="__RequestVerificationToken" type="hidden" value="CfDJ8H4psHKzqwFOtAVrjxx7Q_ysMf8MPORkKqjhb0fXnq6cknVQVliaLdoUa7t_XJWZC-JUDMVyx7Wis-sZurgMXjBMdkE3cxfJ5bhHPsZ929oeuuX8JIue7DtuuIEz1eFpSLy-Zc1aHPHQO02o4qrbVfs" /></form>
</body>
</html>
OnGetでタグを出力した場合(動作しない場合)
<html>
<head>
</head>
<body>
  <h2>動的出力のデモ</h2>
  <form method="post">
    <input type="text" asp-for="TextBoxString" /><br />
    <input type="submit" value="button1" />
  <input name="__RequestVerificationToken" type="hidden" value="CfDJ8H4psHKzqwFOtAVrjxx7Q_xR896YDQF5KiDZ6OZ3ZG-mai_HvF6eH1-GtpZTex12JxpALQMtwUQzoYpxzKzRoCdKj2XqpOpMDVMPQz3vn0o0vXhz6A7gzaZAf0eW7owsqTMNgIV73wsbNTtv_g78uf0" /></form>
</body>
</html>
正しく動作する場合は、hidden フィールドが設定されており、asp-for タグが id, name タグに置き換わっていることが確認できます。

対処法

動的にRazor Pagesのタグを出力する場合は、あらかじめページにタグを記述しておき、 Razor Page側のロジックでタグを書き出さないなどの制御を実装することで、 動的にページの要素の出しわけができます。

下記のRazorPagesを作成します。リダイレクト先のStaticBindTextBoxResult ページは先のコードのRazorPagesをそのまま利用します。
DynamicGenerateBindTextBox.cshtml
@page  "{param:bool?}"
@model DynamicGenerateElement.Pages.DynamicGenerateBindTextBoxModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
}
<html>
<head>

</head>
<body>
  <h2>動的出力のデモ</h2>
  <form method="post">
    @if (Model.ShowTextBox == true) {
      <input type="text" asp-for="@Model.TextBoxString" /><br />
    }
    <input type="submit" value="button1" />
  </form>
</body>
</html>
DynamicGenerateBindTextBox.cshtml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace DynamicGenerateElement.Pages
{
  public class DynamicGenerateBindTextBoxModel : PageModel
  {
    public bool ShowTextBox { get; set; }

    [BindProperty]
    public string TextBoxString { get; set; }

    public string target { get; set; }

    public void OnGet(bool param)
    {
      if (param == true) {
        ShowTextBox = true;
      }
      else {
        ShowTextBox = false;
      }
    }

    public IActionResult OnPost()
    {
      return RedirectToPage("/StaticBindTextBoxResult", new { text = TextBoxString });
    }
  }
}

解説

@page "{param:bool?}" の記述によりこのRazor Pageでは、boolのパラメーターを受け取る設定とします。 末尾の? によりパラメータが記述されていない場合もページの表示をします。

OnGetメソッド内で取得したパラメータの値を確認し、trueであれば、ShowTextBox 変数の値を true に設定します。
    public void OnGet(bool param)
    {
      if (param == true) {
        ShowTextBox = true;
      }
      else {
        ShowTextBox = false;
      }
    }

Razor Pageの下記のコードでモデルクラスのShowTextBox の値を確認し、trueの場合にif文内を実行し、テキストボックスを画面に表示します。
    @if (Model.ShowTextBox == true) {
      <input type="text" asp-for="@Model.TextBoxString" /><br />
    }

実行結果

上記のプロジェクトを実行し、Webブラウザで (アプリケーションルート)/DynamicGenerateBindTextBox のURLにアクセスします。下図のページが表示されます。 パラメーターが無いためテキストボックスが非表示になっています。


URLを(アプリケーションルート)/DynamicGenerateBindTextBox/true に変更してアクセスします。下図のページが表示されます。パラメーターにtrueが設定されているため、 テキストボックスがページに表示されます。


URLが(アプリケーションルート)/DynamicGenerateBindTextBox/false の場合はテキストボックスは表示されません。


また、URLが(アプリケーションルート)/DynamicGenerateBindTextBox/(bool値でない文字列)の場合は、404エラーとなります。


テキストが表示された状態でテキストボックスに文字列を入力し、[button1]をクリックするとリダイレクトが実行され、StaticBindTextBoxResult ページが表示され、 テキストボックスに入力した文字列がページに表示されます。


著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2021-10-28
作成日: 2021-02-13
iPentec all rights reserverd.