0

I'm working through the Aura Components Basics trailhead modules and am struggling a bit to get the example expenses.cmp component working correctly. This is code that I have taken straight from the module examples and modified while working through the instructions and tutorials.

What the code should do is:

  • display a form that allows entry of Expense records
  • display a list of existing expense records

What is actually occurring:

  • displays a form that allows entry of Expense records. When the user enters data and clicks Create Expense, the newly submitted record shows in the Expenses list on teh Aura page but is not saved to the Salesforce org
  • after refreshing the page, the expense records list only shows 2 expense records that I entered through the regular Salesforce UI, and anything entered by the user through this page is gone

What am I missing?

expenses.cmp component

    <aura:attribute name="expenses" type="Expense__c[]"/>

    <!--not sure if this should go after the first attribute or the second one-->
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/> 

    <aura:attribute name="newExpense" type="Expense__c"
         default="{ 'sobjectType': 'Expense__c',
                        'Name': '',
                        'Amount__c': 0,
                        'Client__c': '',
                        'Date__c': '',
                        'Reimbursed__c': false }"/>

    <!-- PAGE HEADER -->
    <lightning:layout class="slds-page-header slds-page-header_object-home">
        <lightning:layoutItem>
            <lightning:icon iconName="standard:scan_card" alternativeText="My Expenses"/>
        </lightning:layoutItem>
        <lightning:layoutItem padding="horizontal-small">
            <div class="page-section page-header">
                <h1 class="slds-text-heading_label">Expenses</h1>
                <h2 class="slds-text-heading_medium">My Expenses</h2>
            </div>
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / PAGE HEADER -->
    <!-- NEW EXPENSE FORM -->
    <lightning:layout>
        <lightning:layoutItem padding="around-small" size="6">

            <!-- CREATE NEW EXPENSE -->
            <div aria-labelledby="newexpenseform">
                <!-- BOXED AREA -->
                <fieldset class="slds-box slds-theme_default slds-container_small">
                <legend id="newexpenseform" class="slds-text-heading_small
                    slds-p-vertical_medium">
                    Add Expense
                </legend>

                <!-- CREATE NEW EXPENSE FORM -->
                <form class="slds-form_stacked">
                    <lightning:input aura:id="expenseform" label="Expense Name"
                                    name="expensename"
                                    value="{!v.newExpense.Name}"
                                    required="true"/>
                    <lightning:input type="number" aura:id="expenseform" label="Amount"
                                    name="expenseamount"
                                    min="0.1"
                                    formatter="currency"
                                    step="0.01"
                                    value="{!v.newExpense.Amount__c}"
                                    messageWhenRangeUnderflow="Enter an amount that's at least $0.10."/>
                    <lightning:input aura:id="expenseform" label="Client"
                                    name="expenseclient"
                                    value="{!v.newExpense.Client__c}"
                                    placeholder="ABC Co."/>
                    <lightning:input type="date" aura:id="expenseform" label="Expense Date"
                                    name="expensedate"
                                    value="{!v.newExpense.Date__c}"/>
                    <lightning:input type="checkbox" aura:id="expenseform" label="Reimbursed?"
                                    name="expreimbursed"
                                    checked="{!v.newExpense.Reimbursed__c}"/>

                    <lightning:button label="Create Expense"
                                    class="slds-m-top_medium"
                                    variant="brand"
                                    onclick="{!c.clickCreate}"/>
                </form>
                <!-- / CREATE NEW EXPENSE FORM -->
            </fieldset>
            <!-- / BOXED AREA -->
            </div>
            <!-- / CREATE NEW EXPENSE -->

        </lightning:layoutItem>
    </lightning:layout>
    <!-- / NEW EXPENSE FORM -->
    <c:expensesList expenses="{!v.expenses}"/>
</aura:component>

expensesController.js

    clickCreate: function(component, event, helper) {
        let validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid();
            return validSoFar && inputCmp.get('v.validity').valid;
        }, true);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            let newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    },

    // Load expenses from Salesforce
    doInit: function(component, event, helper) {
        // Create the action
        let action = component.get("c.getExpenses");
        // Add callback behavior for when response is received
        action.setCallback(this, function(response) {
            let state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.expenses", response.getReturnValue());
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
        // Send action off to be executed
        $A.enqueueAction(action);
    },
})

expensesHelper.js

    createExpense: function(component, expense) {
        //let theExpenses = component.get("v.expense");
        let theExpenses = component.get("v.expenses");
        // Copy the expense to a new object
        // THIS IS A DISGUSTING, TEMPORARY HACK
        let newExpense = JSON.parse(JSON.stringify(expense));
        
        console.log("Expenses before 'create': " + JSON.stringify(theExpenses));
        theExpenses.push(newExpense);
        component.set("v.expenses", theExpenses);
        //component.set("v.expense", theExpenses);
        console.log("Expenses after 'create': " + JSON.stringify(theExpenses));
        console.log("Expenses in newExpense variable: " + JSON.stringify(newExpense));
    }
})
2
  • There is no apex component to save to the DB nor is there any LDS invokes which will commit to the DB. As per the code above, you're simply adding it to the expenses list. Not to the database.
    – Ysr Shk
    Oct 8, 2022 at 11:39
  • 1
    Cool, thanks. Clearly I have to learn a few more things, and clean up this code. Ha. Oct 9, 2022 at 20:41

1 Answer 1

0

Ysr Shk was correct in the comment above (not sure how to give credit).

My controller ExpensesController.cls was missing the saveExpense function. NOt sure how it got deleted, as it was once there, but this is the correct code

ExpensesController.cls

    @AuraEnabled
    public static List<Expense__c> getExpenses() {
        // Check to make sure all fields are accessible to this user
        String[] fieldsToCheck = new String[] {
            'Id', 'Name', 'Amount__c', 'Client__c', 'Date__c',
            'Reimbursed__c', 'CreatedDate'
        };
        Map<String,Schema.SObjectField> fieldDescribeTokens =
            Schema.SObjectType.Expense__c.fields.getMap();
        for(String field : fieldsToCheck) {
            if( ! fieldDescribeTokens.get(field).getDescribe().isAccessible()) {
                throw new System.NoAccessException();
            }
        }
        // OK, they're cool, let 'em through
        return [SELECT Id, Name, Amount__c, Client__c, Date__c,
                       Reimbursed__c, CreatedDate
                FROM Expense__c];
    }

    @AuraEnabled
    public static Expense__c saveExpense(Expense__c expense) {
        // Perform isUpdatable() checking first, then
        upsert expense;
        return expense;
    }
}

And my expensesHelper.js was completely goofy and needed to be reworked.

expensesHelper.js

    createExpense: function(component, expense) {
        let action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        action.setCallback(this, function(response){
            let state = response.getState();
            if (state === "SUCCESS") {
                let expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses", expenses);
            }
        });
        $A.enqueueAction(action);
    },
})

You must log in to answer this question.

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