Web Formsで動的に要素の「表示・非表示」を切り替える

こんにちは、エンジニアの糸賀です。

突然ですが、皆さんはWebアプリケーションを開発していてこんな場面に出くわすことはありませんか?
フロントエンドの文字列をサーバー処理の変数値によって表示・非表示を動的に切り替えたいときに、

  • “さて、いくつか方法があるけど今回はどうやって実装しようか。”
  • “というか、どれも変わらんだろ。”
  • “既存のコードはこの方法を採用しているけど、本当にこれでいいのか?”
  • “今回のケースだと、どの方法が一番いいのか迷うな。”

など。私は3万回くらいあります。

そこでこの記事では、ASP.NET WebFormsにおいて、aspxやascxなどのデザインファイル内の要素を動的に表示したり非表示にしたりする方法をケース別に紹介します。
これは私が実際に頭を抱えた問題で、実際に調べるまでに至ったのでその調査結果を皆さんに共有したいと思います。

調査対象のフレームワーク: Microsoft .NET Framework 4.5
最終更新日: 2021/05/30

ケース1: クライアント側の処理で表示と非表示を切り替えたい場合


スタイル情報のDisplayプロパティの値を三項演算子によって決定します。表示切替をする対象の情報が誰に見られてもいい場合は、基本的にこの方法をおススメします。なぜなら、Javascriptを使ってクライアント側で表示を切り替えられるためです。ケース2やケース3で紹介している方法で情報を非表示にする場合、サーバーがクライアント側にHTMLファイルを渡す時点で非表示にした情報は完全に欠如しています。対してこの方法はCSSを使って非表示にしているだけなので、情報自体はクライアント側に渡されています。

書き方

<tag style="display: <%= (…) ? "block" : "none" %>">
	your code
</tag>

ex.

<p style="display: <%= (this.IsGuest) ? "block" : "none" %>">
	新規登録をしてください。
</p>

長所

* 柔軟性が高い

クライアント側の処理で柔軟に表示・非表示を切り替えられます。CSSのDisplay値を”block”ではなく、”inline”とすることもできます。

短所

* 可読性が落ちる

Visibleプロパティと比べると文字量が多くなる、かつ直感的な見た目をしていないので可読性が落ちます。

* 秘匿性が低い

ユーザーはブラウザの検証ツールを使えば、非表示にした内容を見ることができます。CSSで表示をコントロールしているにすぎないためです。

ケース2: 表示切替対象が少なく、非表示にした要素をクライアントに見せたくない場合


この方法が一番シンプルです。
また、要素を非表示としているときにクライアントはケース1と違ってその要素を秘匿できます。

書き方

HTML Control:

<tag Visible="…" runat="server">text<tag/>

Web Control:

<asp:controltag ID="…" Visible="…" Text="text" runat="server" />

また、コードビハインドでも表示と非表示の切り替えが可能です。

webControlId.Visible = false;

ex.

HTML Control:

<div Visible="<%# this.IsGeneralManager %>" runat="server">
	<dl>
		<dt>顧客電話番号1</dt>
		<dd>080-xxxx-xxxx</dd>
		<dt>顧客電話番号2</dt>
		<dd>090-xxxx-xxxx</dd>
					…
		<dt>顧客電話番号500</dt>
		<dd>03-xxxx-xxxx</dd>
	</dl>
<div/>

Web Control:

<asp:Literal ID="lAsset" Visible="<%# this.IsGeneralManager %>" Text="会社の総資産: n円" runat="server" />
lAsset.Visible = true;

長所

* 可読性が高い

Visibleの値を変えるだけ、かつ直感的な表現なので可読性が高いです。
また、コードビハインドで表示と非表示を切り替えれば、aspxファイル内の情報が減るので見た目がすっきりします。

* 秘匿性が高い

ケース1ではクライアント側での処理に対応できますが、言い換えると表示切替の対象であるデータは非表示でもクライアント側から丸見えです。
対してこの方法を使うと、Visibleの値がFalseの時はサーバーがクライアントへ返すHTMLデータの中に、対象の要素がそもそも出力されないため、条件に当てはまらない場合は完全にそれを隠せます。

短所

* 必要な処理を忘れるとうまく動作しない

コントロールの”Visible”値にコードビハインドのプロパティを使用している場合は、Postback時に表示切替を反映させるためにDatabind()メソッドの実行が必要です。別のWebコントロールによるPostbackで、表示切替をするコントロールの”Visible”を変更するとします。”Visible”値には<%# … %>が使われています。つまりデータバインディングをしないと、”Visible”値に使われるプロパティの値が変わっても表示が切り替わりません。
プロパティの値を更新した際は、Databind()を実行を忘れないでください。これ以上詳しく話すと、長くなってしまうのでここまでにします。

* 情報が意図せず隠れてしまう可能性がある

ケース2のメリットで、コードビハインドを通してWebコントロールの表示と非表示を切り替えるとaspxファイルの見た目がすっきりすると述べましたが、それがデメリットにもなり得ます。Webコントロールの”Visible”プロパティを書かずにコードビハインドのみで表示と非表示を切り替えると、aspx内のWebコントロールを見ただけだとそのコントロールが非表示になり得るのか、そうでないのかがわからなくなります。つまり、以下のような場合、常に表示してほしい情報が意図せず隠れてしまう可能性があります。

<div ID="dvControl" runat="server">
	<span>常に表示しておいてほしい情報</span>
<div/>
// 非表示にする
dvControl.Visible = false;

ケース3: 表示と非表示を切り替える対象が多い場合

方法は2つあります。
1つ目はembedded code blocks (<% … %>)を使います。If文を用いてその中に、要素を入れます。
2つ目はHtmlコントロール化した要素(状況によってはWebコントロール要素)の”Visible”値を利用します。

書き方 – 1つ目の方法

<% if(…){ %>
	your code
<% } %>

ex.

<% if (this.IsGeneralManager){ %>
	<p>トップシークレット情報</p></ br>
	<p>=== 社外秘 ===</p>
	<p>取締役会長の好きな食べ物はティラミス</p>
	<p>=== 社外秘 ===</p>
<% } %>

書き方 – 2つ目の方法

<htmltag ID="..." Visible="<% ... %>" runat="server">
	your code
</htmltag>
<tr ID="trCredential" Visible="<%# this.IsSecretary %>" runat="server">
	<td>
		トップシークレット情報</ br></ br>
		=== 社外秘 ===</ br>
		取締役会長の好きな食べ物はティラミス</ br>
		=== 社外秘 ===</ br>
		トップシークレット情報</ br>
	</td>
</tr>

長所

* 効率的

複数の要素をまとめて指定して、一度に表示と非表示を切り替えられます。

短所

* 可読性が落ちやすい

この方法でIf文を多用しなければ、他の2つの方法に比べて大して可読性は変わらないと思います。しかし、If文のネストが深くなると話は別で、可読性は落ちてしまいます。また、条件文が長いとさらに見にくくなります。

* その都度、値の代入が必要
(<% if(コードビハインドのプロパティ) {} %>を利用する場合)

条件文にコードビハインドのプロパティを当てはめて<% if() {} %>を使用する場合は注意が必要です。Postbackか否かにかかわらず、そのプロパティの値は保持されないので、ページに対してリクエストを送る度に値を代入することが求められます。一方でコントロールの値はPostback時であれば、保持されます。つまり、以下の”this.IsGeneralManager”の値は保持されないので、このプロパティをPostback時に再び利用する場合は、値を再度代入する必要があります。

<% if (this.IsGeneralManager){ %>
	<p>トップシークレット情報</p>
<% } %>
if (!Postback)
{
	this.IsGeneralManager= true;
}
else
{
	var isGeneralManager = this.IsGeneralManager; // false
	this.IsGeneralManager= true; // 代入が必要
	if (this.IsGenerealManager) 
	{
		...
	}
}

一方で理想的な実装方法かどうかはさておき、webコントロールのプロパティを使用すれば、プロパティに値を再度代入する必要はありません。

<tr ID="trCredential" Visible="<%# this.IsSecretary %>" runat="server">
	<td>
		トップシークレット情報
	</td>
</tr>
if (!Postback)
{
	trCredential.Visible = true;
}
else
{
	var isVisible = trCredential.Visible; // true
	if (isVisible) 
	{
		...
	}
}

どっちを使えばいいの?

“え、2つ方法のうちどっち使えばいいんだよ。”
という疑問をお持ちの方もいると思います。
その問いに対する答えは、コードビハインドで表示と非表示を自由に切り替えたい場合は2つ目の方法を採用した方が良いです。なぜなら2つ目の方法はコントロールを利用して表示と非表示を切り替えているので、処理フロー内の任意の場所でそのコントロールの”Visible”プロパティの値を更新すれば表示と非表示を切り替えられるためです。

ケース4: 表示と非表示を切り替える対象が複数あり、それぞれ表示する条件が対称の関係にある場合

書き方

<% if(true) { %>
	<p>true</p>
<% } else { %>
	<p>false</p>	
<% } %>

ex.

<% if(this.IsBeverage) { %>
	<h1>コーラ</h1>
	<h1>お茶</h1>
	<h1>水</h1>
	<h1>マウンテンデュー</h1>
<% } else { %>
	<h1>パン</h1>
	<h1>麺</h1>
<% } %>

長所

* 直感的に理解しやすい

表示、非表示にする情報が複数あり、それぞれ表示する条件が対称の関係にある場合には、この方法を使用した方が可読性が高くなるかもしれません。上記の例のような場合です。”this.IsBeverage”がTrueの時はコーラやお茶などが表示され、パンや麺は表示されません。対して”this.IsBeverage”がFalseの時はその逆がです。このようなケースでは、以下にあるようにコントロールのVisibleプロパティを利用するよりも直感的に見やすいコードが書けます。

<asp:Literal ID="lCoke" Visible="<%# this.IsBeverage %>" Text="コーラ" runat="server" />
<asp:Literal ID="lTea" Visible="<%# this.IsBeverage %>" Text="お茶" runat="server" />
<asp:Literal ID="lWater" Visible="<%# this.IsBeverage %>" Text="水" runat="server" />
<asp:Literal ID="lMountainDew" Visible="<%# this.IsBeverage %>" Text="マウンテンデュー" runat="server" />
<asp:Literal ID="lBread" Visible="<%# (this.IsBeverage == false) %>" Text="パン" runat="server" />
<asp:Literal ID="lNoodle" Visible="<%# (this.IsBeverage == false) %>" Text="麺" runat="server" />

短所

* 可読性が落ちやすい

これはケース3で紹介されている方法の短所と同様、ネストが深くなると一気に可読性が落ちやすくなります。

まとめ

私が8000時間の犠牲を払って得た調査結果が皆さんのお役に立てれば幸いです。

1. 表示・非表示切替の対象となる情報を秘匿する必要がなく、クライアント側の処理で表示・非表示を切り替えたい。
-> ケース1のようにCSSを使う。

2. 表示予定の情報をクライアント側から見えないように隠したい。
-> ケース2のようにコントロールの”Visible”を使う。

3. 表示・非表示を切り替える対象が多い。
-> ケース3のようにif文を使う。

4. 表示と非表示を切り替える対象が複数あり、それぞれ表示する条件が対称の関係にある場合。
-> ケース4のようにif-else文を使う。

関連記事

プロジェクトストーリー

技術

コメント

この記事へのコメントはありません。

カテゴリー

TOP
TOP