Newer Version Available
Manage Synchronous Action Dependencies
Hidden Dependencies in a Canvas Component
When a child component calls hidden lifecycle actions, timing errors can occur more frequently with dynamic boxcar optimization. For example, this Aura component uses the fetchCanvasParameters action to retrieve values from a server-side Apex controller in its init handler, and then passes the retrieved values as parameters to a child component.
1<!-- canvasExample.cmp -->
2<aura:component controller="CanvasExampleController" >
3 <aura:attribute name="canvasParameters" type="Map"/>
4 <aura:handler name="init" value="{!this}"
5 action="{!c.fetchCanvasParameters}"/>
6
7 <force:canvasApp developerName="{!v.AppName}" scrolling="auto"
8 width="100%" height="100%" title="{!v.title}"
9 parameters="{!v.canvasParameters}" />
10</aura:component>Behind the scenes, the child <force:canvasApp> component calls its own lifecycle action getCanvasAppData to retrieve the Canvas app’s metadata. The order of these two action calls—fetchCanvasParameters and getCanvasAppData—isn’t obvious because the call to getCanvasAppData is implicit. More importantly, the order in which they return isn’t guaranteed. With standard boxcar grouping, both calls were usually grouped together in the same boxcar, which usually ensured that they completed in the correct order.
With dynamic boxcar optimization, the framework has more flexibility in how it groups actions into boxcars, using more XHR slots to send actions separately to avoid bottlenecks. It's much more likely that the two actions are sent in different boxcars. The use of separate boxcars greatly increases the possibility that the <force:canvasApp> component is instantiated and makes another call in another boxcar before the component parameters are returned in the parent component’s init handler. As you can imagine, this change in sequence can cause any number of problems.
The simplest and easiest way to resolve this issue is to conditionally render the <force:canvasApp> component only if the initial response values are available, that is, after the fetchCanvasParameters action has completed.
1<!-- canvasExampleFixed.cmp -->
2<aura:component controller="CanvasExampleController" >
3 <aura:attribute name="canvasParameters" type="Map"/>
4 <aura:handler name="init" value="{!this}"
5 action="{!c.fetchCanvasParameters}"/>
6
7 <aura:if isTrue="{!v.canvasParameters}">
8 <force:canvasApp developerName="{!v.AppName}" scrolling="auto"
9 width="100%" height="100%" title="{!v.title}"
10 parameters="{!v.canvasParameters}" />
11 </aura:if>
12
13</aura:component>By wrapping the child <force:canvasApp> component in an <aura:if> block, the creation of the <force:canvasApp> component is deferred until after the call to the fetchCanvasParameters action completes.
For completeness, here’s the Aura component helper, which contains the fetchCanvasParameters action, and the server-side Apex controller method that returns the Canvas app parameters that the child <force:canvasApp> component can use.
1# canvasExampleHelper.js
2({
3 fetchCanvasParameters : function(component, event, helper) {
4 var action = component.get("c.getCanvasParameters");
5 action.setCallback(this, function(response) {
6 var state = response.getState();
7 if (state === "SUCCESS") {
8 component.set("v.canvasParameters",
9 response.getReturnValue());
10 }
11 });
12 $A.enqueueAction(action);
13 }
14})1# CanvasExampleController.apex
2public with sharing class CanvasExampleController {
3 @AuraEnabled
4 public static Map<String, Object> getCanvasParameters() {
5 // Example: return some parameters
6 Map<String, Object> params = new Map<String, Object>();
7 params.put('param1', 'value1');
8 params.put('param2', 123);
9 return params;
10 }
11}