function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Alexander PlacidiAlexander Placidi 

Lightning Components Basics: Connect to Salesforce with Server-Side Controllers - Callback Function always returns error

Hello,

I am struggling with this trailhead challenge. I passed the challenge, but it doesn't work, when I do manually in the browser:

Repro:
Preview the camping app
Create a new camping item
Callback function return error state. 

This is my code:
camping list component:
<aura:component controller="CampingController">
    <aura:attribute name="items" type="Camping_Item__c[]"/>
    <aura:attribute name="newItem" type="Camping_Item__c" 
                    default="{'sObjectType':'Camping_Item__c', 'Price__c':0, 'Quantity__c':0}"/>
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    
    <form>
    	<ui:inputText aura:id="name" label="Name" value="{!v.newItem.Name}" required="true"/>
        <ui:inputNumber aura:id="quantity" label="Quantity" value="{!v.newItem.Quantity__c}" required="true"/>
        <ui:inputCurrency aura:id="price" label="Price" value="{!v.newItem.Price__c}" required="true"/>
        <ui:inputCheckbox aura:id="packed" label="Packed?" value="{!v.newItem.Packed__c}"/>
        <ui:button label="Create Item" press="{!c.clickCreateItem}"/>
    </form>
    
    <aura:iteration items="{!v.items}" var="item">
        <c:campingListItem item="{!item}"/>
    </aura:iteration>
    
</aura:component>

camping list controller: 
 
({
     doInit: function(component, event, helper) {
        // Create the action
        var action = component.get("c.getItems");
    
        // 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.items", response.getReturnValue());
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
    
        // Send action off to be executed
        $A.enqueueAction(action);
    },   
	clickCreateItem : function(component, event, helper) {
        if(helper.validateItem(component)){
            // Create the new expense
            var newItem = component.get("v.newItem");
            console.log("Create Item: " + JSON.stringify(newItem));
            helper.createItem(component, newItem);
        }
  
	}
})

camping list helper:
({
	validateItem : function(component) {
		var validItem = true;
        
        var nameField = component.find("name");
        var itemName = nameField.get("v.value");
        
        var priceField = component.find("price");
        var itemPrice = priceField.get("v.value");
        
        var quantityField = component.find("quantity");
        var itemQuantity = quantityField.get("v.value");
        
        if ($A.util.isEmpty(itemName)){
            validItem = false;
            nameField.set("v.errors", [{message:"Item name can't be blank."}]);
        } else {
            nameField.set("v.errors", null);
        }
        
        if ($A.util.isEmpty(itemPrice)){
            validItem = false;
            priceField.set("v.errors", [{message:"Price can't be blank."}]);
        } else {
            priceField.set("v.errors", null);
        }
        
        if ($A.util.isEmpty(itemQuantity)){
            validItem = false;
            quantityField.set("v.errors", [{message:"Quantity can't be blank."}]);
        } else {
            quantityField.set("v.errors", null);
        }	
        console.log("valid: " + validItem);
        return validItem;
	},
    createItem: function(component, newItem) {
    	var action = component.get("c.saveItem");
        action.setParams({"newItem":newItem});

    	action.setCallback(this, function(response){
        	var state = response.getState();
            console.log("state: " + state);
        	if (component.isValid() && state === "SUCCESS") {
            	var items = component.get("v.items");
            	items.push(response.getReturnValue());
            	component.set("v.items", items);
                
                component.set("v.newItem",{'sobjectType':'Camping_Item__c',
                                   'Name': '',
                                   'Quantity__c': 0,
                                   'Price__c': 0,
                                   'Packed__c': false}); 
        	}
    	});
    	$A.enqueueAction(action);
                
    },
})

Server side camping controller:
({
	validateItem : function(component) {
		var validItem = true;
        
        var nameField = component.find("name");
        var itemName = nameField.get("v.value");
        
        var priceField = component.find("price");
        var itemPrice = priceField.get("v.value");
        
        var quantityField = component.find("quantity");
        var itemQuantity = quantityField.get("v.value");
        
        if ($A.util.isEmpty(itemName)){
            validItem = false;
            nameField.set("v.errors", [{message:"Item name can't be blank."}]);
        } else {
            nameField.set("v.errors", null);
        }
        
        if ($A.util.isEmpty(itemPrice)){
            validItem = false;
            priceField.set("v.errors", [{message:"Price can't be blank."}]);
        } else {
            priceField.set("v.errors", null);
        }
        
        if ($A.util.isEmpty(itemQuantity)){
            validItem = false;
            quantityField.set("v.errors", [{message:"Quantity can't be blank."}]);
        } else {
            quantityField.set("v.errors", null);
        }	
        console.log("valid: " + validItem);
        return validItem;
	},
    createItem: function(component, newItem) {
    	var action = component.get("c.saveItem");
        action.setParams({"newItem":newItem});

    	action.setCallback(this, function(response){
        	var state = response.getState();
            console.log("state: " + state);
        	if (component.isValid() && state === "SUCCESS") {
            	var items = component.get("v.items");
            	items.push(response.getReturnValue());
            	component.set("v.items", items);
                
                component.set("v.newItem",{'sobjectType':'Camping_Item__c',
                                   'Name': '',
                                   'Quantity__c': 0,
                                   'Price__c': 0,
                                   'Packed__c': false}); 
        	}
    	});
    	$A.enqueueAction(action);
                
    },
})
It seems there is a problem wth the invocation of the saveItem-method. The response is always 'ERROR'
What I am missing here? I'm getting crazy....
Thank you for any hints
Alexander PlacidiAlexander Placidi

The ouput in line 43 is: 'state: Error' 

It doesn't save the record in the database. 

piyush.mathurpiyush.mathur
Hi Alexander Placidi - 
The parameter you setting for - 
action.setParams({"newItem":newItem});
is newItem is the same variable name that you have passed to server side method ?
try making them same like - 
@AuraEnabled
public static Camping_Item__c saveItem(Camping_Item__c newItem) {
 // stuff 
}
hope this will help.
Alexander PlacidiAlexander Placidi

Hi

the parameters definetely match, I doubled checked this. I solved the problem by using a String rather than sObject as a parameter
 

({
	updateItem: function(component, item) {
    	var action = component.get("c.saveItem");
    	action.setParams({
        	"item": JSON.stringify(item)
    	});
    	action.setCallback(this, function(response){
            var state = response.getState();
        	if (component.isValid() && state === "SUCCESS") {
            	// do nothing!
        	}
    	});
    	$A.enqueueAction(action);
	}

})
 
@AuraEnabled
    public static Camping_Item__c saveItem(String item) {
        //System.debug('Before: ' + newItem);
        //Camping_Item__c newItem = new Camping_Item__c();
        //newItem.Name = 'Test';
        //newItem.Price__c = 1;
        //newItem.Quantity__c = 1;
        Camping_Item__c sObj = (Camping_Item__c) JSON.deserialize(item, Camping_Item__c.class);
        
        UPSERT sObj;
        
        return sObj;
    }


If I use JSON.stringify and JSON.deserialize, it works. In the trailhead they clearly state, that you can pass sObject safely, but it seems to be buggy. 

Thank you for the feedback. 

Best