9

Here @Peter describes a very cool way to use a <force:inputField ... /> together with

<aura:attribute name="YourObjectType" type="YourObjectType" 
    default="{ sobjectType: 'YourObjectType' }" 
/>

where YourObjectType could be Account for instance.

My js-controller is

({
        init : function(component, event, helper) {
            console.log('init');
            var action = component.get("c.getData");        
            action.setCallback(this, function(response) {
                console.log(response.getReturnValue());
                component.set("v.opportunities", response.getReturnValue().opportunities);
            });
            $A.enqueueAction(action);
        }, 
})

And my apex controller is

public class elfClientOpportunityList { 
    public class data {
        @AuraEnabled public List<Opportunity>       opportunities               { get; set; }
        @AuraEnabled public String                  text2                       = 'text2';
        public data() {
            this.opportunities = new List<Opportunity>();           
        }
    }
    @AuraEnabled public static data     getData() {
        data result = new data();
        result.opportunities = (List<Opportunity>) Database.query( xs.soql(
            ' SELECT *, Account.Name FROM Opportunity '
        ));
        return result; 
    }
}

(Sidenotes: text2 is just a placeholder for now and SELECT * does what it does in SQL using this)

Now I need to use these fields inside an iteration over something like this

<aura:attribute name="opportunities" type="Opportunity[]" />
// ...
<aura:iteration var="item" items="{!v.opportunities}">
    // ...
    <force:inputField value="{!item.YourCustomFieldName__c}"/>
    // ...
<aura:iteration>

What do I need to define at the sobjectType-property in the default attribute?

I've tried this already

    <aura:attribute name="opportunities"    type="Opportunity[]" default="{ sobjectType: 'Opportunity[]' }" />

But as an unfortunate it bring only an not helpful error message

Aura.loadComponent(): Failed to initialize application.
An internal server error has occurred Error ID: 319439292-6868 (-1241857102)

enter image description here

Using List<Opportunity> does not work either

<aura:attribute name="opportunities"    type="Opportunity[]" default="{ sobjectType: 'List<Opportunity>' }" />

You can't save it, because the API says:

markup://c:elfClientOpportunityList:5,90: ParseError at [row,col]:[6,90] Message: The value of attribute "default" associated with an element type "null" must not contain the '<' character.: Source

Now I'm out of ideas an need to ask for help. Thanks a lot!!

3

It's about 2 years after I initially touched this issue and today I've found this Known Issue here https://success.salesforce.com/issues_view?id=a1p3A0000008gDzQAI

It confirms my ancient observations from 2015 and confirms also that the Peters fine-grained approach does not work.

For me the recommended workaround is not usefull, since ui:whatever can't out of the box deal with picklists values, field dependencies, translation and localization like number- and date-formats. It can be done, but needs an unjustified amout of complexity and code. My workaround is to use visualforce with it's mature apex:inputField instead.

Here is the Known Issue

<force:inputField> is not rendering fields on UI when being used inside <aura:iteration> tag or inside nested components in Lightning application

Summary

<force:inputField> is not rendering fields on UI as expected when being used inside <aura:iteration> or inside inner component in Lightning application

Repro

  1. Login to your org
  2. Try creating a sample lightning application
  3. Create a component with below sample code snippet & try to preview the app
  4. You will notice the fields doesn't render on the UI as expected.
<aura:component>  
    <aura:iteration aura:id="iteration" items="{!v.expenses}" var="expenses">  
        <force:inputField value="{!expenses.Amount__c}"/>  
    </aura:iteration>  
</aura:component>

Workaround

Try using <ui:inputText> or or <ui:inputCheckbox> instead of for rendering input fields inside <aura:iteration> or inside inner components

2

In this case, you'd need to have a more granular component as the item inside of <aura:iteration>.

Create a smaller granular component that contains the markup for your list item:

c:opptyListItem Components

<aura:component> 
  ...blah blah blah...
  <aura:attribute name="opptyItem" type="Opportunity" default="{ sobjectType: 'Opportunity' }"/>

  <div>
    <force:inputField value="{!v.opptyItem.YourCustomFieldName__c}"/>
    ...more fields here...
  </div>
</aura:component>

So in the top level component, you would have your list attribute and aura:iteration.

<aura:attribute name="opptys" type="Opportunity[]"/>

<aura:iteration var="oppty" items="{!v.opptys}>
  <c:opptyListItem opptyItem="{!oppty}" />
</aura:iteration>

This may seem like overkill, but everything about Lightning Components points to smaller granular components as opposed to big monolithic UIs (the prior Visualforce way).

  • 3
    Thanks, this would technically work... But still: overkill it is. Not everything is reusable or is intended to be reused. Sometimes a more monolithic design is a big advantage, speedup and clearly decouples things properly. So to be forced to design fine-grained always feels very wrong to me. It should be choice, at least for reasons like simple iterations. I will end up with bizillions of hard-to-maintain compos this way... not cool – Uwe Heim Oct 26 '15 at 12:54
  • Or hopefully Salesforce will provide a few fine-grained "list-row" components in the future. The trick here would be ideally to make a single list row component that can become any sObject record that you could then pass in the fields to render. To have one of these for every single sObject type would quickly become unmaintainable. – pchittum Oct 26 '15 at 13:00
  • I would not expect to get something new... What is already there is fine. Only this single line should work as expected default="{ sobjectType: 'Opportunity[]' }" - I think this would make sense, doesn't it? Or even better: let the force:inputField be smart enough to "know" what it is, like in Visualforce apex:inputField knew... Plus we already told the framework explicitly what type it is here type="Opportunity[]". As far as I understood, all that default-acrobatic is only a necessary workaround, because the feature isn't finished, yet - right? – Uwe Heim Oct 26 '15 at 13:39
  • As I understand it, yes. – pchittum Oct 26 '15 at 14:20
  • 1
    I think the whole force:outputText implementation is extremely confusing. It doesn't make any sense to me to have some kind of metadata definition in the default value of the attribute. It feels like a very hackish way of getting information about the object that's stored in the attribute. It's also not immediately obvious how this component could be instantiated dynamically or even if it can be instantiated dynamically. At the very least I would think this component needs much better handling in the documentation. – Scott Morrison Jun 6 '16 at 19:04
1

Try using <lightning:inputField> instead of <force:inputField>. It worked for me.

-1

It looks like the problem is with setting the default value of the attribute. With some attribute types, the default value is interpreted as a string rather than an object, event is an example. As for Opportunity[], when running the following, the opplist2 attribute when set with default is logged as a string, while the opplist3 is logged as an object (array) when set on initialization with a js Array literal.

app

<aura:application >
    <aura:attribute type="Opportunity[]" name="opplist1" default="{ sobjectType: 'YourObjectType' }" />
    <aura:attribute type="Opportunity[]" name="opplist2" default='[{"sobjectType":"Opportunity"},{"sobjectType":"Opportunity"}]' />
    <aura:attribute type="Opportunity[]" name="opplist3" />
    <aura:handler name="init" value="{!this}" action="c.doInit" />
</aura:application>

js

({
    doInit : function(component, event, helper){
        console.log(component.get("v.opplist1"));
        console.log(component.get("v.opplist2"));
        console.log(typeof component.get("v.opplist2"));
        component.set("v.opplist3",[{"sobjectType":"Opportunity"},{"sobjectType":"Opportunity"}]);
        console.log(component.get("v.opplist3"));
        console.log(typeof component.get("v.opplist3"));
    }
})
  • I don't understand how to make things work this way. Will it work with force:input inside of the aura:iteration this way (v.opplist3) or not? At the .set() you are setting only the type, not the value - as far as I see. How to set type and value? Wait, I'll add my js controller, than it's easier to discuss... – Uwe Heim Oct 26 '15 at 13:13
  • Now I've added my controllers (js+apex). How to combine your technique component.set("v.opportunities",[{"sobjectType":"Opportunity"},{"sobjectType":"Opportunity"}]); and the "common value setting" I use component.set("v.opportunities", response.getReturnValue().opportunities);? Done subsequently? Won't it clash and only the last statement win? – Uwe Heim Oct 26 '15 at 13:28
  • Martin, could you tell me if this way is intended to work somehow? I still can't figure it out. Inspecting the result values response.getReturnValue().opportunities, even in my original code each element already has a property "sobjectType":"Opportunity" and it doesn't work. So how setting this manually can could it work? – Uwe Heim Oct 26 '15 at 13:50
  • Martin, at this time I think your answer does not provide an viable method to solve the issue. Possibly it's more about the reasons behind, but still I can't really understand it. Any further clarification is very welcome! – Uwe Heim Oct 27 '15 at 13:02
  • @UweHeim I agree. I was looking at this some more this evening, and it looks like my post addresses a different symptom of the underlying issue and doesn't actually solve your problem. – martin Oct 27 '15 at 14:13

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.