天天看点

Tapestry 5 组件参数

Tapestry 5 组件参数     本文根据 http://tapestry.apache.org/tapestry5/tapestry-core/guide/parameters.html 翻译整理过来,请高手指正,转载请注明出处!     组件参数是 Tapestry 一个重要的方面。组件类实例的存在还不够,它必须还要配置以达到处理正确的事情。配置依据组件参数。   一个组件可以带有许多参数,每一个参数都有一个明确的名字、明确的 Java 类型(可以是一个简单类型),是可选的或必须的。   参数的定义是通过在私有属性上放置一个 Parameter annotation 。   以下列出的是一个循环组件;通过它的 start 和 end 参数(用来设置循环的边界),组件的 body 将多次呈现( renders )。组件可以更新 value 参数(参数所在容器的属性),它能依据 start 或 end 哪一个大来自动加减:   package org.example.app.components;

  参数名字来自属性名(去掉首字符 "_" 和 "$" )——实际情况是匹配 getter 方法 "get" 后字串。这里的参数名字是: "start", "end" 和 "value" 。   绑定参数   上例中的组件可被其他组件或页面的模板( template )引用:  

  end 属性用来绑定 Count 组件的 end 参数,这里它被绑定到一个字符值 "3" ,它将被 Tapestry 自动强制转换为 int 值 3 。   许多参数都可以采用这种绑定方式。   组件参数也可以通过在组件类中使用 Component annotation 来绑定。   当两者绑定发生冲突时,用 Component annotation 绑定的参数要优先于模板绑定的参数。   表达式绑定   前面例子模板中的值“ 3 ”是一个表达式绑定。通过在参数值前面使用不同的前缀,我们可以改变 Tapestry 如何解释表达式的剩余部分(冒号后面的部分):  

前缀 描述
block 模板中 block 的 id 。
component 同一模板中另一组件的 id 。
literal 字面字符常量。
message 从分类信息文件( message catalog )中查找值。
prop 容器组件属性名,用于读写。
translate 配置的 translator 的名字。
validate 被用来创建许多字段校验的校验说明。

  参数有一个默认前缀,通常是 "prop:" ,未指定前缀时用此前缀。   属性绑定   "prop:" 绑定前缀意味着属性绑定。   属性绑定的表达式是一个点分隔的属性名序列。简单的属性表达式就仅仅是一个属性的名字,如: "prop:userName" 。复杂的属性表达式可以在属性前加上部分导航来进行读写,如: "prop:userData.name" 。   除了属性名之外,我们还可以调用任意方法,方法必须是公共的、返回一个非 void 值、抛出的异常为非检查型的且是无参的。为区分方法名和属性名,我们需要加上一个圆括号“ () ”。因此,先前的例可以写成 "prop:getUserName()" 和 "prop:getUserData().getName()" 。注意当表达式最后一项是一个方法名,绑定将是只读的,而非可读写的。   这一特性对于访问标准集合类一对的属性(没有命名相应的属性)来说非常有用,如 Collection.size() 或 Map.keySet() 。   一但获值失败,会不会因为这一表达式中存在空指针而绊倒?你可以使用 "?." 替换 "." 作为分隔符。这会增加一个空值检测,忽略掉表达项的空值。假设 "foo?.bar?.baz" 中 foo 或 bar 为 null ,表达式将会返回 null ,与此同时, "foo?.bar?.baz" 中如果 foo 或 bar 为 null ,则更新此表达式将转变为一个空操作。   另外,还支持少数特殊的情况。大多数情况下,这些特殊的值可以减少你在值前面添加前缀 "literal:" 。这些少数情况相当于属性表达式:

  • "true" 和 "false" 将被转换成布尔值。
  • "null" 将被转换成值null。
  • "this" 将是组件自己的引用。
  • 简单的数字值也可以被接收,这些将会被解析成Long或Double对象。比如:"prop:3.14"。
  • 被句点分隔的一个整数范围。如:"1..10"。
  • 单引号里的字面字符串,如:"'Hello World'"。

这些情况下,多出的空格将被忽略。对于关键字( "true" , "false" , "this" 和 "null" ),忽略大小写。 这些值都是只读且无变化的。   验证绑定   绑定前缀 "validate:" 非常特殊,它允许一个简短的字符串来创建和配置对象执行的表单控制组件的输入验证,如 TextField 和 Checkbox 。   这些字符串是一个逗号分隔的验证类型( validator types )列表,验证类型是对象执行验证的简短别名。在许多情况下,验证是多方式可配置的:比如,一个强制最小字符长度验证,想知道最小的字符长度是多少的,这个值被指定在一个等号( = )后面。   如: validate:required,minLength=5强制该字段域必填且最少5个字符。   TODO: More ability to escape or quote constraint values. Ability to reference methods or properties of the container to perform some of the validation. Links to proper discussion of validation, once the code and documentation is ready.   转换绑定   绑定前缀 "translate:" 也可以用在输入验证。它是配置项 Translator 的名字,主要负责服务端与客户端表现数据转换(例如,客户端字符串与服务端数字值的转换)。   有效的转换( translators )列表配置在 TranslatorSource 服务中。   非正式(informal )参数   一些组件支持非正式参数,额外的参数超过了正式定义的参数。非正式参数将在组件标签呈现时作为额外的属性输出。一般来说,组件与特有的 HTML 标签有 1:1 的对应关系(比如 TextField 和 < input >支持非正式参数)。   非正式参数通常且于设置一个元素的样式,或者指定客户端的事件句柄(处理)。   非正式参数默认的绑定前缀依赖于指定的参数绑定方式。如果参数绑定在 Java 类中的 Component annotation 里面,这里的默认绑定为 "prop:" ,如果参数绑定在模板中,默认的绑定前缀为 "literal:" 。这反映出用 annotation 使参数指定在 Java 类里,很可能就是一个用来计算的值,反之,一个指定在模板中的值将被简单的拷贝,按照原样输出到 HTML (结果)流中。   参数双向性   参数并非简单的变量;在组件和它的容器组件(包含组件的组件)的属性之间,每一个参数等同于一个连接或者说绑定,当使用前缀 prop: 时,组件能将改变通过赋值的方式强制到它的容器组件的属性里。  

  因为 Count 组件更新 value 参数( _value 属性 ),容器组件的index属性将被更新。在Count的body内,我们通过扩展表达式(等同于JSP中表达式语言) ${index}输出当前index属性的值。输出结果大至如下:     <p> Countdown: 5 ... 4 ... 3 ... 2 ... 1 ... </p> (尽管空格完全不同) 它们的关系是组件可以读取固定的值或者其容器组件中可变的属性值,同样能更改其容器组件中的属性值。   必选参数   必选参数必须要绑定。如果一个组件有未绑定的必选参数将会发生运行时异常。   可选参数   参数不是必要的,是可选的。 我们可以设置可选参数的默认值以提供其他属性使用。在 Count 组件中,参数 min 有默认值为 1 ,如果参数 min 绑定了一个值,则绑定的值将替代默认值。 参数的默认绑定 Parameter annotation 的 value() 属性可以用来指定一个绑定的表达式,用以参数未绑定的情况下作为默认绑定。典型地,绑定的表达式是一个属性的名字,这个属性将会在运行时被计算值。 Example:

  在其他地方,我们可以在 value 上使用前缀。一个普通的前缀 "message:" 用以访问国际化信息( localized message )。     计算方式的参数默认绑定   在少数情况下,我们需要用计算绑定的方式来作为参数的默认值。在此,我们要提供一个默认的无参的绑定方法,方法的返回值用来绑定参数。返回值可以是 Binding 实例或者简单的值(比较多的使用)。   方法名以 "default" 加上首字母大写的参数名。   使用这种方法,先前的例子可以写成:  

: 在这个例子中,属性表达式 "defaultMessage" 用来动态访问信息。   以上例子可以写得更简洁:  

  这种形式很像使用绑定前缀 "literal:" ,但是字面值是通过 defaultMessage() 方法计算出来的。   显然,这要比简单地指定一个默认的参数值做更多的工作。 In the few real cases where this is approach is used, the default binding method will usually deduce a proper binding, typically in terms of the component's id. For example, the TextField component will deduce a value parameter that binds to a property of its container with the same name. 这种方法在一些少量的实际情况中被采用,默认的绑定方法将通常会根据组件的 id 产生适当的绑定。比如, TextField 组件将产生 value 参数,用它容器中具有相同名字的一个属性绑定到此 value 参数。   默认的绑定方法只有在 Parameter annotation 未提供默认值时才被调用。   未绑定的参数   如果一个参数没有被绑定(可选参数),那么它的值可以在任何时候被读写。   更新对于未绑定的参数并没有作用。在第一个例子中, Count 组件的 value 参数没有绑定,这完全是可以的。   注意:更新对于这个属性是临时性的;当组件完成呈现时,属性将被置为默认值。   TODO: This seems contradictory. What does it mean to update an unbound component parameter when the component is not rendering? 参数缓存   读取一个参数值会耗费不少资源的(因为类型的强制转换)。因此,缓存参数值是有意义的,至少当组件正在呈现自己时。极少情况下,我们可以通过设置 Parameter annotation 的 cache() 属性为 false 来打破这种缓存机制。   参数类型的强制转换   Tapestry 包含自动类型转换( coecing types automatically )机制。大多情况下,它用来将字面字符串转换成相应的值,但在许多情况下,将会发生较复杂的转换。   参数名   缺省情况, Tapestry 将去掉首字母 "$" 和 "_" 的属性名转换为参数名。 我们也可以使用 Parameter annotation 的 name() 属性覆盖这种缺省情况。   决定是否绑定   很少情况,我们需要根据参数是否绑定来产生不同的行为。这可以通过查询组件资源( resources )来完成,组件资源可以通过 Inject annotation 注入到组件中:  

以上粗略举例这种方法。因为参数类型是简单类型 int ,我们很难区分没有绑定和显示地绑定 0 。   Inject annotation 将注入组件的资源( ComponentResources ),这些资源是我们写的 Java 类与 Tapestry 底层构建我们类的类之间的桥梁。任何情况,资源一但被注入,就可以被访问。