0

I am trying to show a list of custom object records via a lightning component

Custom Object : Book__c with two fields

The lightning application comprise of two lightning components one for adding a record and the second one displays all the records from Salesforce

Below is my code

Application markup

<aura:application extends="force:slds" >
    <c:CreateBookComponent />

    <c:ShowAddedBookComponent />
</aura:application>

Component 1 (enter a new record) markup

<aura:component controller="BookController" >
    <aura:attribute name="book" type="Book__c" default="{'sobjectType':'Book__c','Book_Name__c':'','Book_Author__c':''}"/>

    <div class="slds-card slds-p-top--medium">
        <header class="slds-card__header">
            <h3 class="slds-text-heading--small">Create a New Book</h3>
        </header>


    </div>

    <form class="slds-form--stacked">

    <div class="slds-form-element">
        <div class="slds-form-element__control">
            <ui:inputText aura:id="idBookName" label="Book Name:"  labelClass="slds-form-element_label" value="{!v.book.Book_Name__c}"/>
        </div>
    </div>

        <div class="slds-form-element">
            <div class="slds-form-element__control">
                <ui:inputText aura:id="idBookAuthor" label="Book Author:"  labelClass="slds-form-element_label" value="{!v.book.Book_Author__c}"/>
            </div>
        </div>

        <div class="slds-form-element">
            <div class="slds-form-element__control">
                <ui:button label="Add New Book" class="slds-button slds-button--brand" press="{!c.AddNewBookHandler}"/>
            </div>
        </div>

    </form>
</aura:component>

Component 2 (list of all records) markup

<aura:component controller="BookController" >
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>

    <aura:attribute name="allbooks" type="Book__c[]"/>    
    <aura:iteration items="{!v.allbooks}" var="each_book">
        <force:outputField aura:id="op_Bookname" value="{!each_book.Book_Name__c}"/>
        <force:outputField aura:id="op_Bookauthor" value="{!each_book.Book_Author__c}"/>
        <br/>
    </aura:iteration>

</aura:component>

Component 1 controller

({
    AddNewBookHandler : function(component, event, helper) {
        var newBook = component.get("v.book");

        if(newBook.Book_Name__c === 'Please enter Book Name' )
        {
            alert("You need to change the details first !!");
        }
        var action=component.get("c.saveBook");
        action.setParams({"b":newBook});
        action.setCallback(this,function(response){
           var state = response.getState();

            if(component.isValid() && state === "SUCCESS")
            {
                //alert("Book Saved Successfully");
                alert("Book Id = "+response.getReturnValue());
                component.set("v.book.Book_Name__c",'');
                component.set("v.book.Book_Author__c",'');
                //component.set("v.book.Book_Author__c",'Please enter Book Author');
                //component.set("v.book.Book_Author__c",'Please enter Book Author');
            }
            else
            {
                alert("Book Not Saved Successfully");
                console.log("Response = "+response);
                console.log("Response State ="+response.getState());
                console.log("Response Return Value = "+response.getReturnValue());
            }
        });
        $A.enqueueAction(action);
    }
})

Component 2 controller

({
    doInit : function(component, event, helper) {

        var action = component.get("c.getBooks");

        action.setCallback(this, function(response){

            var state = response.getState();
            if(state === "SUCCESS")
            {
                //alert("here");
                component.set("v.allbooks",response.getReturnValue());
            }
            else
            {
                alert("Error in retrieving book records");
            }
        });

        $A.enqueueAction(action);
    }
})

Shared Apex Controller

public class BookController {

    @AuraEnabled
    public static String saveBook(Book__c b)
    {
        Book__c inputBook = new Book__c();
        inputBook.Book_Author__c = b.Book_Author__c;
        inputBook.Book_Name__c = b.Book_Name__c;
        insert inputBook;
        return inputBook.Id;
    }

    @AuraEnabled
    public static List<Book__c> getBooks()
    {
        List<Book__c> allBooks = [SELECT ID,Book_Name__c,Book_Author__c FROM Book__c];
        return allBooks;
    }

}

Issues

a) I could observe performance issue while rendering the output. The first component is getting rendered without any issues while it takes around 1-2 seconds for the second component to render. Is this common ?

b) The second component does not render properly instead it shows the following message list as

enter image description here

I did some debugging and found that if I use ui:outputText instead of force:outputField I am getting the data as expected.

Can someone let me know what is wrong with my markup ?

1
  • Most likely have to strinify the new book to send it back to apex
    – Eric
    Mar 31, 2017 at 4:31

2 Answers 2

2

force:outputField works only when you bind with one sObject. It doesn't work with iteration but if you want to do this anyway you can create another component as shown below.

Single book component

<aura:component>
    <aura:attribute name="singlebook" type="Book__c"/>    
        <force:outputField aura:id="op_Bookname" value="{!single.Book_Name__c}"/>
        <br/>
</aura:component>

Show All Book component

<aura:component controller="BookController" >
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>

    <aura:attribute name="allbooks" type="Book__c[]"/>    
    <aura:iteration items="{!v.allbooks}" var="each_book">

        <c:singlebookcomponent singlebook="{!each_book}"/>
        <br/>
    </aura:iteration>
</aura:component>

PS: It will decrease overall performance for sure but putting this solution if you are doing this to learn lightning.

2

If your only intention is to display the record values in iteration, you could better use ui:outputText instead of force:outputField.

This could solve your problem. Please note that you cannot give static aura:id for elements inside an iteration. So your component becomes as follows :-

<aura:component controller="BookController" >
    <aura:attribute name="allbooks" type="Book__c[]"/>
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>

    <aura:iteration items="{!v.allbooks}" var="each_book">
        <ui:outputText value="{!each_book.Book_Name__c}"/>
        <ui:outputText value="{!each_book.Book_Author__c}"/>
        <br/>
    </aura:iteration>
</aura:component>

And right now displaying multiple records using force:outputField is not possible.

You can verify it by changing the apex method to return a single record and avoid iteration in the component. Also you have to give default value for the attribute as follows:

Component 2

<aura:component controller="BookController" >
    <aura:attribute name="allbooks" type="Book__c" default="{'sobjectType': 'Book__c'}"/>
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>

    <force:outputField aura:id="op_Bookname" value="{!v.allbooks.Book_Name__c}"/>
    <force:outputField aura:id="op_Bookauthor" value="{!v.allbooks.Book_Author__c}"/>
</aura:component>

Shared Controller

public class BookController {

@AuraEnabled
public static String saveBook(Book__c b)
{
    Book__c inputBook = new Book__c();
    inputBook.Book_Author__c = b.Book_Author__c;
    inputBook.Book_Name__c = b.Book_Name__c;
    insert inputBook;
    return inputBook.Id;
}

@AuraEnabled
public static Book__c getBooks()
{
    Book__c book1 = [SELECT ID,Book_Name__c,Book_Author__c FROM Book__c LIMIT 1];
    return book1;
}
}

I hope this would clear your problem.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .