6

As per the doc:

The root-level tag of every component is aura:component. Every component inherits the body attribute from <aura:component>.

The body attribute has type Aura.Component[]. It can be an array of one component, or an empty array, but it's always an array.

The tag can contain tags, such as <aura:attribute>, <aura:registerEvent>, <aura:handler>, <aura:set>, and so on. Any free markup that is not enclosed in one of the tags allowed in a component is assumed to be part of the body and is set in the body attribute.

Setting the body content is equivalent to wrapping that free markup inside <aura:set attribute="body">

As given in the example, i tried this:

Component:

<aura:component>
    <div>Body part</div>
    <ui:button label="Push Me" press="{!c.printCmpBody}"/>
</aura:component>

Controller.js

({
    printCmpBody : function(cmp) {
        console.log("button pressed",cmp.get("v.body"); // got empty array
    },
})

Since the body attribute is implicitly set, I thought it would return array of 2 elements in it. But I got an empty array.

So I tried to set the body attribute explicitly using aura:set, i got the same result.

If I remove the <div/>,<ui:button/> and create it dynamically using $A.createComponent , i'd be doing this: cmp.set("v.body",dynCmps) where dynCmps contains the respective <div/>,<ui:button/> component instances, and it renders in the browser.

And if i do console.log("button pressed",cmp.get("v.body"); i get array with two elements.

So Including the markup <div/>,<ui:button /> alone, also does the same thing(renders in the browser), which means cmp.set("v.body") should have take place somewhere.So I'm wondering, why cmp.get("v.body") is returning empty array.

Is this the way component.get("v.body") suppose to work?

15

@Praveen, yes, this is totally normal. Here is what is happening:

When we say that the body attribute is defined on all components we mean that it's defined on the base <aura:component/> from which all components inherit. Here is a simplified version.

<aura:component/>
   <aura:attribute name="body" type="Aura.Component[]"/>
   {!v.body}
</aura:component>

As you can see, the base component outputs the body tag that it receives in its own body, otherwise, nothing would be rendered. Angular calls that process "transclusion", the inclusion of content from another context.

When you write this:

<aura:component>
    <div>Body part</div>
    <ui:button label="Push Me" press="{!c.printCmpBody}"/>
</aura:component>

You are setting the body tag of the parent component. Everything defined in your component is the "lexical definition", and it's passed to its parent. If you want to read it, you could use cmp.getSuper().get("v.body") but it's a deprecated API, and it will probably not work. However, that's the idea behind it.

Why is it working like this? Let's call your component c:myComponent and use it inside an app:

<c:myComponent>
   <i>Instance Part</i>
</c:myComponent>

This is equivalent to this format:

<c:myComponent>
    <aura:set attribute="body>
       <i>Hello</i>
    </aura:set>
<c:myComponent>

As you see, every component needs to receive an attribute body independently of what it defines internally. You can try this format, it does work, but it's unnecessary except to illustrate how to set the body of a component.

Now, your component can ignore its body attribute as in your original code, or it can use it like this:

<aura:component>
    <div>Body part</div>
    {!v.body}
    <ui:button label="Push Me" press="{!c.printCmpBody}"/>
</aura:component>

You can place the expression {!v.body} anywhere you want.

The rendered content will then be equal to the instance's v.body plus the component-defined lexical definition:

v.body = <i>Instance Part</i>
+
lexical scope = <div>Body part</div>{!v.body}...
=
Rendered: <div>Body part</div><i>Instance Part</i>...

You can get more information on how the attribute body works by looking at slides 24, 25, and 27:

Mastering the Lightning Framework - Part 1

And the explications are at around 15:00 minute here:

Mastering the Lightning Framework - Part 1

  • Thank you for the detailed explanation.It really helped. – Praveen Jul 26 '16 at 6:14
2

Example of usage

Parent Component

<aura:component description="TestComponent">
    <div>Body Parent</div>
    <div>Body Parent</div>
    <div>Body Parent</div>
    <div>Body Parent</div>

    <c:childComponnet>
        <aura:set attribute="body">
            <div>Body nested</div>
            <div>Body nested</div>
            <div>Body nested</div>

        </aura:set>

    </c:childComponnet>
</aura:component>

childComponent

<aura:component description="testComponent2">
<aura:attribute name="body" type="Aura.Component[]"/>
    {!v.body}
    <lightning:button variant="brand" label="show body" onclick="{!c.getComponentBody}"/>
</aura:component>

ChildComponnetController.js

({

    getComponentBody: function(cmp, event, helper) {
        var body = cmp.get("v.body");
        console.log(body);
    },

})

output is array of 3 objects

0

This could be a matter of naming, or lack thereof. You've got a <div>Body part</div> in your code and {!c.printCompBody}, yet you have no attributes that it relates to. So, how is your controller to know what v.body is? The <div> has no name or attribute associated with it. Try giving the div a name of body (or create a body tag) and see if that solves your issue. Either that or create an attribute that's associated with div's contents and links the div to printCompBody.

  • cmp points to the instance of the <aura:component /> that been created.It has an attribute/member called body.Here is the link to source code of aura:component.So I'm wondering why it is not returning div,<ui:button/> elements, when component.get("v.body") is called. – Praveen Jul 24 '16 at 16:11
  • 1
    If I remove the div,ui:button and create it dynamically using $A.createComponent , i'd be doing this: cmp.set("v.body",dynCmps) where dynCmps contains the respective div,ui:button component instances,and it renders in the browser.So Including the markup div,ui:button also does the same thing, which means cmp.set("v.body") should have take place somewhere.So I thought, component.get("v.body") would return the two elements. – Praveen Jul 24 '16 at 16:21
  • I hear what you're saying, but it's still ambiguous and I wouldn't count on it. I esp don't see where you have anything that associates "button pressed" event with cmp.get("v.body") in that console log statement. – crmprogdev Jul 24 '16 at 17:15
  • I am just trying to understand how things work in LC, especially the body setting part. – Praveen Jul 24 '16 at 17:21
  • Do you see the same behavior with the Locker Service critical update deactivated under Setup in your org? – Doug Chasman Jul 25 '16 at 10:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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