WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

Validator の Display="Dynamic" 時の注意点

by WebSurfer 2014年1月16日 17:45

ASP.NET の検証コントロール(RequiredFieldValidator, RegularExpressionValidator など)を使用して、Display プロパティを Dynamic に設定したときの注意点です。

Validator の Display="Dynamic" 時の注意点

この件は stackoverflow の記事 でも報告されています。ただ、実は自分はつい最近までこの問題は知らなかったです。(汗)

例えば、以下のように RequiredFieldValidator を配置して、その直後に Button を配置したとします。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  protected void Button1_Click(object sender, EventArgs e)
  {
    if (Page.IsValid)
    {
      // 何らかの処置。
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" 
      runat="server" 
      ErrorMessage="エラーメッセージ"  
      ControlToValidate="TextBox1" 
      Display="Dynamic">
    </asp:RequiredFieldValidator>
    <asp:Button ID="Button1" 
      runat="server" 
      Text="Button" 
      OnClick="Button1_Click" />                
  </div>
  </form>
</body>
</html>

上記のコードでは以下の手順で問題を再現できます。

  1. TextBox は空のまま Button をクリックする。
  2. クライアント側で検証がかかり、ErrorMessage プロパティに設定した「エラーメッセージ」が表示される。
  3. TextBox に文字を入力して Button をクリックする。 ⇒ 「エラーメッセージ」は消えるがポストバックがかからない。
  4. 再度 Button をクリックする。 ⇒ ポストバックがかかる。

期待される動きは、上記 3 で「エラーメッセージ」が消えるとともにポストバックがかかるということのはずですが、そうはなりません。

上記のコードにおける解決策は、Button の直前に改行 ( <br /> ) を入れることです。そうしないとうまく行かない理由は以下の通りです。

Validator のエラーメッセージは html コードでは span 要素となり、JavaScript による検証結果により表示/非表示を切り替えています。

Display プロパティが Dynamic に設定されている場合は、当該 span 要素の style 属性を "display:none;" または "display:inline;" に設定することにより表示/非常時を切り替えます。

検証対象の TextBox にフォーカスを当ててからフォーカスを外す(例えば、TextBox に入力してから form を submit するために Button をクリックする)と、 そのタイミングで JavaScript による検証がかかるようになっています。

検証結果によって "display:inline;" が "display:none;" に(またはその逆に)書き換えられるので、エラーメッセージの部分のページレイアウトが変わることになります。

従って、上記のコードのように、RequiredFieldValidator の直後に Button が配置されているような場合、エラーメッセージが表示/非表示になる分だけ画面上でボタンが左右に移動します。

ボタンが移動すると、<input type="submit" ... /> タイプのボタンをクリックしたにもかかわらず form が submit されません。 即ち、ポストバックされないという期待に反する動作になります。

なお Button が動くのは左右でなくても、例えばエラーメッセージを p 要素に入れると上下に移動しますが、その場合でも同じくform は submit されません。

このことは、ASP.NET の検証コントロールを使った場合に限った話ではなく、html 要素と JavaScript だけでも再現できます。 以下のサンプルコードは、html 要素と JavaScript だけでこの問題(ボタンが動くと submit されない)を再現する例です。

サンプルコードを実際に動かして試すことができるよう 実験室 にアップしました。興味のある方は試してみてください。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>WebSurfer's Page - 実験室</title>
  <script src="/scripts/jquery-1.8.3.min.js" type="text/javascript">
  </script>
  <script type="text/javascript">
  //<![CDATA[
    function toggleDisplay() {
      var validator = $('#RequiredFieldValidator1');
      if (validator.css("display") == "none") {
        validator.css("display", "inline");
      } else {
        validator.css("display", "none");
      }            
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server" 
    onsubmit="javascript:return confirm('Submit しますか?');">

  <div>
    <input name="textbox1" type="text" id="1extbox1" 
      onblur="javascript: toggleDisplay();" />
    <span id="RequiredFieldValidator1" 
      style="display:none;">エラーメッセージ</span>
    <input type="submit" name="button1" value="POST" 
      id="button1" />    
  </div>
  </form>
</body>
</html>

ちなみに、Display プロパティが Static に設定されている場合は、style 属性を "visibility:hidden;" または "visibility:visible" に設定するので、 表示/非表示を切り替えてもページのレイアウトは変わりません。結果、ボタンは動かないのでこの問題は起こりません。

Tags: ,

Validation

CustomValidator と RequiredFieldValidator

by WebSurfer 2012年7月22日 12:21

CustomValidatorRequiredFieldValidator について新発見(?)がありましたので、忘れないように書いておきます。

(1) CustomValidator

RequiredFieldValidator 以外では、未入力に対する検証結果は OK 扱いになると思っていましたが、CustomValidator の場合は例外がありました。

それは、.NET 2.0 で追加された ValidateEmptyText プロパティ を true にすることです。

ValidateEmptyText プロパティがデフォルトの false の場合は、RegularExpressionValidator や RangeValidator などと同様に、未入力の場合はその値を評価することなく、検証結果は OK 扱いになります。

ValidateEmptyText プロパティを true にすることにより、検証対象コントロールが未入力でも、CustomValidator は、プログラマが設定した検証ロジックを使用して検証対象のコントロールの値を評価し、検証結果を返します。

では、CustomValidator の ValidateEmptyText プロパティを true にして空白の検証を可能にし、RequiredFieldValidator を使わないで済ませられるかと言えば、機能的にはそれで問題なくても、ユーザビリティの面では問題がありそうです。

検証コントロールは、ユーザーに適切なエラーメッセージを提供するということも重要な目的の一つだそうです。検証条件が異なれば適切なエラーメッセージは異なるはずです。

検証コントロールのエラーメッセージは固定的なので、CustomValidator 一つで済ませる場合、エラーメッセージは "未入力もしくは入力形式がXXXと異なります" とせざるを得ません。

それより、RequiredFieldValidator と CustomValidator を併用して、未入力の時は "未入力です" というエラーメッセージを、入力はあるが形式が違っている場合は "入力形式がXXXと異なります" とした方がユーザーフレンドリーだと思います。

というわけで、結局、未入力の検証には、RequiredFieldValidator を使用するのが正解のようです。

なお、上記で言う「未入力」とは String.Empty だけではなくて、半角/全角スペースも含まれますので注意してください。実は、これも今回調べるまで知らなかったです。(笑)

あともうひとつ。CustomValidator がデフォルトで未入力でも検証 OK とするのには理由があります。それは、RequiredFieldValidator と併用した場合、未入力の際に 2 つのエラーメッセージが同時に表示されるのはユーザーにとって煩わしいからだそうです。

(2) RequiredFieldValidator

RequiredFieldValidator は「未入力」をチェックするものだと思っていましたが、正確にはそうではなかったです。

MSDN ライブラリに書いてあったのですが、実は、"入力コントロールに対する検証は、そのコントロールがフォーカスを失ったときに、その値が InitialValue プロパティ の値から変更されていない場合は失敗になります。" ということだそうです。

従って、MSDN ライブラリのサンプルコードにあるように、検証対象の TextBox の Text プロパティに "Enter a value" と設定しておき、RequiredFieldValidator の InitialValue プロパティにも同じ文字列 "Enter a value" を設定しておけば、ユーザーが初期値を変更しなかった場合に検証 NG とします。

InitialValue プロパティのデフォルト値は String.Empty、TextBox の Text プロパティもデフォルトで String.Empty なので、結果的に「未入力」の検証が可能になるということです。

なお、全角/半角スペースを入力しても検証 NG となるのは、MSDN ライブラリにも書いてありますように、"InitialValue プロパティと入力コントロールの両方の文字列は、検証が実行される前にその文字列の前後の余分な空白が削除されてトリムされます。" からです。

Tags:

Validation

Validator の結果で背景を変更

by WebSurfer 2011年7月18日 21:49

RequiredFieldValidator や RegularExpressionValidator の検証結果が無効の場合、対象の TextBox の BackgroundColor を赤などの目立つ色にしてユーザーの注意をひくというサンプルです。

検証結果が無効の場合、対象 TextBox の背景を赤に変更

ヒントは Change Background Color of Invalid Controls (ASP.NET Validator) というページに紹介されていたコードですが、これは TextBox に対し、Validator が一つだけならうまくいきますが、二つあるとうまくいきません。

例えば RequiredFieldValidator と RegularExpressionValidator を組み合わせて使った場合、何も入力しない場合は後者の isvalid は true になって ctrl.style.backgroundColor ="" で上書きされてしまい、TextBox の背景は赤くなりません。

以下に、その問題に対処したサンプルをアップしておきます。

ただし、自分は JavaScript 使いでも jQuery 使いでもないので(と言って、C# 使いでもないですが(汗))、スクリプトの書き方が変かもしれません。でも、一応期待通りに動くことは検証しました。

以下のコードは、実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。この記事の下の方の「2011/7/20 追記」に書いた ErrorMessage と同期を取るためのコードは追加済みです。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  protected void Page_Load(object sender, EventArgs e)
  {
    String csname = "OnSubmitScript";
    Type cstype = this.GetType();
    ClientScriptManager cs = Page.ClientScript;
    if (!cs.IsOnSubmitStatementRegistered(cstype, csname))
    {
      String cstext = "ChangeBackgroundColor();";
      cs.RegisterOnSubmitStatement(cstype, csname, cstext);
    }
  }

  protected void CustomValidator1_ServerValidate(
    object source, ServerValidateEventArgs args)
  {        
    string membership = args.Value.ToLower();

    if (membership == "gold" || membership == "silver")
    {
      args.IsValid = true;
    }
    else
    {
      args.IsValid = false;
    }      
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
  <script type="text/javascript">
  //<![CDATA[
    function CustomValidator1_ClientValidate(sender, args) {
      var membership = args.Value.toLowerCase();
      if (membership === "gold" || membership === "silver") {
        args.IsValid = true;
      } else {
        args.IsValid = false;
      }
    }

    function ChangeBackgroundColor() {
      var color = "#FFAAAA"

      var valid =
        $(Page_Validators).
        filter(function () { return this.isvalid == true; });

      var invalid =
        $(Page_Validators).
        filter(function () { return this.isvalid == false; });

      valid.each(function () {
        $("#" + $(this).get()[0].controltovalidate).
        css("backgroundColor", "");
      });

      invalid.each(function () {
        $("#" + $(this).get()[0].controltovalidate).
        css("backgroundColor", color);
      });
    }
  //]]>
  </script>
</head>
<body>
  <form id="form1" runat="server">
  <table>
    <tr>
      <td>
        User Name
      </td>
      <td>
        <asp:TextBox ID="username" runat="server">
        </asp:TextBox>
      </td>
      <td>
        <asp:RequiredFieldValidator 
          ID="RequiredFieldValidator1" 
          runat="server" 
          ErrorMessage="ユーザー名は必須入力です。" 
          ControlToValidate="username" 
          ForeColor="Red" 
          Display="Dynamic">
        </asp:RequiredFieldValidator>
        <asp:RegularExpressionValidator 
          ID="RegularExpressionValidator1" 
          runat="server" 
          ErrorMessage=
            "半角アルファベットで 40 文字以内にしてください。" 
          ControlToValidate="username" 
          ForeColor="Red" 
          ValidationExpression="^[a-zA-Z''-'\s]{1,40}$" 
          Display="Dynamic">
        </asp:RegularExpressionValidator>
      </td>
    </tr>
    <tr>
      <td>
        Email
      </td>
      <td>
        <asp:TextBox ID="email" runat="server">
        </asp:TextBox>
      </td>
      <td>
        <asp:RequiredFieldValidator 
          ID="RequiredFieldValidator2" 
          runat="server" 
          ErrorMessage="メールアドレスは必須入力です。" 
          ControlToValidate="email" 
          ForeColor="Red" 
          Display="Dynamic">
        </asp:RequiredFieldValidator>
        <asp:RegularExpressionValidator 
          ID="RegularExpressionValidator2" 
          runat="server" 
          ErrorMessage=
            "メールアドレスの形式が正しくありません。" 
          ControlToValidate="email"
          ForeColor="Red" 
          ValidationExpression=
            "\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" 
          Display="Dynamic">
        </asp:RegularExpressionValidator>
      </td>
    </tr>
    <tr>
      <td>
        Membership
      </td>
      <td>
        <asp:TextBox ID="membership" runat="server">
        </asp:TextBox>
      </td>
      <td>
        <asp:RequiredFieldValidator 
          ID="RequiredFieldValidator3" 
          runat="server" 
          ErrorMessage="メンバーシップは必須入力です。" 
          ControlToValidate="membership" 
          ForeColor="Red" 
          Display="Dynamic">
        </asp:RequiredFieldValidator>
        <asp:CustomValidator 
          ID="CustomValidator1" 
          runat="server" 
          ErrorMessage=
            "Gold または Silver でなければなりません。" 
          ControlToValidate="membership"
          ForeColor="Red" 
          OnServerValidate=
            "CustomValidator1_ServerValidate" 
          Display="Dynamic" 
          ClientValidationFunction=
            "CustomValidator1_ClientValidate" >
        </asp:CustomValidator>
      </td>
    </tr>
  </table>
  <asp:Button ID="Button1" runat="server" Text="送信" />
  </form>
</body>
</html>

------------ 2011/7/20 追記 ------------

クライアント側での検証は、form が submit された時だけでなく、TextBox からフォーカスが外れた時も自動的にかかるようで、上記のコードでは ErrorMessage が消えても TextBox の背景は赤いままになってしまいます。

Validator の ErrorMessage と TextBox の背景の同期を取るには、TextBox の onblur 属性にも ChangeBackgroundColor() を設定します。Page_Load ハンドラで以下のようなコードを追加してやるとうまくいきます。

username.Attributes.Add("onblur", 
    "javascript:ChangeBackgroundColor()");
email.Attributes.Add("onblur", 
    "javascript:ChangeBackgroundColor()");
membership.Attributes.Add("onblur", 
    "javascript:ChangeBackgroundColor()");

------------ 2011/7/21 追記 ------------

IE の場合、フォーカスを外しても自動的に検証がかからないケースがあります。Firefox と Chrome でも試しましたが、こちらは期待通り検証がかかります。

具体的には以下のようなケースです。

最初に、空白または無効な文字列を TextBox に入力し、Tab キーでフォーカスを外すとクライアント側で検証がかかって ErrorMessage が表示され、onblur イベントが発生して ChangeBackgroundColor() メソッドが働き TextBox の背景色が赤になります。

次に、再度その TextBox に入力する際、オートコンプリートの一覧からマウスもしくはキー操作で文字列を選んで TextBox に表示し、その状態から Tab キーでフォーカスを外すと、今度は検証はかかりません。従い、有効な文字列なのにエラーとなったままになります。onblur イベントは発生して ChangeBackgroundColor() メソッドが働きますが、検証がかかってないので(即ち、検証結果が NG のままなので)TextBox の背景色は赤のままとなります。

ちなみに、オートコンプリートは一切使わず、有効な文字列を手打ちで TextBox に入力した場合は、フォーカスを外すと期待通り検証がかかります。従い、ErrorMessage は消えて TextBox の背景色も元に戻ります。

動作としては気に入らないですが、どうも IE のバグ仕様(?)らしいということで未対応です。

後で調べて分かったことですが、MSDN ライブラリの Walkthrough: Validating User Input in a Web Forms Page の Note に以下の文がありました。

"If you are using the auto-complete option in Internet Explorer, selecting a value from the auto-complete list will fill a value into the text box, but the client-side validator will not run."

Tags:

Validation

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  2017年3月  >>
2627281234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar