跳转到内容

WebObjects/Web 应用程序/开发/WO 组件/代码模板和 WODs

来自维基教科书,开放世界的开放书籍

WebObjects 是使用模型-视图-控制器架构设计的。这意味着您的应用程序被分成几个不同的角色。您的 EOModels 和它们的 EOEntities 构成了模型层。视图和控制器使用 WOElements 和 WOComponents 实现,这些组件分布在三个不同的部分:代码、模板和 WOD。WebObjects 的一个有趣之处在于,组件架构并不假定生成 HTML。您可以使用相同的组件系统创建 HTML、XML、CSS、XSLT、电子邮件或任何其他类型的动态数据。

无论您扩展 WODynamicElement 还是 WOComponent,您始终都有一个 Java 类来实现组件的逻辑,并可选地存储其状态。默认情况下,Java 类的名称决定了您将在整个应用程序中如何引用您的组件,您将在后面的示例中看到几个例子。在实现 WODynamicElements 和 WOComponents 之间有一些关键区别。

WODynamicElements

[编辑 | 编辑源代码]

如果您正在构建一个WODynamicElement,您的类将没有模板或 WOD 文件。所有输出都直接在代码中生成。WODynamicElements 也不得不假设每个使用都会有一个实例,而是您的类的单个实例可能会同时为多个组件提供服务。因此,您的 WODynamicElement 必须完全线程安全。这些属性使 WODynamicElements 非常适合生成相对较小的输出或“计算”的输出。如果您发现自己编写大量代码来处理输出数据,您可能需要考虑从构建 WOComponents 中获得的模板的好处。

根据您的元素提供的或所需的交互类型,您可以在您的 WODynamicElement 中实现三种主要方法

  • takeValuesFromRequest
  • invokeAction
  • appendToResponse

有关这些方法在运行时的执行顺序的更多信息,请阅读请求-响应循环部分。

要从请求中接收值(例如,表单数据),请实现 takeValuesFromRequest 方法。如果您的 WODynamicElement 响应用户操作,您必须实现 invokeAction 方法。如果您的 WODynamicElement 生成输出,您必须实现 appendToResponse 方法。在运行时,您的 WODynamicElement 将连接到一个 WOComponent,您可以通过传递到上述三个方法中的每个方法的 WOContext 访问它,该上下文可用于解析页面上特定外观中的绑定。例如,要检索绑定的值,它可能看起来像

 private WOAssociation myNameAssociation;
 ...
 public void appendToResponse(WOResponse _response, WOContext _context) {
   WOComponent component = _context.component();
   String name = (String) myNameAssociation.valueInComponent(component);
   super.appendToResponse(_response, _context);
 }

这种相同的模式用于无状态 WOComponents。WODynamicElement 可以提供一个 .api 文件来描述其绑定。有关 .api 文件的更多信息,请参见下面的 .API 文件部分。

WOComponents

[编辑 | 编辑源代码]

WOComponents 在 WODynamicElements 之上增加了几个主要功能。主要额外功能是支持模板。当您创建 WOComponent 时,您还可以创建一个“.wo”文件夹,其中包含三个文件:一个 .html 或 .xml 模板,一个 .wod 绑定声明和一个 .woo 文件。与 WODynamicElements 一样,WOComponents 也可以提供一个可选的 .api 文件(如下所述)。WOComponent 的代码可以简单到仅声明您的类扩展 WOComponent 以及它的构造函数。例如

 public class MyComponent extends WOComponent {
   public MyComponent(WOContext _context) {
     super(_context);
   }
 }

上面针对 WODynamicElements 描述的三种核心方法也存在于 WOComponent 中,但是由于 WOComponents 可以是有状态的,因此您也可以在组件中声明实例变量 (ivars),您可以将其绑定到。每次在页面上使用 WOComponent 时,都会创建一个新实例,这是与 WODynamicElement 相比的另一个重大区别。您的 WOComponents 不必是线程安全的。

WO 文件夹

[编辑 | 编辑源代码]

默认情况下,WOComponent 模板可以是 .html 或 .xml 文件。WebObjects 模板将所有绑定声明分离到一个名为 .WOD 文件的单独文件中,这与许多其他 Web 框架不同。因此,模板通常更容易阅读,并且通常可以更轻松地交给设计师处理,而无需担心代码相关的绑定意外被修改。WOComponents 在模板中使用“webobject 标签”声明,它看起来像 HTML 标签

 <webobject name = "PersonName"></webobject>

在 WebObject 标签中声明的名称将用作在您的 WOD 文件中查找相应绑定定义的键。

WOD 文件

[编辑 | 编辑源代码]

WOD 文件(Web 对象声明?)定义了模板中每个组件引用的绑定。例如,如果上面的引用连接到组件上的一个“public String personName()”方法,则 WOD 声明可能如下所示

 PersonName : WOString {
   value = personName;
 }

此声明有几个部分。第一个标记是出现在模板中的 WebObject 标签名称。这些值必须完全匹配才能使 WebObjects 解析绑定信息。如果您在模板中引用了一个名称但没有声明相应的 WOD 条目,WebObjects 将在运行时抛出异常。

第二个标记,冒号之后,是将在您的模板中实例化的组件或元素的名称。此名称使用 NSBundle 查找规则解析,默认情况下,此规则将找到与标记名称相同的任何类,不包括包名称。例如,WOString 实际上可能是“com.webobjects.appserver._private.WOString”,但该类的名称(不包括其包名称)是“WOString”。这很重要,因为它意味着如果您使用此语法,您必须在所有包中唯一地命名您的类(一个 Objective-C 遗产,其中不存在包的概念)。但是,您可以选择完全限定您的类名,在这种情况下,上面示例中的“WOString”将变为“com.webobjects.appserver._private.WOString”,并且类解析将没有歧义。可以使用 NSUtilities 上的方法覆盖正常的 NSBundle 查找过程

 NSUtilities._setClassForName(com.bla.TestComponent.class, "TestComponent");

此调用将 WOD 引用中出现的名称 "TestComponent" 绑定到类 com.bla.TestComponent,避免进一步的类“查找”。这也有助于用您自己的类替换内部组件实现。例如,您可以用您自己的类替换 WOString 的实现,Project WOnder 广泛使用此功能来提供对核心组件的错误修复和增强。

在 WOD 文件中继续前进,在花括号内,您可以提供一系列键值对,每个键值对以分号作为分隔符。等号左侧是绑定名称。绑定名称在 WOComponent 上通过尝试找到与以下命名约定之一匹配的 mutator 方法或字段来解析:public void setValue(Xxx param)、public void _setValue(Xxx param)、public Xxx value、public Xxx _value(假设上面的绑定名为“value”)。因此,如果您的 WOComponent 具有 setValue 方法,并且启用了自动绑定同步,则将调用 setValue 方法,并传入绑定右侧的计算值。在上面的示例中,右侧是 "personName"。如果 personName 在上面的示例中实际上是用引号引起来的,则它将被视为字符串文字,并将等效于调用 setValue("personName")。

但是,personName 没有引号,因此使用 Key-Value-Coding (KVC) 进行解释。Key-Value-Coding 允许您将一系列访问器方法调用或字段引用串联成一个字符串,WebObjects 将使用 Java 反射动态解析该字符串。例如,在上面的示例中,personName 将尝试查找任何名为:public String getPersonName()、public String _getPersonName()、public String personName()、public String _personName()、public String personName、public String _personName 的访问器或字段。因此,如果您的 Java 类具有 public String getPersonName() 方法,则该方法的返回值将传递到您的 WOComponent 的 setValue(..) 方法。KVC 变得更加有趣的地方在于,您可以构造一系列方法调用。例如,想象一下,您的 WOSession 有一个 "public Person person()" 方法,该方法有一个 "public Address address()" 方法,该方法有一个 "public String zipCode()" 方法。您的 WOD 文件绑定可能如下所示:"value = session.person.address.zipCode;",这会将您会话中人员地址的邮政编码绑定到 value。Foundation 提供的 NSArray 操作更有趣。例如,如果您的会话有一个 "public NSArray purchaseAmounts()" 方法,该方法返回一个 BigDecimal 数组,则可以引用绑定 "value = session.purchaseAmounts.@sum",它将返回数组中值的总和。Foundation 类中还有其他一些数组操作可用,而 Project Wonder 在其 ERXArrayUtilities 类中提供了更多操作。有关更高级的 KVC 功能,请阅读 Project Wonder 的 WOOgnl 部分。

单个 WOD 条目可以包含多个绑定声明,而 WOD 文件可以包含多个条目。例如,以下是一个实际 WOD 文件的摘录

 FilterAction : WOSubmitButton {
   action = filter;
   value = "filter";
 }
 
 EditAction : WOHyperlink {
   action = editRequest;
 }
 
 NoRequestsConditional : WOConditional {
   condition = requestsDisplayGroup.allObjects.count;
   negate = true;
 }

WOO 文件

[编辑 | 编辑源代码]

至少,WOO 文件声明了 WO 版本和模板的字符编码。例如,一个简单的 WOO 文件可能看起来像这样

 {
   "WebObjects Release" = "WebObjects 5.0"; 
   encoding = NSMacOSRomanStringEncoding; 
 }

此外,WOO 文件可以包含您的 WO 组件可以引用的实例化对象的定义。如果您使用 WOBuilder 构建组件,您将在组件的 WOO 文件中找到 WODisplayGroups 属性的定义。

API 文件

[编辑 | 编辑源代码]

API 文件是可选文件,它们出现在与您的 .WO 相同的文件夹中,并提供有关您的组件的元数据,IDE 可以使用这些元数据为您的组件用户提供更好的体验。例如,API 文件声明了您组件的所有绑定、绑定类型(布尔值、日期等),以及用于定义绑定验证的相当广泛的 XML 语言。

例如

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <wodefinitions>
   <wo wocomponentcontent="true" class="AjaxSortableList.java">
     <binding name = "id"/>
     <binding name = "list"/>
     <binding name = "listItemIDKeyPath"/>
     <binding name = "startIndex"/>
     <binding name = "action"/>
 
     <validation message = "'id' is a required binding">
       <unbound name = "id"/>
     </validation>
   
     <validation message="'listItemIDKeyPath' must be bound when 'list' is bound">
       <and>
         <bound name = "list"/>
         <unbound name = "listItemIDKeyPath"/>
       </and>
     </validation>    
   </wo>
 </wodefinitions>

在此示例中,API 定义了顶部的绑定,以及一系列验证。当验证内部的声明计算结果为“true”时,将在 IDE 中显示验证消息。例如,对于第一个验证,如果 "id" 值未绑定,则将显示验证消息。在第二种情况下,如果 "list" 值已绑定,但 "listItemIDKeyPath" 未绑定,则将显示消息。

华夏公益教科书