Mach-II DevGuide 系列教程译文: 页面的设计

 

本章讲设计问题的页面设计部分.页面包含了程序所有HTML用户界面,并作为.cfm页的集合来实现.

 

控制器/页面数据传递(Controller / View Data Transfer)

 

因为页面不含任何逻辑(在此需要重申数据结构以列表形式呈现它们),动态部分由控制器(Mach_II)传入,不管是请求域变量还是经由事件对象(只作为无格式事件在页面被访问).对象的传递一节讲述了这个问题,建议页面包含尽量少的变量,来更好的维持其封装性和低耦合性.

 

关键要记住:请求域变量和页面使用的事件对象里的变量定义了控制器和页面之间的API,这样指定了设计早期需要决定的约定,有可能的话,备案以供接下来的项目周期参考.这可以提高可维护性和稳定性.

 

你可以将请求域数据转移到事件对象,从而将API简化到当前事件对象.

          <event-arg name="foo" variable="request.foo" />
   
   

是否这样做了, 依赖于你是否认为只得为提高封装性而增加<event-arg>命令条目.另一个选择是使用自定义的invoker,EventInvoker,在模型设计一章的Invokers & Listeners小节.我们的经验是将数据留在它产生的地方,而不是人为地移动它而增加封装复杂度.换句话说,我们使用页面里的请求域来处理产生于请求域的数据(例如,contentKey=来设置它);使用事件对象来处理事件对象里的数据例如,URL和表单参数,明确设置<event-arg>或者在过滤器(filter),listener以及插件(plugin),或者由EventInvoker通知命令里,设置event.setArg()).

 

参数化页面和退出事件 Parameterized Views and eXit Events (XEs)

页面里的事件变量的好的用法是用<event-arg>来设置参数化页面的值,比如同时支持修改和创建功能(一般它们的提交和取消动作值与提交按钮的标签值相同)的表单.这比将提交与取消动作硬编码进页面要好得多.

 

Fusebox使用了类似的方法,引用了XFA(eXit FuseAction),这些被称作退出事件(XEs - eXit Events).再说一遍,它降低了页面与程序控制流的耦合性,所以最好的选择就是适当参数化你的页面,使得一个页面(链接和表单动作)的退出路径作为参数被传递.例如,被分页的数据集的页面需要上一页/下一页的链接:

          <a href="index.cfm?event=#event.getArg('XEPrevious')#">Previous</a> |
   
   

     <a href="index.cfm?event=#event.getArg('XENext')#">Next</a>

 

这就允许页面在程序不同部分被使用mach-ii.xml文件里传递好的上一页/下一页事件来调用.

 

contentKey问题

 

Mach-II允许页面被译成contentKey变量,这使得简单的HTML页可以构成复杂的view(页片断).contentKey变量一般在请求域,所以它们也是控制器和页面之间的API一部分,如上面所讨论的那样.因此打算这么做的话也得小心. 作为大体的方针,你将创造出更加易维护的程序,条件是:你必须安排好一个程序,使它的动态数据(请求域变量或者事件对象)能够传递给译成contentKey变量的单一页面,并且这些contentKey变量聚集到单一的布局页面上.例如:

          <event-handler event="showFoo" access="public">
   
   
                    <notify listener="foo" method="getFoo"
   
   
                               resultKey="request.fooData" />
   
   
                    <view-page name="fooPage" contentKey="request.content" />
   
   
                    <view-page name="mainLayout" />
   
   
          </event-handler>
   
   

 

例子中,fooPage和控制器之间的API是单一的request.fooDate(它可以是一个包含复杂条目的结构体),并且mainLayout和控制器之间的API是单一的request.content.,mainlayout和任何动态数据事件没有任何依存关系,所有动态数据被其他的页面译成了HTML后才配嵌入mainLayout.

 

如果你有若干个页面需要几个在一起作为一个布局的一部分,你可以添加到一个contentKey来简化你的事件句柄( 1.0.10 里才有的):

          <event-handler event="showFoo" access="public">
   
   
                    <notify listener="foo" method="getFoo"
   
   
                               resultKey="request.fooData" />
   
   
                    <view-page name="fooPod" contentKey="request.content" />
   
   
                    <view-page name="barPod" contentKey="request.content" append="true" />
   
   
                    <view-page name="mainLayout" />
   
   
          </event-handler>
   
   

 

在这个例子里,barPod的输出是Mach-IIrequest.contentfooPod的一个扩充,这样你就不需要额外的contentKey和布局了.

 

这里的意图是为了每个页面可以专注于它们所依赖的数据以及使用布局来聚集来自预编译HTML片断的页.这可以保持每个页面内聚:每个页面各自完成使命.它同样降低了页面以及页面与控制器之间的耦合性.下一章将详细讨论这个原理.

 

门户,又名格子布局(Portals a.k.a. Grid Layouts

 

在典型的程序里,用户界面是很复杂的,并且含有诸多动态元素.导航条往往是动态的,例如,显示你所在的程序流程位置.检查你的用户界面,将它分解成更小更简单的可以构成一格的零件.大多数门户网站(例如,雅虎)都是格子布局的很好的例子.它们有多样的专栏,各自又包括多样的区域(常被称为pod(豆荚/密集小区))

 

带来的问题是,页头和页尾是否该作为独立的pod页面被布局页面组装起来的?如果页头和页尾是站点级别的(site-wide )元素,那么它们很可能只需要在站点外观发生改变的时候需要被改动.将它们作为独立页面执行是可行的,但之后每个生成HTMLevent-handler都要像以下这项:

          <event-handler event="..." access="...">
   
   
                    ...
   
   
                    <view-page name="..." contentKey="request.content" />
   
   
                    <view-page name="header" contentKey="request.header" />
   
   
                    <view-page name="footer" contentKey="request.footer" />
   
   
                    <view-page name="mainLayout" />
   
   
          </event-handler>
   
   

 

或者要在mach-ii.xml中简化代码,你需要这样:

          <event-handler event="..." access="...">
   
   
                    ...
   
   
                    <view-page name="..." contentKey="request.content" />
   
   
                    <announce event="layoutPage" />
   
   
          </event-handler>
   
   

 

          <event-handler event="layoutPage" access="private">
   
   

                    <view-page name="header" contentKey="request.header" />

                    <view-page name="footer" contentKey="request.footer" />

                    <view-page name="mainLayout" />

</event-handler>

 

每个需要生成HTML的事件句柄都需要通告layoutPage事件.如果你喜欢这种样式,可以自由实现页头和页脚在独立页面上.如果你觉得页头和页脚太过依赖全局页布局的话,可以或直接在布局页面里执行它们,或以分离的文件形式执行,然后把它们包含到布局页面:

          <!--- doctype etc goes here --->
   
   
          <html>
   
   
          <!--- head / title etc go here --->
   
   
          <body>
   
   
          <cfinclude template="header.cfm" />
   
   
          #request.content#
   
   
          <cfinclude template="footer.cfm" />
   
   
          </body>
   
   
          </html>
   
   

  
  
   
    
  
  
Macromedia,我们使用自定义标签来控制页的全局表现,看上去像这样:
    
    
          <!--- set up page settings etc --->
   
   
          <cfimport taglib="/customtags/mmlf/" prefix="mmlf" />
   
   
          <mmlf:renderpage>
   
   
          #request.content#
   
   
          </mmlf:renderpage>
   
   

  
  
   
    
  
  

页面和域(Views And Scopes)

如果你遵循上面的推荐,你的页面会包含对一些请求域变量的调用,还可能有当前事件对象(事件).这对控制器与页面之间的接口很有效.那么在页面使用其他的域呢?

l          Applicationserver : 不允许.

但从封装性考虑,页面不可以涉足这些域(的确,几乎没有什么代码需要在直接访问这些域).Listener和框架其他部分,已经储存在application,并可以管理application域数据(作为数据实例),后者将明确地经由请求域或事件对象(resultKey)来发放给页面.server域只可以用于跨项目程序缓存,并需要被封装在一个litener(另外,它同样经由请求域或事件对象来发放给页面).

l          CGI : 不允许.

CGI域的变量用于不同的网络服务器之间,会受不同设置的影响.它们可以在一个程序下驱动逻辑,但不可以在一个程序下直接驱动表现层.这样,它们就不可以在一个页面里被访问.如果你需要基于不同的布局,比如说,用户浏览器,你可以使用一个listener来通知基于CGI变量不同事件,并编译不同的布局来执行这些事件.

l          Cookie : 不允许.

因为cookies一般被用于驱动行为而不是布局,在页面里使用cookie域是被禁止的.如果需要显示一个cookie的值到页面上,一般应该使用提供确认功能的listener来传递它,经由请求域或事件对象传递到页面上,而不是直接发送.

l          Session : 允许.

如果程序在session域运送数据,并且一个页面要访问它,可以在页面直接涉足session域来访问数据.严格来讲,封装促使我们使用一个Listner或者filter来拷贝所需的session域数据到请求域或者事件对象,不过对于一些广泛依赖于session的程序来说,用复杂编码是很麻烦的,所以不只得那样去做(不经需要你拷贝所有必要的session域数据进入请求域或事件对象, 扩展页面API,而且如果你执行更新数据的工作,就需要将它们序列化回session域或者提供调用(而不是提供值)).如果你在session域权利有限,你可以使用filter来拷贝session变量给请求变量或事件对象,而所付的代价较小.记住,我们在Macromedia从不使用client,所以它也不被推荐.

l          URL和表单域 : 不允许.

因为来自这些域的数据是自动传递给事件对象的,页面从不直接涉足URL或表单域.表单数据可通过使用bean将表单收入一个简单事件变量的方式处理(使用<event-bean>命令或者像通讯录例子里的ContactBeanerFilter过滤器).页面可以使用event.getArg("argName")从事件对象里找回URL和表单域的值.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值