public
with sharing virtual
class
PageControllerBase {
02
03
private
ComponentControllerBase myComponentController;
04
05
public
virtual ComponentControllerBase getMyComponentController() {
06
return
myComponentController;
07
}
08
09
public
virtual
void
setComponentController(ComponentControllerBase compController) {
10
myComponentController
=
compController;
11
}
12
13
public
PageControllerBase getThis() {
14
return
this
;
15
}
16
17
}
Basically we are declaring a field compController to hold a reference to the component controller instance. We also set up a getter to return the instance of the page controller. Finally, we create a virtual method to set the compController. It is useful to make these virtual so that you can simply extend this class in your page controller and, if required, cast the component controller to a specific type (assuming that your component controller extends the class below).
The next step is to create a virtual class for the ComponentController something like this:
01
public
with sharing virtual
class
ComponentControllerBase {
02
03
public
PageControllerBase pageController {
get
;
04
set {
05
if
(value !=
null
) {
06
pageController
=
value;
07
pageController.setComponentController(
this
);
08
}
09
}
10
}
11
}
This class simply defines a property to contain the page controller. This value will be passed by using an attribute on the component markup. Again, these methods are defined as virtual in case you need special handling or casting for the page controller.
Once you have created these two virtual classes you can now create component controllers and page controllers that extend these classes. This will imbue your controller implementations with the ability to communicate with each other. Below is a sample implementation of a component controller.
01
public
with sharing
class
MyComponentController
extends
ComponentControllerBase {
02
03
private
Integer myValue
=
3
;
04
05
public
Integer getIntValue() {
06
return
myValue;
07
}
08
09
public
void
incrementValue() {
10
myValue++;
11
}
12
}
The code above is simply defining a getter and a method that will later be called by the page controller. The last method in the class is implementation specific to this controller.
The component markup is shown below.
01
<apex:component
controller=
"MyComponentController"
>
02
<apex:attribute
name=
"pageController"
03
type=
"PageControllerBase"
04
assignTo=
"{!pageController}"
05
required=
"true"
06
description=
"The controller for the page."
/
>
07
08
<apex:outputPanel
layout=
"block"
style=
"font-size: 12pt; border: 1pt solid black; width: 250px;"
>
09
<center>
10
<apex:outputText
value=
"This is in the component."
/
>
11
<br/
>
12
<apex:outputText
value=
"initial int value: {!intValue}"
/
>
13
</center>
14
</apex:outputPanel
>
15
</apex:component
>
The thing to pay close attention to is the definition of the pageController attribute. We are setting the type to PageControllerBase. This allows you to reference any controller that extends the PageControllerBase class.
The page controller implementation is shown below.
01
public
with sharing
class
MyPageController
extends
PageControllerBase {
02
03
public
MyComponentController myComponentController {
get
; set; }
04
05
public
override
void
setComponentController(ComponentControllerBase compController) {
06
myComponentController
=
(MyComponentController)compController;
07
}
08
09
public
override ComponentControllerBase getMyComponentController() {
10
return
myComponentController;
11
}
12
13
public
PageReference callComponentControllerMethod() {
14
myComponentController.incrementValue();
15
return
null
;
16
}
17
}
In this implementation we are overriding the setComponentController and getMyComponentController methods. We do this so that we can cast the component controller to the specific implementation that we are creating and also reference the MyComponentController declared field - myComponentController.
In addition to the overrides there is an implementation specific action method that calls the incrementValue method of the component controller implementation.
Finally, the page markup that uses this controller is shown below.
01
<apex:page
controller=
"MyPageController"
showHeader=
"false"
>
02
<center>
03
<apex:outputPanel
layout=
"block"
style=
"font-size: 16pt; margin-top: 50px; width: 400px"
>
04
<h2>
Sample Illustrating How to Establish Communication Between Page and Component
</h2>
05
</apex:outputPanel
>
06
<apex:outputPanel
layout=
"block"
style=
"width: 300px;"
>
07
<hr/
>
08
<h1>
Component Shown below:
</h1>
<hr/
>
09
<
c:MyComponent
pageController=
"{!this}"
/
>
10
<hr/
>
11
<br/
>
<br/
>
12
<apex:form
>
13
<apex:commandButton
style=
"font-size: 12pt; color: black"
14
action=
"{!callComponentControllerMethod}"
15
value=
"Call Component Controller Method"
16
rerender=
"output"
/
>
17
<br/
>
18
<apex:outputText
value=
"Clicking the button
above
will call the
19
method on the component controller to increment the initial
20
value
from
the component
above
." /
>
21
</apex:form
>
22
<apex:outputPanel
id=
"output"
style=
"font-size: 12pt;"
>
23
<center>
24
<apex:outputText
value=
"{!myComponentController.intValue}"
/
>
25
<hr/
>
26
</center>
27
</apex:outputPanel
>
28
</apex:outputPanel
>
29
</center>
30
</apex:page
>
The sample page simply displays the initial value held by the component controller using the component and provides a button to call the action method on the page controller to exercise the incrementValue method on the component controller.
Here is a link to a page that implements the technique shown above.
http://ls-developer-edition.na6.force.com/MyPage
Cheers
Extended to support multiple visual force(vf) components on a vf page by Kunle Adeniyi
Fantastic work by the previous developer(Can you put your name in here). When I came across this page I was very impressed and inspired. I started using this pattern immediately but then I quickly ran into a road block. I needed to have multiple components on a visualforce page and I needed to be able to access those components from the page controller. So I added a couple of methods to the ComponentControllerBase class, PageControllerBase, and the component markup to achieve this. See below
Modify the PageControllerBase like this:
01
public
with sharing virtual
class
PageControllerBase {
02
03
private
ComponentControllerBase myComponentController;
04
private
Map<
String
, ComponentControllerBase
>
componentControllerMap;
//new
05
06
public
virtual ComponentControllerBase getMyComponentController() {
07
return
myComponentController;
08
}
09
10
//new getter for the hashmap
11
public
virtual Map<
String
, ComponentControllerBase
>
getComponentControllerMap(){
12
return
componentControllerMap;
13
}
14
//new method for putting value in the hashmap
15
public
virtual
void
setComponentControllerMap(
String
key, ComponentControllerBase compController){
16
if
(componentControllerMap
=
=
null
)
17
componentControllerMap
=
new
Map<
String
, ComponentControllerBase
>
();
18
componentControllerMap.put(key,compController);
19
}
20
21
public
virtual
void
setComponentController(ComponentControllerBase compController) {
22
myComponentController
=
compController;
23
}
24
25
public
PageControllerBase getThis() {
26
return
this
;
27
}
28
29
}
Basically what I have done here is to add a hashmap that will store each componentController, this way in the page controller I can get the controller of a specific component using the appropriate key.
Modify the component mark up like this:
01
<apex:component
controller=
"MyComponentController"
>
02
03
<!-- the
new
attribute --
>
04
<apex:attribute
name=
"key"
05
type=
"String"
06
assignTo=
"{!Key}"
07
description=
"The key given to this component so the page can easily get access to it"
/
>
08
09
<apex:attribute
name=
"pageController"
10
type=
"PageControllerBase"
11
assignTo=
"{!pageController}"
12
required=
"true"
13
description=
"The controller for the page."
/
>
14
15
<apex:outputPanel
layout=
"block"
style=
"font-size: 12pt; border: 1pt solid black; width: 250px;"
>
16
<center>
17
<apex:outputText
value=
"This is in the component."
/
>
18
<br/
>
19
<apex:outputText
value=
"initial int value: {!intValue}"
/
>
20
</center>
21
</apex:outputPanel
>
22
</apex:component
>
As you can see I added a new attribute that will consume the key and assign the key to a property of the componentController named "Key".
Modify the ComponentControllerBase like this:
01
public
with sharing virtual
class
ComponentControllerBase {
02
//the new property
03
public
String
key{
get
;
04
set{
05
if
(value !=
null
){
06
Key
=
value;
07
if
(pageController !=
null
)
08
pageController.setComponentControllerMap(Key,
this
);
09
}
10
}
11
}
12
13
public
PageControllerBase pageController {
get
;
14
set {
15
if
(value !=
null
) {
16
pageController
=
value;
17
pageController.setComponentController(
this
);
18
}
19
}
20
}
21
}
Basically what is going on here is that "key" attribute set on the component placed on the page is assigned to the property named "Key". We get the key and use it to store the component controller in a hashmap so we now have access to it from the page controller using the appropriate key.
Now we can place multiple components of different types on the page, in this case the same type on the same page like this:
01
<apex:page
controller=
"MyPageController"
showHeader=
"false"
>
02
<center>
03
<apex:outputPanel
layout=
"block"
style=
"font-size: 16pt; margin-top: 50px; width: 400px"
>
04
<h2>
Sample Illustrating How to Establish Communication Between Page and Component
</h2>
05
</apex:outputPanel
>
06
<apex:outputPanel
layout=
"block"
style=
"width: 300px;"
>
07
<hr/
>
08
<h1>
Component Shown below:
</h1>
<hr/
>
09
<!-- take notice of the
new
attribute
in
use --
>
10
<
c:MyComponent
pageController=
"{!this}"
key=
"component1"
/
>
11
<hr/
>
12
<br/
>
<br/
>
13
<!-- take notice of the
2
nd component --
>
14
<
c:MyComponent
pageController=
"{!this}"
key=
"component2"
/
>
15
<apex:form
>
16
<apex:commandButton
style=
"font-size: 12pt; color: black"
17
action=
"{!callComponent1ControllerMethod}"
18
value=
"Call Component Controller Method for first component"
19
rerender=
"output"
/
>
20
<br/
>
21
<apex:commandButton
style=
"font-size: 12pt; color: black"
22
action=
"{!callComponent2ControllerMethod}"
23
value=
"Call Component Controller Method for second component"
24
rerender=
"output"
/
>
25
<apex:outputText
value=
"Clicking the buttons
above
will call the
26
method on the component controllers to increment the initial
27
values
from
the components
above
." /
>
28
</apex:form
>
29
<apex:outputPanel
id=
"output"
style=
"font-size: 12pt;"
>
30
<center>
31
<b>
first:
</b>
<apex:outputText
value=
"{!myComponent1Controller.intValue}"
/
>
32
<hr/
>
33
<b>
first:
</b>
<apex:outputText
value=
"{!myComponent2Controller.intValue}"
/
>
34
<hr/
>
35
</center>
36
</apex:outputPanel
>
37
</apex:outputPanel
>
38
</center>
39
</apex:page
>
Self explanatory. The key is not mandatory so it will not affect older implementation.
You now have access to any of the component-controller on page-controller like this:
public
with sharing virtual
class
PageControllerBase {
02
03
private
ComponentControllerBase myComponentController;
04
05
public
virtual ComponentControllerBase getMyComponentController() {
06
return
myComponentController;
07
}
08
09
public
virtual
void
setComponentController(ComponentControllerBase compController) {
10
myComponentController
=
compController;
11
}
12
13
public
PageControllerBase getThis() {
14
return
this
;
15
}
16
17
}
Basically we are declaring a field compController to hold a reference to the component controller instance. We also set up a getter to return the instance of the page controller. Finally, we create a virtual method to set the compController. It is useful to make these virtual so that you can simply extend this class in your page controller and, if required, cast the component controller to a specific type (assuming that your component controller extends the class below).
The next step is to create a virtual class for the ComponentController something like this:
01
public
with sharing virtual
class
ComponentControllerBase {
02
03
public
PageControllerBase pageController {
get
;
04
set {
05
if
(value !=
null
) {
06
pageController
=
value;
07
pageController.setComponentController(
this
);
08
}
09
}
10
}
11
}
This class simply defines a property to contain the page controller. This value will be passed by using an attribute on the component markup. Again, these methods are defined as virtual in case you need special handling or casting for the page controller.
Once you have created these two virtual classes you can now create component controllers and page controllers that extend these classes. This will imbue your controller implementations with the ability to communicate with each other. Below is a sample implementation of a component controller.
01
public
with sharing
class
MyComponentController
extends
ComponentControllerBase {
02
03
private
Integer myValue
=
3
;
04
05
public
Integer getIntValue() {
06
return
myValue;
07
}
08
09
public
void
incrementValue() {
10
myValue++;
11
}
12
}
The code above is simply defining a getter and a method that will later be called by the page controller. The last method in the class is implementation specific to this controller.
The component markup is shown below.
01
<apex:component
controller=
"MyComponentController"
>
02
<apex:attribute
name=
"pageController"
03
type=
"PageControllerBase"
04
assignTo=
"{!pageController}"
05
required=
"true"
06
description=
"The controller for the page."
/
>
07
08
<apex:outputPanel
layout=
"block"
style=
"font-size: 12pt; border: 1pt solid black; width: 250px;"
>
09
<center>
10
<apex:outputText
value=
"This is in the component."
/
>
11
<br/
>
12
<apex:outputText
value=
"initial int value: {!intValue}"
/
>
13
</center>
14
</apex:outputPanel
>
15
</apex:component
>
The thing to pay close attention to is the definition of the pageController attribute. We are setting the type to PageControllerBase. This allows you to reference any controller that extends the PageControllerBase class.
The page controller implementation is shown below.
01
public
with sharing
class
MyPageController
extends
PageControllerBase {
02
03
public
MyComponentController myComponentController {
get
; set; }
04
05
public
override
void
setComponentController(ComponentControllerBase compController) {
06
myComponentController
=
(MyComponentController)compController;
07
}
08
09
public
override ComponentControllerBase getMyComponentController() {
10
return
myComponentController;
11
}
12
13
public
PageReference callComponentControllerMethod() {
14
myComponentController.incrementValue();
15
return
null
;
16
}
17
}
In this implementation we are overriding the setComponentController and getMyComponentController methods. We do this so that we can cast the component controller to the specific implementation that we are creating and also reference the MyComponentController declared field - myComponentController.
In addition to the overrides there is an implementation specific action method that calls the incrementValue method of the component controller implementation.
Finally, the page markup that uses this controller is shown below.
01
<apex:page
controller=
"MyPageController"
showHeader=
"false"
>
02
<center>
03
<apex:outputPanel
layout=
"block"
style=
"font-size: 16pt; margin-top: 50px; width: 400px"
>
04
<h2>
Sample Illustrating How to Establish Communication Between Page and Component
</h2>
05
</apex:outputPanel
>
06
<apex:outputPanel
layout=
"block"
style=
"width: 300px;"
>
07
<hr/
>
08
<h1>
Component Shown below:
</h1>
<hr/
>
09
<
c:MyComponent
pageController=
"{!this}"
/
>
10
<hr/
>
11
<br/
>
<br/
>
12
<apex:form
>
13
<apex:commandButton
style=
"font-size: 12pt; color: black"
14
action=
"{!callComponentControllerMethod}"
15
value=
"Call Component Controller Method"
16
rerender=
"output"
/
>
17
<br/
>
18
<apex:outputText
value=
"Clicking the button
above
will call the
19
method on the component controller to increment the initial
20
value
from
the component
above
." /
>
21
</apex:form
>
22
<apex:outputPanel
id=
"output"
style=
"font-size: 12pt;"
>
23
<center>
24
<apex:outputText
value=
"{!myComponentController.intValue}"
/
>
25
<hr/
>
26
</center>
27
</apex:outputPanel
>
28
</apex:outputPanel
>
29
</center>
30
</apex:page
>
The sample page simply displays the initial value held by the component controller using the component and provides a button to call the action method on the page controller to exercise the incrementValue method on the component controller.
Here is a link to a page that implements the technique shown above.
http://ls-developer-edition.na6.force.com/MyPage
Cheers
Extended to support multiple visual force(vf) components on a vf page by Kunle Adeniyi
Fantastic work by the previous developer(Can you put your name in here). When I came across this page I was very impressed and inspired. I started using this pattern immediately but then I quickly ran into a road block. I needed to have multiple components on a visualforce page and I needed to be able to access those components from the page controller. So I added a couple of methods to the ComponentControllerBase class, PageControllerBase, and the component markup to achieve this. See below
Modify the PageControllerBase like this:
01
public
with sharing virtual
class
PageControllerBase {
02
03
private
ComponentControllerBase myComponentController;
04
private
Map<
String
, ComponentControllerBase
>
componentControllerMap;
//new
05
06
public
virtual ComponentControllerBase getMyComponentController() {
07
return
myComponentController;
08
}
09
10
//new getter for the hashmap
11
public
virtual Map<
String
, ComponentControllerBase
>
getComponentControllerMap(){
12
return
componentControllerMap;
13
}
14
//new method for putting value in the hashmap
15
public
virtual
void
setComponentControllerMap(
String
key, ComponentControllerBase compController){
16
if
(componentControllerMap
=
=
null
)
17
componentControllerMap
=
new
Map<
String
, ComponentControllerBase
>
();
18
componentControllerMap.put(key,compController);
19
}
20
21
public
virtual
void
setComponentController(ComponentControllerBase compController) {
22
myComponentController
=
compController;
23
}
24
25
public
PageControllerBase getThis() {
26
return
this
;
27
}
28
29
}
Basically what I have done here is to add a hashmap that will store each componentController, this way in the page controller I can get the controller of a specific component using the appropriate key.
Modify the component mark up like this:
01
<apex:component
controller=
"MyComponentController"
>
02
03
<!-- the
new
attribute --
>
04
<apex:attribute
name=
"key"
05
type=
"String"
06
assignTo=
"{!Key}"
07
description=
"The key given to this component so the page can easily get access to it"
/
>
08
09
<apex:attribute
name=
"pageController"
10
type=
"PageControllerBase"
11
assignTo=
"{!pageController}"
12
required=
"true"
13
description=
"The controller for the page."
/
>
14
15
<apex:outputPanel
layout=
"block"
style=
"font-size: 12pt; border: 1pt solid black; width: 250px;"
>
16
<center>
17
<apex:outputText
value=
"This is in the component."
/
>
18
<br/
>
19
<apex:outputText
value=
"initial int value: {!intValue}"
/
>
20
</center>
21
</apex:outputPanel
>
22
</apex:component
>
As you can see I added a new attribute that will consume the key and assign the key to a property of the componentController named "Key".
Modify the ComponentControllerBase like this:
01
public
with sharing virtual
class
ComponentControllerBase {
02
//the new property
03
public
String
key{
get
;
04
set{
05
if
(value !=
null
){
06
Key
=
value;
07
if
(pageController !=
null
)
08
pageController.setComponentControllerMap(Key,
this
);
09
}
10
}
11
}
12
13
public
PageControllerBase pageController {
get
;
14
set {
15
if
(value !=
null
) {
16
pageController
=
value;
17
pageController.setComponentController(
this
);
18
}
19
}
20
}
21
}
Basically what is going on here is that "key" attribute set on the component placed on the page is assigned to the property named "Key". We get the key and use it to store the component controller in a hashmap so we now have access to it from the page controller using the appropriate key.
Now we can place multiple components of different types on the page, in this case the same type on the same page like this:
01
<apex:page
controller=
"MyPageController"
showHeader=
"false"
>
02
<center>
03
<apex:outputPanel
layout=
"block"
style=
"font-size: 16pt; margin-top: 50px; width: 400px"
>
04
<h2>
Sample Illustrating How to Establish Communication Between Page and Component
</h2>
05
</apex:outputPanel
>
06
<apex:outputPanel
layout=
"block"
style=
"width: 300px;"
>
07
<hr/
>
08
<h1>
Component Shown below:
</h1>
<hr/
>
09
<!-- take notice of the
new
attribute
in
use --
>
10
<
c:MyComponent
pageController=
"{!this}"
key=
"component1"
/
>
11
<hr/
>
12
<br/
>
<br/
>
13
<!-- take notice of the
2
nd component --
>
14
<
c:MyComponent
pageController=
"{!this}"
key=
"component2"
/
>
15
<apex:form
>
16
<apex:commandButton
style=
"font-size: 12pt; color: black"
17
action=
"{!callComponent1ControllerMethod}"
18
value=
"Call Component Controller Method for first component"
19
rerender=
"output"
/
>
20
<br/
>
21
<apex:commandButton
style=
"font-size: 12pt; color: black"
22
action=
"{!callComponent2ControllerMethod}"
23
value=
"Call Component Controller Method for second component"
24
rerender=
"output"
/
>
25
<apex:outputText
value=
"Clicking the buttons
above
will call the
26
method on the component controllers to increment the initial
27
values
from
the components
above
." /
>
28
</apex:form
>
29
<apex:outputPanel
id=
"output"
style=
"font-size: 12pt;"
>
30
<center>
31
<b>
first:
</b>
<apex:outputText
value=
"{!myComponent1Controller.intValue}"
/
>
32
<hr/
>
33
<b>
first:
</b>
<apex:outputText
value=
"{!myComponent2Controller.intValue}"
/
>
34
<hr/
>
35
</center>
36
</apex:outputPanel
>
37
</apex:outputPanel
>
38
</center>
39
</apex:page
>
Self explanatory. The key is not mandatory so it will not affect older implementation.
You now have access to any of the component-controller on page-controller like this:
01
public
with sharing
class
MyPageController
extends
PageControllerBase {
02
03
public
MyComponentController myComponent
1
Controller {set;
04
get
{
05
//get the map, then the component controller using the appropriate key
06
if
(getComponentControllerMap()!=
null
){
07
MyComponentController mcc;
08
mcc
=
(MyComponentController )getComponentControllerMap().
get
(
'component1'
);
09
if
(mcc!=
null
)
10
return
mcc;
11
}
12
//just in case something goes wrong return something...this can be handled better
13
return
new
MyComponentController();
14
}
15
}
16
public
MyComponentController myComponent
2
Controller {set;
17
get
{
18
//get the map, then the component controller using the appropriate key
19
if
(getComponentControllerMap()!=
null
){
20
MyComponentController mcc;
21
mcc
=
(MyComponentController )getComponentControllerMap().
get
(
'component2'
);
22
if
(mcc!=
null
)
23
return
mcc;
24
}
25
//just in case something goes wrong return something...this can be handled better
26
return
new
MyComponentController();
27
}
28
}
29
30
31
32
public
PageReference callComponent
1
ControllerMethod() {
33
myComponent
1
Controller .incrementValue();
34
return
null
;
35
}
36
public
PageReference callComponent
2
ControllerMethod() {
37
myComponent
2
Controller .incrementValue();
38
return
null
;
39
}
40
}
01
public
with sharing
class
MyPageController
extends
PageControllerBase {
02
03
public
MyComponentController myComponent
1
Controller {set;
04
get
{
05
//get the map, then the component controller using the appropriate key
06
if
(getComponentControllerMap()!=
null
){
07
MyComponentController mcc;
08
mcc
=
(MyComponentController )getComponentControllerMap().
get
(
'component1'
);
09
if
(mcc!=
null
)
10
return
mcc;
11
}
12
//just in case something goes wrong return something...this can be handled better
13
return
new
MyComponentController();
14
}
15
}
16
public
MyComponentController myComponent
2
Controller {set;
17
get
{
18
//get the map, then the component controller using the appropriate key
19
if
(getComponentControllerMap()!=
null
){
20
MyComponentController mcc;
21
mcc
=
(MyComponentController )getComponentControllerMap().
get
(
'component2'
);
22
if
(mcc!=
null
)
23
return
mcc;
24
}
25
//just in case something goes wrong return something...this can be handled better
26
return
new
MyComponentController();
27
}
28
}
29
30
31
32
public
PageReference callComponent
1
ControllerMethod() {
33
myComponent
1
Controller .incrementValue();
34
return
null
;
35
}
36
public
PageReference callComponent
2
ControllerMethod() {
37
myComponent
2
Controller .incrementValue();
38
return
null
;
39
}
40
}
No comments:
Post a Comment