1

I am trying to create a simple component:

<aura:component implements="force:appHostable">

    <aura:attribute name="mystr" type="String" required="false" default="hello world" access="private" description="" />

    <aura:attribute name="mycmp" type="Aura.Component[]" access="public" description="test">
        <span>{! v.mystr }</span>
    </aura:attribute>

</aura:component>

When I try to include this component into a lightning app, I receive an error saying:

This page has an error. You might just need to refresh it.
Error during init [Assertion Failed!: Unable to get value for key 'v.mystr'. No value provider was found for 'v'. : false]

Is it working as expected or is it bug?

P.S. switching access to public does not have any affect.

Added on 2017-01-21

What I am trying to achieve is to define snippets inside the component that I can either include in different places inside the same component or set as body inside another component that is created dynamically.

Using example above I would like to reference {! v.mycmp } in different places inside the same component to render the same element in different places on the page. As @javanoob pointed out in his posts, the code above works if reference to {! v.mystr } is replaced with expression . However, when I tried to use it with some other tags like where items has a reference to an attribute inside my main component, the main component doesn't render again with the same error.

This leads me back to the original question: Is it me trying to use aura:attribute of type Aura.Component[] outside its intended purpose or aura:attribute not behaving correctly?

Added on 2017-01-22

Summarising the solution from javanoob, the aura:attribute can be replaced by a basic component like

<aura:component>{! v.body }</aura:component>

and be used as an attribute. It can be hidden from the page using standard css approach.

I would personally expect to declare complex attributes without the need for placeholders like this. It works as a workaround though.

  • 3
    Okay, that's the code you tried, but this may be an x-y problem. What are you actually trying to accomplish? Be descriptive. – sfdcfox Jan 20 '17 at 17:16
  • My ultimate goal was to define a similar attribute and pass it another component dynamically. That would allow to declare body for generic components without the need to create them all in code. For example, I show declared component on the screen on big screen, or provide a button to open up the same thing within a popup on a mobile device. The popup would be a generic component that would receive this component as body. Similar to the documented approach developer.salesforce.com/docs/atlas.en-us.lightning.meta/… but with attribute reference. – Artur Jan 20 '17 at 19:21
  • 1
    Not exactly what you are looking for but I tried like this <aura:attribute name="mycmp" type="Aura.Component[]"> <span><aura:text value="{!v.mystr}"></aura:text></span> </aura:attribute> and it is working – javanoob Jan 20 '17 at 21:05
  • 1
    @javanoob thank you! I have just tried to use it in a similar fashion and it seems that using aura:attribute (mystr) as a value for the component attribute (in this case 'value') works. It seems not to be working when used as an element on its own. Still not sure whether it is expected behaviour or not, but your hint definitely allows me to do what I need. – Artur Jan 20 '17 at 23:23
  • @Artur I'm glad that it is working for you! – javanoob Jan 20 '17 at 23:25
3

I read the documentation you mentioned in the comments and it says attribute of type Aura.Component[] is called a facet. Then I checked the documentation about Component facet and came up with the below working example on how to use the attribute type List in other attribute of type Aura.Component[]:

HelloWorld.cmp

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

HellWorld.app:

<aura:application extends="force:slds">
    <aura:attribute name="foo" type="List" default="['red','blue','green']"/>
    <c:HelloWorld>
        <aura:set attribute="bar">
            <aura:iteration items="{!v.foo}" var="item">
                <p><aura:text value="{!item}"></aura:text></p>
            </aura:iteration>
        </aura:set>
    </c:HelloWorld>
</aura:application>

Output:

enter image description here

UPDATE:

I did not work much on Lightning but I was curious on how to solve the issue you are talking about in comments so I came up with this example:

HelloWorld.cmp

<aura:component implements="force:appHostable">
    <aura:attribute name="bar" type="Aura.Component[]" access="global">
    </aura:attribute>
    <aura:attribute name="baz" type="Aura.Component[]" access="global">
    </aura:attribute>    
    <aura:attribute name="componentNumber" type="String" access="global">
    </aura:attribute>
    Below are the contents of {!v.componentNumber} component: <br></br>
    <div style="background-color:red">{!v.bar}</div>
    <div style="background-color:yellow">{!v.baz}</div>
    <div style="background-color:green">{!v.body}</div>
</aura:component>

HelloWorldApp.app

<aura:application extends="force:slds">

    <aura:attribute name="foo1" type="List" default="['red','blue','green']"/>
    <aura:attribute name="foo2" type="List" default="['Orange','Apple','Mango']"/>

    <c:HelloWorld aura:id="first" componentNumber="1">
        <aura:set attribute="bar" >
            <aura:iteration items="{!v.foo1}" var="item" indexVar="i">
                <p><aura:text value="{! i + ':' + item}"></aura:text></p>
            </aura:iteration>
        </aura:set>
    </c:HelloWorld>

    <c:HelloWorld aura:id="second" componentNumber="2">
        <aura:set attribute="bar" >
            <aura:iteration items="{!v.foo2}" var="item" indexVar="i">
                <p><aura:text value="{! i + ':' + item}"></aura:text></p>
            </aura:iteration>
        </aura:set>     
    </c:HelloWorld>

    <div></div>

    <ui:button  label="Update first component with second component body" 
                press="{!c.updateFirstCmp}"/>

</aura:application>

HelloWorldAppController.js:

({
    updateFirstCmp : function(component, event, helper) {
        debugger;
        var secondCmpBdy = component.find('second').get('v.bar');
        // Setting the attribute of type Aura.Component[] of first HelloWorld Component from 
        // second HelloWorld component attribute of type Aura.Component[]
        component.find('first').set('v.baz',secondCmpBdy);
        // Setting the body(type Aura.Compoent[] as per docs) of first HelloWorld Component 
        // from second HelloWorld component attribute of type Aura.Component[]
        component.find('first').set('v.body',secondCmpBdy);
    }   
})

Output:

enter image description here

After clicking on the button: (second component still exists on the page while the content is copied to first component)

enter image description here

I am learning a lot from this question :)

  • Big thanks for looking into this further. Unfortunately it is not the same as using the aura:attribute. There are two problems here: 1) it immediately includes the component on the page, which could work in my scenario, but; 2) when I find this c:helloworld component in javascript and try to set it as another components body (or other Aura.Component[] attribute), the framework immediately removes the component from the original location. This is not how it behaves with aura:attribute atm. – Artur Jan 21 '17 at 12:36
  • @Artur Regarding the first point, If we don't want the component to immediately display on the page, I think we can control the display using another attribute and passing the value for that as true or false. I don't see the behavior you are talking about in your second point about framework immediately removing the component(Please see my updated answer). Also check this link(developer.salesforce.com/docs/atlas.en-us.lightning.meta/…) on how Aura.Component[] is used in the docs. – javanoob Jan 21 '17 at 22:07
  • Sorry for no formatting - comments here don't allow it. :( Spent several hours solving the disappearing components mystery. Following your example, I added a second button: <ui:button label="Remove body from first" press="{!c.removeBody}"/> and associated method: removeAdditions : function(component, event, helper) { component.find('first').set('v.body',[]); } . Notice how the component gets removed (dissappear) everywhere. See next comment for some additional details. – Artur Jan 22 '17 at 14:13
  • So I thought that using component.autoDestroy method would help. However, when I tried to loop through component.find('second').forEach(function(el){ el.autoDestroy(false); }); the autoDestroy function is not available. If I try to modify the HelloWorld component to add autoDestroy(false) flag on component initialisation or change - same problem. However, tried again the same with aura:attribute and have the same problem. Therefore I will close this question as you provided the solution to my original problem, and open a new one to deal with the disappearing problem. – Artur Jan 22 '17 at 14:30
  • For the reference, here is a link to the question related to component being removed: salesforce.stackexchange.com/questions/156984/… – Artur Jan 22 '17 at 15:01

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.