@Ivony
2014-05-05T17:15:38.000000Z
字数 5607
阅读 3588
Jumony
Jumony Binding 是 Jumony 的一个衍生项目,这个项目使得我们通过简单的修改就能将 HTML 文档当作模板来使用。
绑定表达式用于描述绑定数据源,其由一个类名(一般是eval)以及若干参数组成。绑定表达式在不同的位置需要使用不同的形式。
下面是一段 HTML 模板,看起来像是这样:
<html>
<body>
<div><strong>Name</strong>: <eval path="FirstName" /> <eval path="LastName"></div>
</body>
</html>
在其中出现的<eval path="FirstName" />
就是一个数据绑定表达式。在数据绑定时,这些标签将会自动替换为实际的数据。
path 的值是一个成员选择表达式,其形式为:
property-name.property-name[index-name].property-name...
其语法和 WPF 绑定表达式的 path 属性是一样的,如果你不熟悉 WPF,这个语法其实与 WebForm 中的 DataBinder.Eval( expression )
的语法也是一样的。
像path="FirstName"
即表示取出当前数据模型的 FirstName 属性的值
当我们需要对属性值进行数据绑定时,则需要使用数据绑定表达式的属性形式:
<a href="{eval path=Url}"><img src="{eval path=ImageUrl}" /></a>
值得注意的是,在属性中使用数据绑定表达式,需要用数据绑定表达式填满整个属性值,前后不允许出现任何其他字符,像下面的这个例子将不会被视为合法的数据绑定表达式:
<img src="http://img.my-site.com/{eval path=ImageName}.jpg" />
如果我们需要进行这样的数据绑定,则需要使用 format 属性,像这样:
<img src="{eval path=ImageName, format=http://img.my.com/{{0}}.jpg}" />
在上面的例子中,我们也知道如何在绑定表达式中插入"{"和"}"字符了,只需要简单的双写这两个字符,即使用"{{"和"}}"来代替"{"和"}"。
最后,特别的, Jumony Binding 还提供了在客户端 JavaScript 脚本中进行数据绑定的语法:
<script>
var userId = null;//{eval path=UserID}
</script>
在脚本中使用绑定表达式的形式为:
var variable-name = any-script-expression;//{binding-expression}
其中等号后面的any-script-expression会被忽略并替换为绑定值的JSON格式。
在脚本中使用绑定表达是有几个值得注意的限制:
不能写成多行,也就是说any-script-expression必须只有一行,同时变量名和等号之间也不允许插入任何换行。
必须以var开头,如果你需要绑定到一个全局变量或是某个对象的属性,则需要先绑定到一个局部变量,再另起一行通过赋值语句赋值给全局变量或者属性。
只能对变量进行绑定,即variable-name必须是一个合法的变量名,而不能是某个对象的属性或是别的表达式。
绑定表达式必须出现在注释的最后面,在后面不允许出现除了空白字符之外的任何字符。
在脚本中使用绑定表达式不会自动对被绑定的对象调用 ToString 方法。在脚本中绑定默认将会生成目标对象的 JSON 表达形式,这使得你可以将一个复杂的对象绑定在 JavaScript 的变量上。但是如果你在绑定表达式中使用了 format 或者其他对值进行文本化的属性,则最终绑定的结果是经过处理后的字符串。
对于如下绑定表达式:
{eval path=Name , format= }
这个有两个属性,一个是path,其值为字符串"Name "
,另一个是format,其值为字符串" "
。
属性名前后的空白字符将会被忽略,但属性值前后的空白字符不会忽略,}
之前的空白字符也不会被忽略。
再考虑这样的绑定表达式:
{eval format=, path}
<eval format="" path /><!--这个是上面表达式的等价形式-->
这个绑定表达式也有两个属性,一个是format,其值为空字符串""
,另一个是path,其值为空(null)。
最后,我们需要知道,绑定表达式可以嵌套:
{eval format={eval path=Format}, path=Name}
一般而言,会先计算被嵌套的绑定表达式的文本表达形式的值,然后再套到外层绑定表达式,在此例中,我们假设当前数据模型的Format属性为"this is {0}"
,则这个绑定表达式等价于:
{eval format=this is {{0}}, path=Name}
可以通过在元素中使用 datamodel 属性来设置当前元素的数据模型:
<div datamodel="{eval path=UserData}">
...
</div>
在这个元素中以及所有未设置数据上下文的子元素中,默认的模型将会变为UserData属性所指的对象。
譬如说下面两段代码是等价的:
<a href="{eval path=ImageInfo.LinkUrl}"><img src="{eval path=ImageInfo.ImageUrl}" /></a>
<a datamodel="{eval path=ImageInfo}" href="{eval path=LinkUrl}"><img src="{eval path=ImageUrl}" /></a>
在第二段代码中,通过datamodel="{eval path=ImageInfo}"
设置了数据模型,所以在该元素及其子元素中默认的数据模型,所以所有的绑定表达式中默认的数据模型变成了ImageInfo属性所指向的对象。
设置datamodel
属性也可以使得系统将数据储存下来,不会每一次绑定的时候都去获取一次对象的值。
如果需要直接绑定当前数据模型,则可以使用不带任何参数的{eval}
绑定表达式:
<img src="{eval}" />
<eval />
最后,如果你将一个空值(null)设置到元素的当前数据模型,那么这个元素将会被直接移除。
有时候我们需要将一个数据列表绑定到模板,这个时候我们需要另一种数据绑定表达式{eval-list}
,这个绑定表达式只能运用在datamodel属性上。
假设当前数据模型有一个属性ImageInfos
,这是一个关于图片信息的列表,其实现了IEnumerable
接口(这一点很重要,一般来说只有实现了IEnumerable
接口的类型可以被当作列表数据源),那么我们只需要写成这样:
<a datamodel="{eval-list path=ImageInfos}" href="{eval path=LinkUrl}"><img src="{eval path=ImageUrl}" /></a>
系统会自动根据数据源中的数据项数将<a>
元素复制同样的份数,并对其中每一份进行数据绑定。
{eval-list}
绑定表达式支持和{eval}
相同的path属性,但是很显然的,{eval-list}
不支持format和其他将绑定数据源转换为字符串的属性。
将{eval-list}
绑定表达式运用在其他属性或是使用其标签形式都是错误的:
<a href="{eval-list path=ImagesInfo}" ></a><!--错误-->
<eval-list path="ImagesInfo" /><!--错误-->
{eval-list}
还支持两个额外的属性,select属性可以指定一个选择器,使得我们可以把数据绑定到符合要求的指定的子元素上去:
<table datamodel="{eval-list path=DataItems, select=tr.data-item}">
<thead>
<tr>
<td>Name</td>
<td>Age</td>
</tr>
</thead>
<tr class="data-item">
<td><binding path="Name" /></td>
<td><binding path="Age" /></td>
</tr>
<tr class="data-item alternative">
<td><binding path="Name" /></td>
<td><binding path="Age" /></td>
</tr>
<tr class="data-item">
<td><binding path="Name" /></td>
<td><binding path="Age" /></td>
</tr>
<tr class="data-item alternative">
<td><binding path="Name" /></td>
<td><binding path="Age" /></td>
</tr>
</table>
在这种模式下,系统不会自动创建被绑定元素的副本,系统只会按照满足要求的元素的出现的顺序,将数据源中的每一项设置为元素的当前数据模型。在这里我们假设数据源是一个List类型的对象,所以可以通过下标访问每一项,那么事实上上面这个模板等价于:
<table>
<thead>
<tr>
<td>Name</td>
<td>Age</td>
</tr>
</thead>
<tr datamodel="{eval path=DataItems[0]}" class="data-item">
<td><binding path="Name" /></td>
<td><binding path="Age" /></td>
</tr>
<tr datamodel="{eval path=DataItems[1]}" class="data-item alternative">
<td><binding path="Name" /></td>
<td><binding path="Age" /></td>
</tr>
<tr datamodel="{eval path=DataItems[2]}" class="data-item">
<td><binding path="Name" /></td>
<td><binding path="Age" /></td>
</tr>
<tr datamodel="{eval path=DataItems[3]}" class="data-item alternative">
<td><binding path="Name" /></td>
<td><binding path="Age" /></td>
</tr>
</table>
绑定表达式可以用来描述我们需要绑定的数据,使用绑定表达式可以将这些数据简单的作为文本绑定到指定的位置。数据绑定器则用来让我们进行复杂的绑定。
系统内置的样式绑定器可以协助我们更好的进行 CSS 样式、样式类以及元素可见性进行绑定,先看如下代码:
<div class="item"><binding path="Name" /></div>
假设我们有一个需求,如果当前的模型处于“被选中”的状态(即数据模型的 IsSelected 属性的值为 true 的时候),我们需要给这个<div>
元素增加一个样式类:selected,借助样式绑定器来非常轻松的完成这一点。
<div class="item" style-class="{binding path=IsSelected, value=selected}"><binding path="Name" /></div>
value属性表示如果绑定的目标值不为false、null或者其他任何可以被转换为false的值的时候,使用value作为最终的值。在这里,这意味着如果IsSelected不为false的时候,我们就增加一个selected的样式类。
上面这段HTML代码也可以写为:
<div class="item" style-class-selected="{binding path=IsSelected}"><binding path="Name" /></div>
这与上面的代码是一样的效果。
当然不仅仅是样式类,CSS样式值也可以通过相同的语法进行绑定:
<div class="item" style-diaplay="{binding path=IsHidden, value=none}"><binding path="Name" /></div>
上面的模板语法表示如果IsHidden
属性不为false,则设置display样式值为none。
值得注意的是,当你使用这种表达式对某个样式设置一个空值(null)的时候,这个样式将被移除。这甚至有可能会覆盖这个元素原有的样式设置:
<div style="color: red;" style-color="{binding path=ForeColor}"></div>
若是ForeColor属性为null,则style="color: red;"
样式设置也会被移除。
总结,样式绑定器将检测元素中符合如下语法规则的属性:
style-class
style-stylename
style-class-classname
并将这些属性的值,应用为当前元素的样式属性和样式类。
这里还有一个值得注意的地方是,这些属性的值并不一定需要是绑定表达式,即:
<div style-color="red"></div>
在进行数据绑定后,将会变为:
<div style="color: red;"></div>
尚未实现
尚未实现