读者对象: Force.com 高级开发者
使用过VisualForce的Force.com开发者都会感叹其页面组件在交互界面复用时的简单灵活性,但是也会碰到一个棘手的问题。 当Visualforce页面在引用一个页面组件时,如何获取到页面组件对应的控制类的数据呢? 因为页面和页面组件所属的控制类之间没有 直接的联系。搜索了一下developforce board, 发现一篇介绍这方面技术的妙文,原文出处 http://wiki.developerforce.com/index.php/Controller_Component_Communication 基于自己的理解现转译如下, 希望和大家一起分享这些极具创造力的实现。 现对翻译术语做一下界定: View 层元素: 页面(Page),页面组件(Component), Controller 层元素: 页面控制类(PageController), 组件控制类 (ComponentController)
第一步, 我们需要创建一个Visualforce页面控制类的基本抽象类 PageControllerBase, 这个基类会保留一个对组件控制类的属性,同时暴露自身引用 为公用以便页面组件对其进行引用,看到这里不禁想起了面向对象设计里面的friendly关键字,是不是很眼熟呢? 明显这是对封装的破坏,但是它是友好而具有 建设性意义的破坏 :) 艺术性的代码往往让人看不懂,但还是请继续往下看
public with sharing virtual class PageControllerBase {
private ComponentControllerBase myComponentController;
public virtual ComponentControllerBase getMyComponentController() {
return myComponentController;
}
public virtual void setComponentController(ComponentControllerBase compController) {
myComponentController = compController;
}
public PageControllerBase getThis() {
return this;
} }
第二步,我们需要创建一个Visualforce 页面组件控制类的基本抽象类 ComponentControllerBase, 这个基类同样会保留一个对页面控制类的属性. 由于它会作为页面控制类的组合对象存在与页面控制类中,这里不是将自身引用暴露为共有方法,而是直接将自身引用植入其页面控制类属性中,这样 如果页面控制类作为页面组件的输入参数,那么输入的页面控制类就可以直接访问组件控制类所有的属性和公用方法。类与类之间的互相渗透抽象目标基本达成!
public with sharing virtual class ComponentControllerBase {
public PageControllerBase pageController { get;
set {
if (value != null) {
pageController = value;
pageController.setComponentController(this);
}
}
}
}
第三步, 创建具体组件控制类及其页面组件,这个步骤相对简单,跟一般的页面组件使用基本类似,只是它继承了父类特有的"潜伏"气质 :) 在页面组件中我们可以看到有页面控制类会被作为参数传入,这是重要一环,使这个页面组件可以植入到任意一个继承了我们第一步创建的抽象页面控制类 的页面控制类中。
public with sharing class MyComponentController extends ComponentControllerBase {
private Integer myValue = 3;
public Integer getIntValue() {
return myValue;
}
public void incrementValue() {
myValue++;
}
}
<apex:component controller="MyComponentController" > <apex:attribute name="pageController" type="PageControllerBase" assignTo="{!pageController}" required="true" description="The controller for the page." />
<apex:outputPanel layout="block" style="font-size: 12pt; border: 1pt solid black; width: 250px;"> <center> <apex:outputText value="This is in the component." /> <br/> <apex:outputText value="initial int value: {!intValue}" /> </center> </apex:outputPanel> </apex:component>
第四步,创建具体页面控制类及页面, 在具体类的实现中,我们需要重写抽象类的函数,赋予页面控制类具体的组件控制类引用。 在页面的实现中,会将页面控制类作为参数传入到页面组件中。所有组件控制类进行的操作和数据对页面控制类可见,从而时页面对 组件控制类的数据可见并回显到页面。
public with sharing class MyPageController extends PageControllerBase {
public MyComponentController myComponentController { get; set; }
public override void setComponentController(ComponentControllerBase compController) {
myComponentController = (MyComponentController)compController;
}
public override ComponentControllerBase getMyComponentController() {
return myComponentController;
}
public PageReference callComponentControllerMethod() {
myComponentController.incrementValue();
return null;
} }
<apex:page controller="MyPageController" showHeader="false"> <center> <apex:outputPanel layout="block" style="font-size: 16pt; margin-top: 50px; width: 400px"> <h2>Sample Illustrating How to Establish Communication Between Page and Component</h2> </apex:outputPanel> <apex:outputPanel layout="block" style="width: 300px;"> <hr/> <h1>Component Shown below:</h1><hr/> <c:MyComponent pageController="{!this}" /> <hr/> <br/><br/> <apex:form > <apex:commandButton style="font-size: 12pt; color: black" action="{!callComponentControllerMethod}" value="Call Component Controller Method" rerender="output" /> <br/> <apex:outputText value="Clicking the button above will call the method on the component controller to increment the initial value from the component above." /> </apex:form> <apex:outputPanel id="output" style="font-size: 12pt;"> <center>
<apex:outputText value="{!myComponentController.intValue}" />
<hr/>
</center>
</apex:outputPanel> </apex:outputPanel> </center> </apex:page>
最后,原文还对一个页面如何引用多个页面组件做了扩展介绍,这里不再赘述,详情请见英文原文。 |