+ Start a Discussion
parkerAPTparkerAPT 

Order of execution during AJAX postback and child-component rerendering

I'm struggling with the order-of-execution during a VisualForce AJAX postback request and the appropriate way to nest-components that might need to rerender each other.

 

The documentation (http://www.salesforce.com/us/developer/docs/pages/Content/pages_controller_postback_request.htm ) has 4 key steps:

 

  1. View Deserialization
  2. Evaluate expressions and all method calls including those on custom component controllers
  3. Evaluate the action that triggered the postback
  4. Render view -> HTML sent to browser

 

I'm getting frustrated by the second step, specifically all method calls.  From what it appears, any public methods (specifically a getter) that are used in a view are called before the action method gets call even if these methods are not binding anything in the View State.

 

The example below illustrates that getDisplayedList method gets called immediately after view state deserialization and then again during the rendering of the view.  In my toy example, I lazy-load a list of strings based on the iCount member variable.  In my real-world application I'm actually using a SOQL query to dynamically fetch a list of S-Objects to display to the user. 

 

 

<apex:page controller="LazyLoadController">

<apex:form id="theWrapper">
	<apex:repeat value="{!DisplayedList}" var="s">
		{!s}
	</apex:repeat>
	
	<apex:commandLink value="refresh" rerender="theWrapper" />
	
	<div>
	iCount : <b>{!iCount}</b>
	</div>
</apex:form>

</apex:page>

public with sharing class LazyLoadController {

	public LazyLoadController(){
		this.iCount = 5;
	}

	public Integer iCount {get; set;}
	
	private Transient List<String> t_theList;
	
	// I want to be able to gurantee getDisplayedList() only gets executed during the rendering of the Component and not during "Postback" validation
	public List<String> getDisplayedList(){
		if(this.t_theList == null){
			// The real use case would Query the Database for the appropriate objects.
			
			this.t_theList = new List<String>();
			for(Integer i=0 ; i< this.iCount; i++){
				this.t_theList.add('[' + String.valueOf(i) + ']');
			}
		}
		iCount++;
		return this.t_theList;
	}
	
	
	public PageReference refresh(){
		// In theory I want to be able to execute code here that is guranteed to be executed before getDisplayedList
		return null;
	}
}

 

Unfortuntaely, because getDisplayedList gets called before I execute any code in my post-back method, I do not have the opportunity to do any additional logic before the Transient list of strings ( t_theList) gets initalized.

 

One quick and obvious solution would be to set the Transient list to null in the postback method and force the list to be regenerated or remove the lazy-loading indirection and initalize the list directly.

 

I typically would do this except for the fact I want this controller to be a component-controller that can be easily dynamically rerendered.  I've outlined the at the high-level what I want to do below.

 

 

<apex:page controller="ParentController">
	<!-- Code that will create a new Task and 
		re-render the component that should display 
		a list containing the newly created task 
		-->
	<apex:commandLink value="addNewTaskAndRefreshList" rerender="componentWrapper" />
	
	<apex:outputPanel id="componentWrapper">
		<c:lazyLoadComponent 
			countOfTasks=5/>
		</c:lazyLoadComponent>
	</apex:outputPanel>
	
</apex:page>

 

public with sharing class ParentController {
	public PageReference addNewTaskAndRefreshList(){
		Task t = new Task();
		//...
		insert t;
		return null;
	}
}

 

 

Pretend for example, that my LazyLoadController is now displaying a list of tasks that are dynamically loaded from the database.  I'd like to be able to put this component onto my page and re-render it so that it dynamically refreshes the list of Tasks.  For example, in the ParentController I want to insert a new task, refresh the child-component and display the new task.  Currently, the Lazy-Loading happens in Step 2 (method calls) of the order of execution before Step 3 (Invoke PostBack method).

 

Does anyone have a suggestion for how I can re-design my code so that I can achieve the above behavior?

 

Thanks

Parker

 

wesley.noltewesley.nolte

Hey,

 

I think I've understood. One way to do it - that isn't elegant but works - is to use a boolean flag that's "false" until it's set to true by your refresh() method. If you then wrap your repeat in an outputPanel for example and set "rendered={!flag}" then the Apex:Repeat statement will not load at all and your getter won't be called.

 

There's another more complex but more elegant solution where the Component Controller and Page Controller can communicate directly with eachother but it's probably overkill for this problem.

 

Wes

parkerAPTparkerAPT

Thanks Wes.

You're right, one option that works fairy well is adding a Transient Boolean to the ChildController.  In the getItems method we can reload the items if this boolean is set to true.  If I add a default attribute to the component file and set this boolean on the controller we can force the controller to reload the items during rendering.  Because I believe we are guaranteed that the attributes / assignTo for a component get setup before the rendering of the page occur, we can use this flag to effectively notify the controller that the child-component is starting to render and we should re-query or regenerate the list.(I can easily modify the example below to have these changes if I'm not being clear)

 

I'm providing a more complete example that illustrates my challenges so that others can basically copy and paste into the appropriate controllers and components and see my problem.

 

 

public with sharing class ParentController {
	public static String ContactFirstNamePrefix = 'Parent-Child Demo';
	
	public ParentController(){
		this.iCount = 0;
	}

	public Integer iCount {get; set;}
	
	public PageReference createContact(){
		// In theory I want to be able to execute code here that is guranteed to be executed before getDisplayedList
		this.iCount++;
		Contact c = new Contact();
		c.FirstName = ParentController.ContactFirstNamePrefix + ' - ' + String.valueOf(this.iCount);
		c.LastName  = ParentController.ContactFirstNamePrefix + ' Last Name';
		insert c;
		
		return null;
	}
}

 

<apex:page controller="ParentController">
	<apex:form >
		<div>
		<apex:commandLink value="createContact" rerender="childWrapper" action="{!createContact}" status="inprogress"/>
		<apex:actionStatus id="inprogress">
			<apex:facet name="start">
				<b>Creating ....</b>
			</apex:facet>
		</apex:actionStatus>
		</div>
	</apex:form>
		
		<apex:outputPanel id="childWrapper">
			num-contacts created : {!iCount}
			<c:ChildComponent >
			</c:ChildComponent>
		</apex:outputPanel>
</apex:page>

 

public with sharing class ChildController {

	private Transient List<Contact> t_theList;
	
	public Transient ID ContactId { get; set; }
	
	public ChildController(){
	}
	
	// I want to be able to gurantee getDisplayedList() only gets executed during the rendering of the Component and not during "Postback" validation
	public List<Contact> getDisplayedList(){
		if(this.t_theList == null){
			// Lazy-Load the list of contacts
			this.t_theList = [SELECT Id, Name FROM Contact WHERE FirstName like :ParentController.ContactFirstNamePrefix + '%' ORDER BY CreatedDate DESC ];
		}
		return this.t_theList;
	}

	public Transient Integer t_itemCount { get; private set; }
	// This method does not get called during the 2nd Stage of the Ajax Post back method (different from the above getDisplayedList method) 
	public Integer getDisplayedItemCount(){
		if(this.t_itemCount == null){
			// Lazy-Load the list of contacts
			this.t_itemCount = [SELECT COUNT() FROM Contact WHERE FirstName like :ParentController.ContactFirstNamePrefix + '%'];
		}
		return this.t_itemCount;
	}

	public PageReference deleteClickedContact(){
		system.debug(this.ContactId);
		if(this.ContactId != null){
			Contact [] c = [SELECT id from Contact where Id =:this.ContactId];
			system.debug(c);
			if(c.size() > 0){
				delete c.get(0);
			}
		}
		system.debug(this.t_theList);
		// In order for the displayed list to refresh we must explicitly set the transient list to NULL 
		// so that when refreshing the child component, the list gets refreshed
		// this.t_theList = null;
		return null;
	}
}

 

<apex:component controller="ChildController" allowDML="true">

	<apex:outputPanel id="ListWrapper">
		<apex:actionStatus id="delete-status">
			<apex:facet name="start">
			deleting ...
			</apex:facet>					
		</apex:actionStatus>
		
		<div>
			<p>
			Number of Contacts (DisplayedItemCount) : {!DisplayedItemCount}
			</p>
			vs
			<p>
			List Size (DisplayedList.size):  {!DisplayedList.size}
			</p>
		</div>
		
		<apex:form id="innerForm">
		<apex:repeat value="{!DisplayedList}" var="oContact">
			<div>{!oContact.Id} | {!oContact.Name} (
				<apex:commandLink value="delete" action="{!deleteClickedContact}" rerender="ListWrapper" status="delete-status">
					<apex:param name="ContactId" value="{!oContact.Id}" assignTo="{!ContactId}" />
				</apex:commandLink>
				)
			</div>
		</apex:repeat>
		</apex:form>
	</apex:outputPanel>

</apex:component>

 

This example highlights the following oddities:

 

  1. The Child Components getters: getDisplayedList and getDisplayedItemCount are called at different frequencies. The getDisplayedList method gets called both before the Action of the PostMethod and during rendering of the ChildComponent.  The getDisplayedItemCount only gets called during the rendering of the child component. I know this because the getDisplayedItemCount accurately shows the correct number of expected items while the getDisplayedList is always shows the previous list of Contacts (1 less after a create and 1 more after a delete)

 

I'd love to see a "Salesforce Design Pattern" Cookbook be released that discusses how to appropriate handle parent-child components, especially involving rerendering these components and message-passing between Child & Parent controllers.

 

Can anyone shed more light on the above example and provide feedback on how I'm handling child-parent component re-rendering?

Does anyone know why some getters (getDisplayedList) are called during the PostBack execution but others (getDisplayedItemCount ) are only called during rendering?

WesNolte__cWesNolte__c

Sorry I have two accounts logged in on different computers, but it's still me :)

 

I hate to say this but there are bugs when combining components and reRendering. I've seen them a few times myself, and especially when some type of iterating component is used. I've had strange situations where methods are called twice when a component is loaded even if it make one appearance in a page. I've logged cases for these situations but they've never been resolved. All I can do is offer some information that might be able to help you pinpoint your problem.

 

1. You shouldn't be using more than 1 form in a page, it screws with the viewstate. The solution is to use actionRegions as shown here: http://www.tgerm.com/2010/09/visualforce-actionregion-deep-dive.html

2. You can use inheritance to get a page and component's controllers to be directly aware of one another: 

http://th3silverlining.com/2009/10/01/oop-in-the-cloud-recaptcha-revisited/

 

Cheers,

Wes