I ran into this error when trying to Preview my Application from the Developer Console. When I click the "CreateExpense" button after entering in data into the fields (I might have spelled Expenses wrong)
All of this code references the Trail head Module: "Connecting Components With Events" from 'Lightning Components Basics' There is a lot of code and I am not experienced enough to know where the error is happening in the code and since I can't debug, line by line, this error is hard to find.
Any help is appreciated!!!
This is the error message:
Action failed: c:exspenses$controller$clickCreateExpense [action.setCallBack is not a function]
Failing descriptor: {c:exspenses$controller$clickCreateExpense}
exspensesApp.app
<aura:application extends="force:slds">
<c:expenses />
</aura:application>
exspenses.cmp
<aura:component controller="ExpensesController">
<!-- CUSTOM ATTRIBUTE DEFINITION -->
<aura:attribute name="expenses" type="Expense__c[]" /> // local array of Objects to hold all expenses created.
<aura:attribute name="newExpense" type="Expense__c"
default="{'sObject': 'Expense__c',
'Name': '',
'Amount__c': 0,
'Client__c': '',
'Date__c': '',
'Reimbursed__c': false}"/>
<!-- init handler -->
<aura:handler name="init" action="{!c.doInit}" value="{!this}" />
<!-- PAGE HEADER -->
<div class="slds-page-header" role="banner">
<div class="slds-grid">
<div class="slds-col">
<p class="slds-text-heading--label">Expenses</p>
<h1 class="slds-text-heading--medium">My Expenses</h1>
</div>
</div>
</div>
<!-- /PAGE HEADER -->
<!-- NEW EXPENSE FORM -->
<div class="slds-col slds-col--padded slds-p-top--large">
<div aira-labelledby="newexpenseform">
<!-- BOXED AREA -->
<fieldset class="slds-box slds-theme--default slds-container--small">
<legend id="newexpenseform" class="slds-text-heading--small slds-p-vertial--medium">
Add Expense
</legend>
<!-- CREATE NEW EXPENSE FORM -->
<form class="slds-form--stacked">
<div class="slds-form-element slds-is-required">
<div class="slds-form-element__control">
<ui:inputText aura:id="expname" label="Expense Name"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newExpense.Name}"
required="true" />
</div>
</div>
<div class="slds-form-element slds-is-required">
<div class="slds-form-element__control">
<ui:inputNumber aura:id="amount" label="Amount"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newExpense.Amount__c}"
required="true"/>
</div>
</div>
<div class="slds-form-element">
<div class="slds-form-element__control">
<ui:inputText aura:id="client" label="Client"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newExpense.Client__c}"
placeholder="ABC Co."/>
</div>
</div>
<div class="slds-form-element">
<div class="slds-form-element__control">
<ui:inputDate aura:id="expdate" label="Expense Date"
class="slds-input"
labelClass="slds-form-element__label"
value="{!v.newExpense.Date__c}"
displayDatePicker="true"/>
</div>
</div>
<div class="slds-form-element">
<ui:inputCheckbox aura:id="reimbursed" label="Reimbursed?"
class="slds-checkbox"
labelClass="slds-form-element__label"
value="{!v.newExpense.Reimbursed__c}"/>
</div>
<div class="slds-form-element">
<ui:button label="Create Expense"
class="slds-button slds-button--brand"
press="{!c.clickCreateExpense}"/>
</div>
</form>
<!-- Create new expense form -->
</fieldset>
<!-- Boxed Area -->
</div>
</div>
<!-- NEW EXPENSE FORM -->
<c:expensesList expenses="{!v.expenses}" />
</aura:component>
exspensesController.js
({
clickCreateExpense : function(component, event, helper) {
if(helper.validateExpenseForm(component)) {
// Create a new Expense, if it is valid
var newExpense = component.get("v.newExpense");
console.log(newExpense);
helper.createExpense(component, newExpense);
}
},
// Load expenses from Salesforce
doInit: function(component, event, helper) {
// Create the action (remote method call)
var action = component.get("c.getExpenses");
// Add callback behavior for when response is received
action.setCallback(this, function(response) {
var state = response.getState();
if (component.isValid() && state === "SUCCESS") {
component.set("v.expenses", response.getReturnValue());
}
else {
console.log("Failed with state: " + state);
console.log(response + " ");
}
});
// Send action off to be executed (server request)
$A.enqueueAction(action);
},
})
exspensesHelper.js
({
createExpense : function(component, expenseToAdd) {
var action = component.get("c.saveExpense"); // call to Apex Controller method
action.setParams({
"expense": expenseToAdd
});
action.setCallBack(this, function(response) {
var state = response.getState();
if(component.isValid() && state === "SUCCESS") {
var expenses = component.get("v.expenses"); // get the Local Array [] of Expense__c Objects.
expenses.push(response.getReturnValue()); // pusing the response from the Server into our Local Array
component.set("v.expenses", expenses);
}
});
$A.enqueAction(action);
},
validateExpenseForm : function(component) {
//----------------------------------------------------------------------------------
// simplistic error checking
var validExpense = true;
// Name must not be blank.
var nameField = component.find("expname");
var expname = nameField.get("v.value");
if($A.util.isEmpty(expname)) {
validExpense = false;
nameField.set("v.errors", [{message: "Expense name can't be blank..."}]);
}
else {
nameField.set("v.errors", null);
}
//-------------------------------------------------------------------------------
// Amount must be set, must be a positive number.
var amtField = component.find("amount");
var amt = amtField.get("v.value");
if($A.util.isEmpty(amt) || isNaN(amt) || (amt <= 0.0)) {
validExpense = false;
amtField.set("v.errors", [{message: "Enter a different expense amount..."}]);
}
else {
// if the amount is valid, uset any errors set before.
amtField.set("v.errors", null);
}
return(validExpense);
}
})
this is the component that displays the list, once the expense is created. I am sure this piece of code works.
expensesList.cmp
<aura:component >
<aura:attribute name="expenses" type="Expense__c[]" />
<div class="slds-card slds-p-top--medium">
<header class="slds-card__header">
<h3 class="slds-text-heading--small">Expenses</h3>
</header>
<section class="slds-card__body">
<div id="list" class="row">
<aura:iteration items="{!v.expenses}" var="expense">
<c:expenseItem expense="{!expense}" />
</aura:iteration>
</div>
</section>
</div>
</aura:component>
ExpensesController.apxc
public with sharing class ExpensesController {
// LECTURE ABOUT WHAT IS MISSING HERE COMING SOON
@AuraEnabled
public static Expense__c saveExpense(Expense__c expense) {
// Perform isUpdatable() check first, then
upsert expense;// upsert slaps a new ID onto this record passed in
return expense;
}
@AuraEnabled
public static 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 Object
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();
return null;
}
}
// OK, they're cool, let 'em through
return [SELECT Id, Name, Amount__c, Client__c, Date__c,
Reimbursed__c, CreatedDate
FROM Expense__c];
}
}
expensesItemUpdate.evt
Custom Event type I made for this project.
<aura:event type="COMPONENT" >
<!-- there are 2 types of Events: APPLICATION and COMPONENT Events -->
<aura:attribute name="expense" type="Expense__c" />
</aura:event>
expenseItem.cmp
this is the most recently edited code, it is what uses the Event I made
<aura:component >
<aura:attribute name="expense" type="Expense__c"/>
<!-- This component FIRES/SENDS an EVENT named "updateExpense" -->
<aura:registerEvent name="updateExpense" type="c:expensesItemUpdate" />
<div class="slds-card">
<!-- Color the item GREEN if the expense is Reimbursed -->
<div class="{!v.expense.Reimbursed__c == true ?
'slds-theme--success' : 'slds-theme--warning'}">
<header class="slds-card__header slds-grid grid--flex-spread">
<a aura:id="expense" href="{!'/' + v.expense.Id}">
<h3>{!v.expense.Name}</h3>
</a>
</header>
<section class="slds-card__body">
<div class="slds-title slds-hint-parent">
<p class="slds-title__title slds-truncate">Amount:
<ui:outputCurrency value="{!v.expense.Amount__c}"/>
</p>
<p class="slds-truncate">Client:
<ui:outputText value="{!v.expense.Client__c}"/>
</p>
<p class="slds-truncate">Date:
<ui:outputDate value="{!v.expense.Date__c}"/>
</p>
<p class="slds-truncate">Reimbursed?:
<ui:inputCheckbox value="{!v.expense.Reimbursed__c}"
click="{!c.clickReimbursed}"/>
</p>
</div>
</section>
</div>
</div>
</aura:component>
expensesController.js
This is the JavaScript that fires the Event
({
clickReimbursed : function(component, event, helper) {
var expense = component.get("v.expense");
var updateEvent = component.getEvent("updateExpense");
udateEvent.setParams({ "expense": expense});
updateEvent.fire();
}
})