ASP.NET AJAX 4.0 中的条件呈现
Dino Esposito
客户端呈现是到目前为止所最激动人心,和查找在 ASP.NET AJAX 4 的长时间 awaited、 新功能。 客户端呈现允许您使用 HTML 模板来定义所需的布局和占位符的运行时数据提供基于文本的语法。 它所有类似服务器端数据绑定的不同之处在于它采用客户端浏览器内的位置与 Web 服务通过下载的外部数据。
最后一个月我所覆盖新 DataView 客户端控件和绑定技术将最常使用的基本的知识。 此文章中我转进一步一步和 $ 封面条件模板呈现。
条件模板呈现
在 ASP.NET AJAX 4,数据绑定为一个 HTML 模板是标记的一条可能包含 ASP.NET 标记、 HTML 文本和运行时数据的一些占位符。 呈现算法是相当简单:绑定到此类模板,DataView 控件获取一些数据,并使用的填满该模板。 生成标记与实际数据替代为占位符,然后显示代替原始 HTML 模板。
有两种方法可以在其中创建 DataView 控件的实例:以声明方式或编程方式。 但是,用于生成标记算法保持不变。 这是其中最后一个月已经文章中我们离开该问题。
继续学习问题 springs 自然。 如果某些逻辑是呈现模板所必需的? 如果您需要生成不同的标记基于不同的运行时条件的条件呈现吗? 某些客户端代码必须被 intertwined 带标记检查以及其他全局的 JavaScript 对象的状态被绑定的数据项的值。
ASP.NET AJAX 4 定义一组特殊的 namespaced 属性通过它您将自定义行为附加到 HTML 模板。 这些行为是 JavaScript 表达式求值并在非常特殊的各阶段的呈现过程执行。 图 1 列出了预定义的代码属性的条件的模板呈现。
代码属性识别的模板生成器并适当地呈现过程中使用其内容。 在 的 图 1 中的属性可以附加到任何 ASP.NET AJAX 模板中使用的 DOM 元素。
请注意,在 的 图 1 中的属性已在代码中范围:直到预览 4 ASP.NET AJAX 4 库和 Visual Studio 2010 beta 1 的命名空间。 从预览 5 开始 ASP.NET 小组消除了代码:命名空间,以及它使用仅 sys:命名空间的所有内容。
图 1 条件呈现的模板中的 DOM 元素的属性
在操作中的条件呈现
sys:if 属性分配布尔表达式。 如果该表达式返回 true,然后呈现在元素 ; 否则,算法继续下一步。 在此处 ’s 只是为了快速说明该点的一个常用示例:
<div sys:if="false">
:
</div>
处理此标记,时生成器只是忽略 DIV 标记和所有其内容。 以某种方式 sys:if 属性可用于在开发时注释掉模板的部件。 所有在所有分配给该 sys:if 属性的常数值 false 的不是从以下 C# 中非常不同:
if (false)
{
:
}
设置为 false 值 sys:if,doesn’t 完全隐藏 HTML 元素。 应当注意的是任何 ASP.NET AJAX 模板最初由浏览器视为像普通的 HTML。 这意味着任何模板完全处理以文档对象模型 (DOM) 树。 但是,作为 ASP.NET AJAX 模板是使用修饰 sys 模板属性没有任何 DOM 树显示时显示页。 在事实的方式数据表 sys 模板属性是包含下面的 CSS 类:
.sys-template { display:none; visibility:hidden; }
sys:if 属性保持关闭模板实际标记的 HTML 元素。 如果附加到 HTML 元素,任何 ASP.NET AJAX 模板之外,sys:if 属性将被忽略。 sys:if 属性不是当前与其他分支关联的。
如果定义,sys:codebefore 和 sys:codeafter 属性执行之前和之后呈现的 HTML 元素。 两个属性可用作组合在一起或分别最佳它适合您。 属性的内容必须是可执行的 JavaScript 代码段。
完全,代码属性提供足够的能力,能够处理几乎每个可能的方案甚至但不总是具有一个简单的解决方案。 let’s 考虑 sys:if 和 sys:codebefore 的不太常用的示例。
通过该方式您可能已经注意到一些神秘 $ 前缀变量在前面的代码。 让我简要介绍它们之前引入该示例。
模板 Pseudo-Variables
在自定义代码中使用的模板代码属性中,您可以访问完整的数据项的公共属性的设置。 此外,您可以访问一些额外的变量定义和公开的模板生成器,为方便起见。
目前,将它们引用为 “ pseudo-columns ” 的文档,但我个人喜欢术语 “ pseudo-variable ”。图 2 列出了所有这些。
这些 pseudo-variables 提供单个呈现引擎的内部状态,如它工作。 当您将在 ASP.NET AJAX 模板中使用任何 JavaScript 变量,您可以使用任何这样的变量。
图 2 Pseudo-Variables 支持的 ASP.NET AJAX 模板生成器
表中的颜色匹配行
作为一个示例 let’s 考虑显示客户的列表加上一个下拉列表中选取某个国家/地区的页。 只要选择了新的国家/地区,客户列表刷新呈现从不同的样式中该国家/地区的客户 (请参阅 的 图 3)。
图 3 了条件的模板的示例 ASP.NET AJAX 网页呈现
图 4 演示示例页的标记。 您可以看到该页面是内容页与母版页关联。 必需的系统命名空间声明在母版页中定义的正文标记中。
图 4 的 代码在操作中的属性
<asp:Content ContentPlaceHolderID="PH_Body" runat="server">
<asp:ScriptManagerProxy runat="server" ID="ScriptManagerProxy1">
<Scripts>
<asp:ScriptReference Name="MicrosoftAjax.js"
Path="~/MicrosoftAjax.js" />
<asp:ScriptReference Path="~/MicrosoftAjaxTemplates.js" />
</Scripts>
</asp:ScriptManagerProxy>
<div>
<asp:DropDownList ID="listOfCountries" runat="server"
ClientIDMode="Static"
onchange="listOfCountries_onchange()">
</asp:DropDownList>
<table id="gridLayout">
<tr>
<th>ID</th>
<th>COMPANY</th>
<th>COUNTRY</th>
</tr>
<tbody id="grid" class="sys-template">
<tr sys:if="$dataItem.Country != currentCountry">
<td align="left">{{ ID }}</td>
<td align="right">{{ CompanyName }}</td>
<td align="right">{{ Country }}</td>
</tr>
<tr sys:if="$dataItem.Country == currentCountry"
class="highlight">
<td align="left"
sys:codebefore="if($dataItem.Country == 'USA') {
$element.style.color = 'orange';
$element.style.fontWeight=700;
}">
{{ ID }}
</td>
<td align="right">{{ CompanyName }}</td>
<td align="right">{{ Country }}</td>
</tr>
</tbody>
</table>
</div>
</asp:Content>
您应该注意预览 5 的 ASP.NET AJAX 需要您重写 MicrosoftAjax.js 文件附带 ScriptManager 控件和 beta 1 的。 这是临时的修复程序将不再有必要,如程序集更新为 beta 2,然后再发布到生产。
与 ASP.NET AJAX 模板 grips 的进来让我之前将控制标记代码,为下拉列表上的焦点。
设置下拉列表
对于下拉列表中的国家/地区代码的是,如下所示:
<asp:DropDownList ID="listOfCountries" runat="server"
ClientIDMode="Static"
onchange="listOfCountries_onchange()">
</asp:DropDownList>
正如您看到该控件将值分配给新的 ClientIDMode 属性,并为 DOM 级别 onchange 事件提供客户端处理程序。 服务器精确地在经典 Page_Load 方法上将控件绑定到其数据:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Fetch data
string[] countries = new string [] {"[All]", "USA", ... "};
// Serialize data to a string
string countriesAsString = "’[All]’, ‘USA’, ...’";
// Emit the array in the page
this.ClientScript.RegisterArrayDeclaration(
"theCountries", countriesAsString);
// Bind data to the drop-down list
listOfCountries.DataSource = countries;
listOfCountries.DataBind();
}
}
绑定过程明确在两个步骤。 首先,JavaScript 数组包含相同的数据以编程方式绑定到 DropDownList 控件响应中发出。 接下来,经典的服务器端数据绑定会发生。
此技术称为双端模板化,并且是标准的客户端的数据绑定模式的变体。 不同之处包括中要绑定的数据提取服务器第一次访问和嵌入的 JavaScript 数组作为客户端提供服务页上的事实。
进一步证明必要的客户端的数据绑定然后可以采取进行使用嵌入的数组。 以此方式基本上保存一个额外的往返,以获取数据。 显示用户交互期间 don’t 更改的静态数据时,传统的客户端数据绑定的此变体很有帮助。 该示例中我使用此技术只用于获取的国家/地区列表 ; 从 Web 服务器使用 Web 服务提取的客户相反,列表。
时要发出的 HTML 标记中使用服务器端控件,您可能可以少控制实际的 ID,如果使用母版页的网页。 在 ASP.NET AJAX 4,新 ClientIDMode 属性提供了更灵活的方法可以解决该问题。
在具体的方式而言如果您在该示例中为静态设置 ClientIDMode 然后 HTML 元素的客户端 ID 将匹配完全服务器 id。 打算重复数据绑定模板化控件的上下文中该服务器控件时,这一轮牌不很有用。
下面的脚本代码处理下拉列表中选择事件的更改:
<script language="javascript" type="text/javascript">
var currentCountry = "";
function listOfCountries_onchange() {
// Pick up the currently selected item
var dd = $get("listOfCountries");
currentCountry = dd.options[dd.selectedIndex].value;
// Refresh the template
refreshTemplate();
}
function refreshTemplate() {
var theDataView = $find("grid");
theDataView.refresh();
}
</script>
注意此代码将引发 JavaScript 错误是否 don’t 将 DropDownList 控件的 ClientIDMode 属性设置为静态。 杩欐槸鍥犱负 ID 重整工作的 ASP.NET 通常不会以确保使用母版页时每个生成的 HTML 元素有唯一的 id。
前面的 onchange 事件处理程序将当前选定的国家/地区的名称保存到全局 JavaScript 变量,并随后刷新 ASP.NET AJAX 模板。 let’s 现在集中在模板上。
条件模板
该模板创建并填充以编程方式为下面:
<script language="javascript" type="text/javascript">
function pageLoad()
{
dv = $create(Sys.UI.DataView,
{
autoFetch: true,
dataProvider: “/ajax40/mydataservice.asmx",
fetchOperation: “LookupAllCustomers"
},
{},
{},
$get(“grid")
);
}
</script>
DataView 客户端控件使调用指定的 Web 服务、 执行给定的提取操作,并使用任何返回的数据填充 DOM 元素名为 “ 网格 ” 中根路径在 ASP.NET AJAX 模板。
使用一个 DataView 实例绑定到 LookupAllCustomers 方法的返回值的示例 Web 服务上呈现整体的模板。 该方法返回与属性 (如 ID、 公司名称和国家/地区的客户对象的集合。
该模板的数据可能发生的变化而不考虑页面的整个生存期内保持其数据绑定。 怎么办相反,只是想要修改的模板呈现 — 没有数据刷新什么 — — 如某些运行时条件更改? 若要获得这,需要在模板中插入代码属性。
您真正需要在此处为未验证条件呈现如果验证在给定的条件的一种方法中的模板,否则为如果一个真正条件呈现。 已提到,sys:如果属性 doesn’t 支持 “ 如果-那么-其他 ” 语义,和它不会全部是呈现,或忽略其父元素,根据布尔临界值。
可能的解决办法来模拟一个条件的两个分支中使用的模板的两个互相排斥的部分组成,每个由不同的布尔表达式控制。 该代码还显示在 的 图 4,遵循下面架构:
<tr sys:if="$dataItem.Country != currentCountry">
:
</tr>
<tr sys:if="$dataItem.Country == currentCountry"
class="highlight">
:
</tr>
变量 currentCountry 是全局的 JavaScript 变量包含当前选定的国家/地区的名称。 每次 onchange 事件引发的 HTML 标记为服务器端 DropDownList 控件,都会更新该变量。
在前面的代码段前者 TR 元素呈现有条件地基于要绑定的数据项的国家属性的值。 如果该变量与匹配选定的国家/地区,前者 TR 则跳过。 此行为依赖于事实全局变量初始化为空字符串,并随后 doesn’t 任何值匹配。 鍥犳表行模板最初呈现的任何返回的客户。
用户在下拉列表中进行了选择,也更新全局 currentCountry 变量。 但是,此操作 doesn’t 自动触发模板上的任何刷新,正如您看到的 的 图 3 中。 onchange 事件处理程序中要求模板的刷新都必须被显式。 在此处 ’s 一种可能的执行的操作方法:
var theDataView = $find("grid");
theDataView.refresh();
$ 查找函数是检索组件的实例的 Microsoft AJAX Library中查找函数的简写。 使用 $ 查找 (或 $ get) 必须有一个位于工作和引用 MicrosoftAjax.js 核心库的方式配置的 ScriptManager 控件。 一旦您检索 DataView 实例与 “ 网格 ” 模板相关联,您只需调用其刷新的方法。 在内部,该方法只需重新编译该模板并更新该 DOM. 请注意 don’t 严格地需要从已注册的组件列表中检索 DataView 实例。 还可以在页面加载时创建全局变量保存 DataView 实例:
var theDataView = null;
function pageLoad()
{
theDataView = $create(Sys.UI.DataView, ...);
:
}
接下来,您刚刚 onchange 处理程序中调用全局实例上的刷新方法:
theDataView.refresh();
在此的第一个示例我使用下拉列表中要呈现的用户界面部分负责触发其余页上的更改。 下拉列表元素是特定的因为它包含了逻辑,以引发更改事件时其元素之一处于选中状态。
ASP.NET AJAX 但是,提供更常规的机制,导致页特定操作的触发器/更改通知事件。 let’s 修改前面的示例使用普通的手进行列表,而不下拉列表。
sys:command 属性
现在使用 HTML 标记为无序的项目符号列表生成国家/地区的列表。 该模板的是,如下所示:
<fieldset>
<legend><b>Countries</b></legend>
<ul id="listOfCountries" class="sys-template">
<li>
{{ $dataItem }}
</li>
</ul>
</fieldset>
用于呈现目的 DataView 控件是以编程方式附属模板。 通过嵌入的 JavaScript 数组提供了到填满该模板的数据。 从服务器上 Page 类使用 ClientScript 对象的服务发出 JavaScript 数组包含国家/地区的列表。 与前面的示例不同 Page_Load 代码 doesn’t 包括服务器端绑定操作:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string[] countries = new string [] {"[All]", "USA", ... "};
string countriesAsString = "’[All]’, ‘USA’, ...’";
this.ClientScript.RegisterArrayDeclaration(
"theCountries", countriesAsString);
}
}
第二个 DataView 控件在客户端上实例化加载页时。 的 图 5 所示,这里 ’s JavaScript pageLoad 函数修改后的代码。
图 5 的 JavaScript pageLoad 函数
<script language="javascript" type="text/javascript">
function pageLoad()
{
$create(Sys.UI.DataView,
{
autoFetch: true,
dataProvider: "/ajax40/mydataservice.asmx",
fetchOperation: "LookupAllCustomers"
},
{},
{},
$get("grid")
);
$create(Sys.UI.DataView,
{
autoFetch: true,
initialSelectedIndex: 0,
selectedItemClass:"selectedItem",
onCommand: refreshTemplate,
data:theCountries
},
{},
{},
$get("listOfCountries")
);
}
</script>
您可以看到第二个用于绑定到 UL 基于模板的国家/地区的 DataView 具有比另一个非常不同的结构。
第一个区别是数据属性用于导入数据。 使用嵌入的数据时,这是正确的过程。
当数据源时的用户定义的对象数组时,您执行通过 {{表达式}} 语法的绑定。 表达式的内容通常是由数据项目公开一个公共属性的名称。 在此示例中相反,数据绑定的源是一个无格式的字符串数组。 随后,数据项目是一个字符串,用来绑定表达式中引用没有公共属性。 在本例中,重新排序以下列:
<ul>
<li>{{ $dataItem }}</li>
</ul>
initialSelectedIndex 和 selectedItemClass 属性配置该 DataView 的预期的行为,如显示的项的所选内容涉及到目前为止。
该 DataView 可以附加模板所选内容的内置行为。 根据要通过 selectedItemClass 属性设置的 CSS 类将被样式 initialSelectedIndex 所指定位置处项。 如果要 don’t 上第一个显示所做的任何选择 initialSelectedIndex 设置为-1。
该模板从结果列表是普通的 UL 列表,并为这样它 doesn’t 本身并入任何逻辑以处理所选内容,正如您在此处看到:
<ul>
<li>[All]</li>
<li>USA</li>
:
</ul>
通过在模板内的 HTML 元素上使用 sys:command 属性,指示模板生成器动态地将附加的事件处理程序的一堆到该元素,如下所示:
<ul id="listOfCountries" class="sys-template">
<li sys:command="select">
{{ $dataItem }}
</li>
</ul>
图 6 显示了如何类已修改的 LI 元素显示在开发工具窗口的 Internet 资源管理器 8。 sys:command 属性采用一个字符串值,代表触发命令的名称。 名称实际上是给您。 通过单击在元素上触发命令。 常用的命令都选择、 删除、 插入和更新。 当命令,将触发时,该 DataView 激发 onCommand 事件。 在此处 ’s 处理 onCommand 事件和刷新该模板的代码:
<script type="text/javascript">
var currentCountry = "";
function refreshTemplate(args)
{
if (args.get_commandName() == "select")
{
// Pick up the currently selected item
currentCountry = args.get_commandSource().innerHTML.trim();
// Refresh
var theDataView = $find("grid");
theDataView.refresh();
}
}
</script>
图 6 处理程序事件动态添加为 sys:command 的效果
相同的方法可用于只要发出直接在 HTML 为下面的下拉列表:
<select>
<option sys:command="select"> {{ $dataItem }} </option>
</select>
尽管,注意 bug 可防止前面的代码按预期 beta 1 中工作。 (图 7 显示示例页中的操作)。
图 7 命令用于处理选择
HTML 属性
在 ASP.NET AJAX 4,sys 一特殊组:属性指定特殊的 HTML 属性的绑定。 从功能上对说话这些属性是与 HTML 属性一样,won’t 注意到不同的行为。 图 8 列出 namespaced HTML 属性。
在模板中的所有元素属性可以为该 sys 都前缀:命名空间。 因此什么 ’s 最终原因使用映射的属性? 和为什么只有其中的一些都列在 的 图 8 中?
图 8 HTML 映射属性
通常您希望绑定到 HTML 属性,但 don’t 要以您 {...} 绑定表达式设置属性本身。 从 DOM 角度绑定表达式就是一个分配给属性的值,并按如下方式处理。 这的课程可能有一些 unpleasant 的副作用。 渚嬪如果您将绑定到一个输入的元素的值属性或元素的内容,加载页时,为第二个用户可能会出现绑定字符串。 如果您正在使用的模板 (即,实时绑定或双向绑定) 之外的绑定表达式,会出现相同情况。 此外,有一堆的 HTML 属性 — — 的那些列在 的 图 8 — 其中使用的绑定表达式可能产生不需要的效果。 渚嬪考虑以下标记:
<img src="{{ URL }}" />
它对于字符串 “ URL ” 触发一个请求而不是属性 URL 上的数据的项的值。
您可能面临其他问题包括由浏览器 XHTML 验证问题和一般错误属性的分辨率。 如果前缀 sys 命名空间与类关键属性,您可以解决该问题。
因此,最好的做法是始终前缀与 sys 命名空间被分配一个绑定表达式的任何属性。 DOM doesn’t 关心 namespaced 属性,因此属性保留其绑定表达式不具负面影响,直到由模板生成器处理。
即使它们不一定会强制除了在其中可以保存您的错误 HTML 分析效果的情况下,将在客户端呈现推荐 Namespaced 属性。
整个新建世界
模板和数据绑定打开全新的世界上的 ASP.NET AJAX 的开发人员的可能性。 下一个月我为回以涵盖各种类型的绑定,包括实时的绑定和大纲/细节视图。
Dino Esposito 是在 IDesign 和 co-author 的架构师 “ Microsoft.net:构建应用程序为企业 ” (微软出版社,2008年)。 Dino 居住在意大利,经常就世界范围内的行业事件进行发言。 您可加入他的博客,网址为 weblogs.asp.net/despos。