Given a Lightning Component with a custom renderer, is it possible to arbitrarily attach another Lightning Component (for example, a lightning:button
component) to the DOM generated by the parent component's custom renderer?
I know that it is possible to attach dynamically created components to other components defined in markup by setting the v.body
property, but when using a custom renderer there does not appear to be a v.body
property for elements created using the document.createElement
function. Theoretically, I could use the child component's getElement
function, except that I need to force the child component to render before I can access the getElement
function.
Following is a super-simplified example. Note that the example does not justify the dynamic button creation or the custom renderer - that's not the question. The question is, given the following, is it possible to somehow force the button to render, or otherwise attach it to the DOM created by the parent component's custom renderer?
Component Markup
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,forceCommunity:availableForAllPageTypes,force:hasRecordId" access="global">
<aura:attribute name="dynamicButtons" type="Aura.Component[]"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
</aura:component>
Component Controller
({
'doInit' : function(cmp) {
$A.createComponents([
["lightning:button", {
"aura:id": "dynamicButton",
"label": "Press Me",
"onclick": cmp.getReference("c.handlePress")
}],
],
function(newButtons, status, errorMessage){
if (status === "SUCCESS") {
//Add the new button to the body array
cmp.set('v.dynamicButtons', newButtons[0]);
}
}
);
},
'handlePress' : function(cmp) {
console.log("button pressed");
}
})
Component Renderer
({
'render' : function(component) {
this.superRender();
var mainElement = document.createElement('div'),
spanElement = document.createElement('span');
spanElement.innerText = 'Hello World.'
mainElement.appendChild( spanElement );
// Note that this is a super-simplified custom renderer.
// In reality, there are many third-party libraries that
// generate the DOM which cannot be easily converted to
// generate lightning components.
component.mainElement = mainElement;
return mainElement;
},
'afterRender' : function(component){
var dynamicButton = component.get('v.dynamicButtons')[0];
// How do I render the dynamic button within my div, after the span?
}
})
Out of curiosity, I decided to look at how aura was handling low-level rendering for its own components.
Aura documentation hints at the existence of a
renderingService
(see the "js://renderer" tab), but it does not appear to be available in Lightning, (at least in Locker Service).The
renderingService
has arender()
function which, given an array of components, will call therender()
function on each component it's given and, if supplied, will append the low-level DOM elements directly to a parent DOM element. Again, though, the renderingService appears to be a low-level service that is not available to custom Lightning Component developers.It looks like maybe the
renderingService.renderFacet()
is the real key, here, as it both handles the actual rendering of the low-level DOM for a child component and associates the child component with the parent component so that unrendering behaves correctly. Maybe. If I'm reading the code right.
document.createElement
which can have lightning components as children.