function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
DharshniDharshni 

Using same Visualforce components twice

I am trying to use a same VF component more than once in a VF page. I can see that the last used component alone works fine and the others give no response. 

 

Could anyone please tell if there is a problem in using the same component twice? 

 

Below are the generic code with standard objects for your convenience:

 

Component :

 <apex:component controller="ContactEmailController">

<apex:actionFunction action="{!dispFunc}" name="callFunc"/>
<apex:inputField value="{!opp.accountId}" onchange="callFunc();"/>
{!accnt.Name}
</apex:component>

 

Component Controller :

public class ContactEmailController {


public Account accnt{get;set;}
public Opportunity opp{get;set;}

public PageReference dispFunc(){
if(opp.AccountId!=null)
accnt = [select id, name from Account where id=:opp.AccountId];
return null;
}

public ContactEmailController(){
accnt = new Account();
opp = new Opportunity();
}
}

 

Page using the component :

<apex:page >
<apex:form >
<c:ContactEmail />
</apex:form>
<apex:form >
<c:ContactEmail />
</apex:form>
</apex:page>

 

I am trying to get this working for a long time now. Please help.

Thanks in advance. 

 

Best Answer chosen by Admin (Salesforce Developers) 
bob_buzzardbob_buzzard

I wouldn't expect the same controller instance to be shared across multiple custom components - it kind of breaks the reusability.

 

I've set this up in my dev org and can confirm that it is the conflicting actionfunction names.  Regardless of how many times you use this component, there is only one callFunc javascript function.  So when you fill in the details in the first one, the actionfunction associated with the final one fires, but the input field is empty so the account id will be null.

 

You need to allow the functions to be distinguishable in javascript - I've changed this as follows and its working for me:

 

Component:

 

<apex:component controller="ContactEmailController">
<apex:attribute name="idx" type="string" description="Unique index for this component in the page" />
<apex:actionFunction action="{!dispFunc}" name="callFunc{!idx}"/>
<apex:inputField value="{!opp.accountId}" onchange="callFunc{!idx}();"/>
{!accnt.Name}
</apex:component>

 

Page:

 

<apex:page >
<apex:form >
<c:ContactEmail idx="1"/>
</apex:form>
<apex:form >
<c:ContactEmail idx="2"/>
</apex:form>
</apex:page>

 

 

 

All Answers

bob_buzzardbob_buzzard

I wonder if its related to the callFunc actionfunction - as they all have the same name, there may be a conflict there.  Have you tried passing the function name as an attribute to the component?

DharshniDharshni

Hi bob

 

What I am trying to acheive is, when an account is selected, its name is displayed near the inputfield. I want this to happen independently in each instance of the component. 

 

The thing is, when I use the component only once,it works well. But when I use it more than once, the last instance alone works fine and the previous ones are not displaying the chosen account's name. 

 

The debug log for the previous component instances, shows that the opportunity's account is null inside the actionfunction. 

 

Is this a problem related to the component-controller's instance conflict? Please advise me on this. 

 

Thanks a lot.

bob_buzzardbob_buzzard

I wouldn't expect the same controller instance to be shared across multiple custom components - it kind of breaks the reusability.

 

I've set this up in my dev org and can confirm that it is the conflicting actionfunction names.  Regardless of how many times you use this component, there is only one callFunc javascript function.  So when you fill in the details in the first one, the actionfunction associated with the final one fires, but the input field is empty so the account id will be null.

 

You need to allow the functions to be distinguishable in javascript - I've changed this as follows and its working for me:

 

Component:

 

<apex:component controller="ContactEmailController">
<apex:attribute name="idx" type="string" description="Unique index for this component in the page" />
<apex:actionFunction action="{!dispFunc}" name="callFunc{!idx}"/>
<apex:inputField value="{!opp.accountId}" onchange="callFunc{!idx}();"/>
{!accnt.Name}
</apex:component>

 

Page:

 

<apex:page >
<apex:form >
<c:ContactEmail idx="1"/>
</apex:form>
<apex:form >
<c:ContactEmail idx="2"/>
</apex:form>
</apex:page>

 

 

 

This was selected as the best answer
DharshniDharshni

Thanks a ton. I understand the point which I missed before. 

JamsieJamsie

Hi Bob,

 

I'm calling my actionFunction via javascript.

 

I have updated my actionFunction to append a string to the base-name.

 

Is there any way to call the dynamically constructed actionFunction name from within the javascript?

 

<script>
//Call the init controller method when the page has loaded.
window.onload=function(){
  init();		}
</script>

<apex:actionFunction name="doInit{!theBusinessEntity}" action="{!init}" rerender="tablePanel"/>

 Many thanks,

James.

bob_buzzardbob_buzzard

If you use the same mechanism to construct the javascript call, that should work.  The merge fields are replaced before the javascript is sent back to the page, so the names will be literals when the javascript engine runs.

JamsieJamsie

Hi Bob,

 

thanks for the super quick reply.  That worked - i.e. I can call the init controller method via the javascript.

 

It seems that I'm only calling init once though.  My guess is that it's because my method is being called on the onload event and that only runs once all the components have been loaded on the page.

 

Is there an equivalent event that fires after each component has loaded?

 

Thanks again,

James.

bob_buzzardbob_buzzard

No, because the components aren't loaded individually.  They are processed individually on the server side, but they are all returned to the page in one go.

JamsieJamsie

Thanks Bob.

 

Plan b it is then.

 

Cheers,

James.

dcarmen66dcarmen66

I'm experiencing a similar issue - I basically defined a component as a "day" and I'm trying to display multiple days on a VF page. If I add in more than one day, the page won't re-render (based on actionSupport from the main page) . The component does not have any action function, it just renders a table. If I only include one instance of this the pageBlock rerenders without an issue.

 

A little update on this: The problem for me seems to the the <apex:repeat>. If I comment out the repeat, everything else works. If I uncomment the repeat, even with nothing between the tags, then the page does not refresh.

 

<apex:repeat value="{!dayInfo}" var="userInfo" >

</apex:repeat>

 

causes the page to not refresh.

 

 

<apex:component >

   <apex:attribute name="dayDesc"
      type="String"
      required="true"
      description="The long form of the day" />
   <apex:attribute name="dayInfo" 
      type="CalendarDayOutput[]" 
      required="true" 
      description="The rep detail" />

      <apex:pageBlockSection columns="1">
      
      <style type="text/css">
        .tableStyle {border-collapse:collapse;border: 1px solid black; width:80%;padding:0px;spacing:1px;}
        .nameHeaderCell { font-weight: bold;padding:0px;width:19%; color:blue;}
        .timeHeaderCell { padding:0px;width:9%; }
        .freeCell { padding:0px;height:8px;background-color:white;}
        .busyCell { padding:0px;height:8px;background-color:#FA5858; font-weight: bold}
        .tableRowHeader { vertical-align:center;padding:0px;padding-bottom:0px }
        .tableRow { vertical-align:center;padding:0px;border-top: #BDBDBD solid thin; padding-bottom:0px}
      </style>
         
         <table class="tableStyle" cellPadding="0" cellSpacing="1" >
         <caption><center><h1>{!dayDesc}</h1></center></caption>
         <tr class="tableRowHeader">
             <th class="nameHeaderCell">Rep Name</th>
             <th class="timeHeaderCell">8AM</th>
             <th class="timeHeaderCell">9AM</th>
             <th class="timeHeaderCell">10AM</th>
             <th class="timeHeaderCell">11AM</th>
             <th class="timeHeaderCell">12PM</th>
             <th class="timeHeaderCell">1PM</th>
             <th class="timeHeaderCell">2PM</th>
             <th class="timeHeaderCell">3PM</th>
             <th class="timeHeaderCell">4PM</th>
         </tr>
            <apex:repeat value="{!dayInfo}" var="userInfo" id="userCalendarRows">
               <tr class="tableRow">
               <td>{!userInfo.userName}</td>
               <td class="{!IF(userInfo.isBusy08am,'busyCell','freeCell')}">&nbsp;</td>
               <td class="{!IF(userInfo.isBusy09am,'busyCell','freeCell')}">&nbsp;</td>
               <td class="{!IF(userInfo.isBusy10am,'busyCell','freeCell')}">&nbsp;</td>
               <td class="{!IF(userInfo.isBusy11am,'busyCell','freeCell')}">&nbsp;</td>
               <td class="{!IF(userInfo.isBusy12pm,'busyCell','freeCell')}">&nbsp;</td>
               <td class="{!IF(userInfo.isBusy01pm,'busyCell','freeCell')}">&nbsp;</td>
               <td class="{!IF(userInfo.isBusy02pm,'busyCell','freeCell')}">&nbsp;</td>
               <td class="{!IF(userInfo.isBusy03pm,'busyCell','freeCell')}">&nbsp;</td>
               <td class="{!IF(userInfo.isBusy04pm,'busyCell','freeCell')}">&nbsp;</td>
               </tr>
            </apex:repeat>
         </table>
      </apex:pageBlockSection>



</apex:component>

 

 

dcarmen66dcarmen66

I fixed my issue by removing the pageBlockSection tags from my component. Not sure why, but it works now.

niklas1.392882504069509E12niklas1.392882504069509E12

I've have tried to do this for a custom component inside a apex:repeat: http://salesforce.stackexchange.com/questions/28366/custom-component-lookup-contact

However the inputfields on the component page seams to be unable to map a the value to the component controller.

<apex:outputpanel id="panelBlock">
    {!temporary}
        <apex:pageBlock mode="maindetail" title="Add {!policyCap.productUser.pe.Display_name__c}">
              <apex:pageBlockSection >
                   <apex:actionFunction action="{!addNewUser}" name="newUser{!uniqueID}" rerender="panelBlock">
                         <apex:param name="x" value="x" assignTo="{!temporary}"/>
                   </apex:actionFunction>
                           <apex:inputText label="String" value="{!temporary}" onchange="newUser{!uniqueID}('Temporary string: {!temporary} Id {!uniqueID}');"/>
              </apex:pageBlockSection>
         </apex:pageBlock>
    </apex:outputpanel>
</apex:outputpanel>

 

The result for  rendering of {!temporary} when a value is entered text into the <apex:inputText > show the correct ID of the component but the string is always empty....

niklas1.392882504069509E12niklas1.392882504069509E12

Instead of using an apex:inputtext i did the following:

<input id="theTextInput{!uniqueID}" type="text" value="{!temporary}" name="theTextInput" onchange="newUser{!uniqueID}(document.getElementById('theTextInput{!uniqueID}').value);">
</input>

This updates the controller value i.e temporary, and the rerendering of the value does it correct.


But shouldn't this be possible to do this using apex components???

naresh reddy 18naresh reddy 18
Yes the mentioned solution worked for me. It saved me from frustration. Thanks