Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

I want to create a page like this with 2 components:

<apex:page standardController="Campaign">
         <c:import campaign="{!campaign}" />
          <c:message campaign="{!campaign}" />
    </apex:page>

and for example in the component I want to render something like this:

<apex:component controller="ImportCampaignController" allowDML="true">  <apex:attribute name="campaign" description="This is the value of the campaign" type="Campaign" assignTo="{!currentCampaign}" required="true"/>  <apex:pageBlock title="Sync status" >
<apex:form >
  <apex:outputPanel rendered="{!currentCampaign.Sync__c == false}">
    <apex:commandButton value="Import" action="{!importCampaignMember}" />
  </apex:outputPanel></apex:form> 
</apex:pageBlock></apex:component>

But obviously the visualforce complains about: SObject row was retrieved via SOQL without querying the requested field: Campaign.Sync__c

One solution might be to add the required fields on the visualforce page with some apex elements not rendered but it's not a clean solution. There is the addFields method on standardController but here is useless...

share|improve this question
add comment

3 Answers 3

up vote 3 down vote accepted

Fields can only be queried automatically via a StandardController on an apex:page. Even though apex:include is unavilable in apex:component, you can cheat by moving your component body to a page, then doing a poor-man's include (parameterized PageReference.getContent) from the component controller.

Then, communicate the Campaign Id into your component so it can be requeried. Here's an example:

  • the Campaign Name is queried from the Page
  • the Campaign Status is queried from the Component

poor man's include

CustomCampaign.page

<apex:page standardController="Campaign">
  <apex:sectionHeader title="{!Campaign.Name}" />
  <c:importCampaign campaignId="{!Id}" />
</apex:page>

ImportCampaign.component

<apex:component controller="ImportCampaignController">
  <apex:attribute name="campaignId" type="Id" description="Id" assignTo="{!IdParam}" />
  <apex:outputText escape="false" value="{!Include}" />
</apex:component>

ImportCampaignController.cls

public class ImportCampaignController {

  public Id IdParam {get; set;}

  public String getInclude() {
    System.PageReference pageReference = Page.ImportCampaignBody;
    pageReference.getParameters().put('id', this.IdParam);
    return pageReference.getContent().toString();
  }

}

ImportCampaignBody.page

<apex:page standardController="Campaign">
  <apex:sectionHeader title="Status: {!Campaign.Status}" />
</apex:page>

(special thanks to Andrew Fawcett for sanity checking and analyzing this and multiple other approaches)

share|improve this answer
    
I'll try this approach soon as possible thanks! –  Alberto Maianti Feb 26 '13 at 9:55
add comment

It is not ideal, but the best way that I know of would be to just add those fields as part of your original Visualforce page. Something like:

<apex:page standardController="Campaign">
    <c:import campaign="{!campaign}"/>
    <c:message campaign="{!campaign}"/>
    <div style="display:none;" id="fieldsToLoad">
        <apex:outputField value="${!Campaign.Sync__c}" rendered="false"/>
    </div>
</apex:page>

I realize you said you want to avoid doing this, but I believe you have to do this to load those fields. Another option would be to add a controller extension in which all you do is add the fields in the constructor. Something like:

<apex:page standardController="Campaign" extension="CampaignLoaderExtension">
    <c:import campaign="{!campaign}"/>
    <c:message campaign="{!campaign}"/>
</apex:page>


public class CampaignLoaderExtension{
    public Campaign campaign {get; set;};

    public myControllerExtension(ApexPages.StandardController stdController) {
        List<String> fields = new List<String>();
        fields.add('Sync__c');
        stdController.addFields(fields);
        this.campaign = (Campaign)stdController.getRecord();
    }
}

I believe that should work but I haven't tested running that code. I am just going off the documentation. Neither solution just works like you would hope though, but it does get the job done.

share|improve this answer
add comment

So if a solution with add these fields on the VF page is not clear and otherwise it does not work - only solution here would be to add an extra Campaign object which has been read from the database:

Main Page:

<apex:page standardController="Campaign">
    <!-- No need to add any parameters here because of standard controller -->    
    <c:import/>
</apex:page>

Controller:

public Campaign currentCampaign { get; set; }

public ImportCampaignController(ApexPages.StandardController controller){
    String myid = controller.getId();
    currentCampaign = [Select id, name, Sync__c From Campaign where id = :myid];
}

Conponent:

<apex:component controller="ImportCampaignController" allowDML="true">  
<apex:pageBlock title="Sync status" >
    <apex:form >
        <apex:outputPanel rendered="{!currentCampaign.Sync__c == false}">
            <apex:commandButton value="Import" action="{!importCampaignMember}" />
        </apex:outputPanel>
    </apex:form> 
</apex:pageBlock>
</apex:component>
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.