Ajax (1) Apex Class (12) Apex Trigger (2) Community (2) Home Page (1) HTML (4) Integration (3) JS (7) KB (1) Label (1) Licenses (1) Listing (1) Log (1) OOPs (5) Sharing (1) Static Resource (1) Test Class (3) URI (1) Visualforce (10)

Tuesday 22 April 2014

component

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 2nd 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 2nd 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 myComponent1Controller {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 myComponent2Controller {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 callComponent1ControllerMethod() {
33     myComponent1Controller .incrementValue();
34     return null;
35   }
36   public PageReference callComponent2ControllerMethod() {
37     myComponent2Controller .incrementValue();
38     return null;
39   }
40 }

view sourceprint?
01 public with sharing class MyPageController extends PageControllerBase {
02  
03   public MyComponentController myComponent1Controller {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 myComponent2Controller {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 callComponent1ControllerMethod() {
33     myComponent1Controller .incrementValue();
34     return null;
35   }
36   public PageReference callComponent2ControllerMethod() {
37     myComponent2Controller .incrementValue();
38     return null;
39   }
40 }


No comments:

Post a Comment