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
Manuel CasasManuel Casas 

Lightning Components Basics - Connect to Salesforce with Server-Side Controllers

Hi everyone!
I have a problem with the challenge, my App work fine but I don´t pass the challenge. This is my code:

campingList.cmp:
 
<aura:component controller="CampingListController">
    
    <ltng:require styles="/resource/SLDS105/assets/styles/salesforce-lightning-design-system-ltng.css"/>

	<aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    
     <aura:attribute name="newItem" type="Camping_Item__c"
     default="{ 'sobjectType': 'Camping_Item__c',
                    'Price__c': 0,
                    'Quantity__c': 0}"/>
    
    <div class="slds-card slds-p-top--medium">
    <ui:inputText aura:id="campname" label="Camping Name"
        value="{!v.newItem.Name}" required="true"/>
    <ui:inputCheckbox aura:id="packed" label="Packed?"
        value="{!v.newItem.Packed__c}"/>
     <ui:inputCurrency aura:id="price" label="Price"
        value="{!v.newItem.Price__c}" required="true"/>
     <ui:inputNumber aura:id="quantity" label="Quantity"
        value="{!v.newItem.Quantity__c}" required="true"/>
    
    <ui:button label="Create Camping" press="{!c.clickCreateCamping}"/>
    </div>
    
    <aura:attribute name="items" type="Camping_Item__c[]"/>	 
    <div class="slds-card slds-p-top--medium">
        <header class="slds-card__header">
            <h3 class="slds-text-heading--small">Campings</h3>
        </header>
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="item">
                    <c:campingListItem item="{!item}"/>
                </aura:iteration>
            </div>
        </section>
    </div>
</aura:component>

campingListController.js:
 
({
    
    // Load expenses from Salesforce
	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);
	},
    
    clickCreateCamping: function(component, event, helper) {
        
        if(helper.validateCampingForm(component)){
	        // Create the new expense
	        var newCamping = component.get("v.newItem");
	        helper.createItem(component, newCamping);
	    }
    }
})
campingListHelper.js
 
({
    
    createItem: function(component, camping) {
        
        var action = component.get("c.saveItem");
        action.setParams({
            "item": camping
        });
    	action.setCallback(this, function(response){
        var state = response.getState();
        if (component.isValid() && state === "SUCCESS") {
            var campings = component.get("v.items");
            campings.push(response.getReturnValue());
            component.set("v.items", campings);
        }
    	});
    	$A.enqueueAction(action);
    },
    
    validateCampingForm: function(component) {
      
     	var validQuantity = true;
        var validPrice = true;

        var nameField = component.find("campname");
        var campname = nameField.get("v.value");
        
        var quantityField = component.find("quantity");
        var quantity = quantityField.get("v.value");
        
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        
        if ($A.util.isEmpty(campname) || $A.util.isEmpty(quantity) || $A.util.isEmpty(price)){
            validQuantity = false;
            validPrice = false;
            nameField.set("v.errors", [{message:"Camping name, quantity or price can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
		
		return(validQuantity && validPrice);        
	}
})
CampingListController.apxc:
 
public with sharing class CampingListController {

    @AuraEnabled
    public static List<Camping_Item__c> getItems() {
    
        // Check to make sure all fields are accessible to this user
        String[] fieldsToCheck = new String[] {
            'Id', 'Name', 'Packed__c', 'Price__c', 'Quantity__c'
        };
        
        Map<String,Schema.SObjectField> fieldDescribeTokens = 
            Schema.SObjectType.Camping_Item__c.fields.getMap();
        
        for(String field : fieldsToCheck) {
            if( ! fieldDescribeTokens.get(field).getDescribe().isAccessible()) {
                throw new System.NoAccessException();
                return null;
            }
        }        
        
        // Perform isAccessible() checking first, then
        return [SELECT Id, Name, Packed__c, Price__c, Quantity__c 
                FROM Camping_Item__c];
    }
    
    @AuraEnabled
    public static Camping_Item__c saveItem(Camping_Item__c item) {
        // Perform isUpdatable() checking first, then
        upsert item;
        return item;
    }
}
I am still getting this error:

Challenge Not yet complete... here's what's wrong:
The campingList JavaScript helper isn't saving the new record to the database or adding it to the 'items' value provider.


My App save the new record into the database and add it to the "items" list.
Thanks for your answers!




 
Best Answer chosen by Manuel Casas
Jeff DouglasJeff Douglas
All, sorry for the inconvenience. I am trying to get this fixed in production. In the meantime, in on line #12 in Manuel's campingListHelper.js, if you rename the variable to "items" that should do the trick. Everything else looks correct.

Jeff Douglas
Trailhead Developer Advocate

All Answers

Mouhamed N'DIONGUEMouhamed N'DIONGUE
I have the exact issue. Everything works fine, my records are saved in the database too and as you do my createItem is really in the helper side but I still get the same error. Thinking about some typos about this challenge
Mateen GreenwayMateen Greenway
I have the same problem, I'm getting the same error message ...
Jeff DouglasJeff Douglas
All, sorry for the inconvenience. I am trying to get this fixed in production. In the meantime, in on line #12 in Manuel's campingListHelper.js, if you rename the variable to "items" that should do the trick. Everything else looks correct.

Jeff Douglas
Trailhead Developer Advocate
This was selected as the best answer
Manuel CasasManuel Casas
Thank you very much Jeff! I just passed the challenge!
MAURICIO ALEXANDRE SILVAMAURICIO ALEXANDRE SILVA
Thank you very much Jeff! I just passed the challenge!
MattEvansMattEvans
Jef you know you're the man, and you've saved my bacon many times (including here) but wow please please please find a way to get the team to write code tests that don't rely on PARSING the code it'self. Or at the very least specify in the description exactly what naming that has to be followed and exactly where. Spent an hour pulling hair re-writing this after I had it working in 10mins! :-@ 
Jeff DouglasJeff Douglas
We just updated this module with some new code. Please try the challenge check again. Sorry for the inconvenience. 

​Jeff Douglas
Trailhead Developer Advocate
Fnu SumitFnu Sumit
Manuel Casas:
 thank you for code but it is not saving records through web form while i try to save them.
Fnu SumitFnu Sumit
can you please explain it please
Quang Tran NhatQuang Tran Nhat
thank you Manuel Casas for full code effort , and thank you Jeff Douglas for good comments. I try and pass it. Hope to get a next level near you all.
Akshay Deshmukh1Akshay Deshmukh1
Hello Everyone,

I also ran into this problem and read what challenge asks us to do line by line.

Trailhead challenge do not ask us to call HELPER method from campingListController.js. It just tells us to perform save action in campingListcontroller.js method instead. This is not very intuitive as trailhead module performs saving operation in HELPER method.

Just copy and past the code from campingListHelper.js to campingListController.js to pass the challenge. This is weird but this is how it is.

I hope this would help others.

Thanks,
Akshay
Hans LissHans Liss
Akshay Deshmukh1, For the previous unit, "Input Data Using Forms", I had to move the code *from* campingListHelper.js to campingListController.js in order to get it approved. For this unit, however, I had to move the code *back* to the helper in order to get it approved.

In case anyone wonders, using a namespaced dev org (which entails having to add the namespace plus two underscores to every custom field name in the Camping_Item__c object in the code), does not seem to be a problem. I implemented the code for the challenge with some apprehension since I expected the namespace to break it, but it didn't.
Akshay Deshmukh1Akshay Deshmukh1
Hans Liss, Glad that your code worked with Namespaced dev org. I had many problems in doing so.
PeteyBPeteyB
Sorry my OCD won't let it be .... :)
This is the proper validateCampingForm, you are setting the "errors" on the nameField not the respective fields that are errored and having two booleans to track valid in your function is redundant
 
validateCampingForm: function(component) {
      
     	var validName = true;
        var validQuantity = true;
        var validPrice = true;

        var nameField = component.find("campname");
        var campname = nameField.get("v.value");
        
        var quantityField = component.find("quantity");
        var quantity = quantityField.get("v.value");
        
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        
        if ($A.util.isEmpty(campname)){
            validName = false;
            nameField.set("v.errors", [{message:"Camping name can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
        
        if ($A.util.isEmpty(quantity)){
            validQuantity = false;
            quantityField.set("v.errors", [{message:"Camping quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
        
        if ($A.util.isEmpty(price)){
            validPrice = false;
            priceField.set("v.errors", [{message:"Camping price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
		
		return(validName && validQuantity && validPrice);        
	}

 
fifedog15fifedog15
This is funny... original post was his app worked but didn't pass the challenge.  My app doesn't work but it did pass the challenge... oh well!
Rondee Juan 1Rondee Juan 1

Hi Everyone,

Codes below works perfecly fine.

campingListController.js

({
	clickCreateCamping : function(component, event, helper) {
               
        if(helper.createItem(component)){            
            var newItem = component.get("v.newItem");
            helper.createCamping(component, newItem);
        }                          
              
	},
    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);
    },      
})
 

campingList.cmp

<aura:component controller="CampingListController">
    
    <aura:attribute name="items" type="Camping_Item__c[]" />    
    <aura:attribute name="newItem" type="Camping_Item__c"                     
                    default="{ 'sobjectType': 'Camping_Item__c',
                                'Name': '',
                             	'Quantity__c': 0,
                                'Price__c': 0,
                                'Packed__c': false
                             }"/> 
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <!-- NEW EXPENSE FORM -->
    <div class="slds-col slds-col--padded slds-p-top--large">        
          
          <div aria-labelledby="campingform">
        
          <!-- BOXED AREA -->
          <fieldset class="slds-box slds-theme--default slds-container--small">
        
            <legend id="campingform" class="slds-text-heading--small 
              slds-p-vertical--medium">
              Add Camping
            </legend>
        
            <!-- CREATE NEW CAMPING FORM -->
            <form class="slds-form--stacked">
        
              <div class="slds-form-element slds-is-required">
                  <div class="slds-form-element__control">
                      <ui:inputText aura:id="Name" label="Name" class="slds-input"
                          labelClass="slds-form-element__label"
                          value="{!v.newItem.Name}"
                          required="true"/>
                  </div>
             </div>
        
             <div class="slds-form-element slds-is-required">
                  <div class="slds-form-element__control">
                      <ui:inputNumber aura:id="Quantity" label="Quantity"
                          class="slds-input"
                          labelClass="slds-form-element__label"
                          value="{!v.newItem.Quantity__c}"
                          required="true"/>
        
                  </div>
              </div>
        
              <div class="slds-form-element">
                  <div class="slds-form-element__control">
                      <ui:inputNumber aura:id="Price" label="Price"
                          class="slds-input"
                          labelClass="slds-form-element__label"
                          value="{!v.newItem.Price__c}"
                          placeholder="0.00"/>
                  </div>
              </div>
                
              <div class="slds-form-element">
                  <ui:inputCheckbox aura:id="Packed" label="Packed?"
                      class="slds-checkbox"
                      labelClass="slds-form-element__label"
                      value="{!v.newItem.Packed__c}"/>
              </div>
        
              
              <div class="slds-form-element">
                  <ui:button label="Create Camping"
                      class="slds-button slds-button--brand"
                      press="{!c.clickCreateCamping}"/>
              </div>
        
            </form>
            <!-- / CREATE NEW CAMPING FORM -->
        
          </fieldset>
          <!-- / BOXED AREA -->
        
        </div>
        <!-- / CREATE NEW CAMPING -->
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="item">
                    <c:campingListItem item="{!item}"/>
                </aura:iteration>
            </div>
        </section>          
        
        
    </div>    
    
</aura:component>
 

campingListHelper.js

({
	createCamping : function(component, item) {  		
        
        var action = component.get("c.saveItem");
        action.setParams({
            "item": item
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                	console.log('pasok1');       
                    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);        
	},
    createItem: function(component, camping) { 
		var name = component.find('Name').get("v.value");
        var quantity = component.find('Quantity').get("v.value");
        var price = component.find('Price').get("v.value");
        var validation = true;
        
        if($A.util.isEmpty(name)) {
            validation = false;
            component.find("Name").set("v.errors", [{message:"Camping name can't be blank."}]);
        }
        if(quantity == 0) {
            validation = false;
            component.find("Quantity").set("v.errors", [{message:"Camping quantity can't be blank."}]);
        }
        if(price == 0) {
            validation = false;
            component.find("Price").set("v.errors", [{message:"Camping price can't be blank."}]);
        }         
        console.log(validation);
         return(validation);
    }
})
 

CampingListController.apxc

public with sharing class CampingListController {
	
    @AuraEnabled
    public static List<Camping_Item__c> getItems() {
        
        
        // Check to make sure all fields are accessible to this user
        String[] fieldsToCheck = new String[] {
            'Id', 'Name', 'Packed__c', 
            'Price__c', 'Quantity__c'
        };        
        
        
        Map<String,Schema.SObjectField> fieldDescribeTokens = 
            Schema.SObjectType.Camping_Item__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, Packed__c, Price__c, Quantity__c 
                FROM Camping_Item__c];        
        
    } 
    
    @AuraEnabled
    public static Camping_Item__c saveItem (Camping_Item__c item) {
        upsert item;
        return item;
    }
    
}
Jaseem Pookandy 8Jaseem Pookandy 8
I have been banging my head on this for a while now... can't find out whats wrong.. passed the challenge, but my app is not working... here is the code..

campingListController.js
({
	
    doInit : function(component,event,helper){
        var action = component.get("c.getItems");
        
        action.setCallback(this,function(response){
            var state = response.getState();
            console.log("state in init "+state);
            if(component.isValid() && state === "SUCCESS"){
                component.set("v.items",response.getReturnValue());
            }else{
                console.log("Failed with state: " + state);

            }
        });
        
        $A.enqueueAction(action);
    },
    clickCreateCamping : function(component, event, helper) {		
       
        if(helper.validateform(component)){
            console.log("entered the block");
           var newcamping = component.get("v.newItem");
            helper.createItem(component,newcamping);
           
        }
	}
})

CampingListController.apxc
public with sharing class CampingListController {

    @AuraEnabled
    public static list<Camping_Item__c> getItems(){
        return([select id,name,packed__c,quantity__c,price__c from Camping_Item__c]);
    }
    
    @AuraEnabled
    public static Camping_Item__c saveItem(Camping_Item__c campitem){
        system.debug('### this is called? ');
        insert campitem;
        return campitem;
    }
}

CampingListHelper.js
({
	
    createItem : function(component, camping){
         var action = component.get("c.saveItem"); 
        console.log("this is saveitem "+action);
            action.setParams({
                "campitem":camping
            });           
            action.setCallback(this,function(response){
                var state = response.getState();
                console.log("what is state "+state);
                if(component.isValid() && state ==="SUCCESS"){ 
                   var items = component.get("v.items");
                    items.push(response.getReturnValue());
                    component.set("v.items",items);
                }                
            });
            $A.enqueueAction(action);
    },
    validateform : function(component) {
		var validinput = true;
         
        var namefield = component.find("cliname");
        var namevalue = namefield.get("v.value");
        
        
        if($A.util.isUndefined(namevalue) ) {
            validinput = false;
            namefield.set("v.errors",[{message:"name cannot be null"}]);
        }else {
            namefield.set("v.errors",null);           
        }
        console.log("validate "+validinput);
        return validinput;
	}
})

Can you guys help me out!!! please@!!
David Roberts 4David Roberts 4
Hi Jeff,
Why is there no official place showing the solution.
I did the challenge following the structure in the module and it worked but failed the challenge.
I moved stuff around and it failedto work but passed the challenge.
I decided to retry and now it doesn't work and all the threads are unsatisfactory.
This is dis-heartening.
I hear so many good things about trailhead but my experience is not a happy one.

...back to the drawing board.
David Roberts 4David Roberts 4
I did it!
See https://developer.salesforce.com/forums/?id=9060G000000XeVtQAK
Patrick McClellanPatrick McClellan
I passed the validations, but it won't load in the browser. Gives me this error:
This page has an error. You might just need to refresh it. Component class instance initialization error [Cannot read property 'apply' of undefined] Failing descriptor: {markup://c:camping}

Any ideas.
 
Riyaz LakhaniRiyaz Lakhani
Try this ..this is working fine ..Records are being saved in Database.

campingList.cmp

<aura:component controller="CampingListController">
    
    <aura:attribute name="items" type="Camping_Item__c[]"/>
    <aura:attribute name="newItem" type="Camping_Item__c"
                    default="{'sobjectType':'Camping_Item__c',
                             'Name':'',
                              'Quantity__c':0,
                              'Price__c':0,
                              'Packed__c':false}"/>
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    
    <!-- NEW Campaing FORM -->
    <div class="slds-col slds-col--padded slds-p-top--large">
           
        <!-- [[ Campaing form goes here ]] -->
        
        <div aria-labelledby="newCampaingForm">
            
            <!-- BOXED AREA -->
            <fieldset class="slds-box slds-theme--default slds-container--small">
                
                <legend id="newCampaingForm" class="slds-text-heading--small
                                                    slds-p-vertical--medium">
                    Add Expense
                </legend>
                
                <!-- CREATE NEW Campaing FORM -->
                <form class="slds-form--stacked">
                    
                    <div class="slds-form-element slds-is-required">
                        <div class="slds-form-element__control">
                            <ui:inputText aura:id="campingName" label="Camping Name"
                                          class="slds-input"
                                          labelClass="slds-form-element__label"
                                          value="{!v.newItem.Name}"
                                          required="true"/>
                        </div>
                    </div>
                    
                    <div class="slds-form-element slds-is-required">
                        <div class="slds-form-element__control">
                            <ui:inputNumber aura:id="quantity" label="Quantity"
                                            class="slds-input"
                                            labelClass="slds-form-element__label"
                                            value="{!v.newItem.Quantity__c}"
                                            required="true"/>       
                        </div>
                    </div>
                    
                    <div class="slds-form-element">
                        <div class="slds-form-element__control">
                            <ui:inputCurrency aura:id="price" label="Price"
                                              class="slds-input"
                                              labelClass="slds-form-element__label"
                                              value="{!v.newItem.Price__c}"/>
                        </div>
                    </div>
                    
                    <div class="slds-form-element">
                        <ui:inputCheckbox aura:id="packed" label="Packed ?"
                                          class="slds-checkbox"
                                          labelClass="slds-form-element__label"
                                          value="{!v.newItem.Packed__c}"/>
                    </div>
                    
                    <div class="slds-form-element">
                        <ui:button label="Create Expense"
                                   class="slds-button slds-button--brand"
                                   press="{!c.createCampingList}"/>
                    </div>
                    
                </form>
                <!-- / CREATE NEW EXPENSE FORM -->
                
            </fieldset>
            <!-- / BOXED AREA -->
            
        </div>
        <!-- / CREATE NEW EXPENSE -->
    </div>
    
    <!-- ITERATIING ITEM LISTS -->
    <div class="slds-card slds-p-top--medium">
        
        <!-- PAGE HEADER -->
        <div class="slds-page-header" role="banner">
            <div class="slds-grid">
                <div class="slds-col">
                    <h1 class="slds-text-heading--medium">Camping List</h1>
                </div>
            </div>
        </div>
        <!-- / PAGE HEADER -->
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="item">
                    <c:campingListItem item="{!item}"/>
                </aura:iteration>
            </div>
        </section>
    </div>    
    <!-- / ITERATIING ITEM LISTS -->
 
</aura:component>


campingListController.js

({
    // Load expenses from Salesforce
    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);
    },
    
    createCampingList : function(component, event, helper) {
    
    if(helper.validateCampingForm(component)){
    // Create the new expense
    var newCampingItem = component.get("v.newItem");
    helper.createItem(component,newCampingItem);
}    

 /*      
        if(isCampingValid){
            var newCampingItem = component.get("v.newItem");
            //helper.createCamping(component,newCampingItem);
            var campings = component.get("v.items");
            var item = JSON.parse(JSON.stringify(newCampingItem));
            
            campings.push(item);
            
            component.set("v.items",campings);
            component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                                       'Name': '',
                                       'Quantity__c': 0,
                                       'Price__c': 0,
                                       'Packed__c': false });
        }
        
        */
 
 }
 })


campingListHelper

({
    createItem : function(component, camping) {
        var action = component.get("c.saveItem");
        action.setParams({
            "item": camping
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                console.log('State== Success');
                var campings = component.get("v.items");
                campings.push(response.getReturnValue());
                component.set("v.items", campings);
                
                component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                                               'Name': '',
                                               'Quantity__c': 0,
                                               'Price__c': 0,
                                               'Packed__c': false });
            }
        });
        $A.enqueueAction(action);
    },
    
    validateCampingForm: function(component) {
        
        //Error Checking
        var isCampingValid = true;
        
        var nameField = component.find("campingName");
        var quantityField = component.find("quantity");
        var priceField = component.find("price");
        
        //Name should not be blank
        if(nameField.get("v.value") == '' ){
            nameField.set("v.errors",[{message:"Name can't be blank"}]);
            isCampingValid = false;
        }
        else{
            nameField.set("v.errors",null);
        }
        
        //Quantity should not be blank
        if( quantityField.get("v.value") == '' ){
            quantityField.set("v.errors",[{message:"Quantity can't be blank"}]);
            isCampingValid = false;
        }
        else{
            quantityField.set("v.errors",null);
        }
        
        //Price Shoud not be blank
        if(priceField.get("v.value") == ''){
            priceField.set("v.errors",[{message:"Price can't be blank"}]);
            isCampingValid = false;
        }
        else{
            priceField.set("v.errors",null);
        }
        return(isCampingValid);
    },
})


CampingListController.apxc


public with sharing class CampingListController {
    
    @AuraEnabled
    public static List<Camping_Item__c> getItems() {
          
        // Check to make sure all fields are accessible to this user
        String[] fieldsToCheck = new String[] {
            'Id', 'Name', 'Price__c', 'Quantity__c', 'Packed__c'
        };        
        
        
        Map<String,Schema.SObjectField> fieldDescribeTokens =
            Schema.SObjectType.Camping_Item__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, Price__c, Quantity__c, Packed__c FROM Camping_Item__c];        
        
    }
    
    @AuraEnabled
    public static Camping_Item__c saveItem (Camping_Item__c item) {
        upsert item;
        return item;
    }
    
}


campingListItem.cmp

<aura:component implements="force:appHostable">
    <aura:attribute name="item" type="Camping_Item__c" />
    <p>Name :
        <ui:outputText value="{!v.item.Name}"/>
    </p>
    <p>Quantity :
        <ui:outputNumber value="{!v.item.Quantity__c}" />
    </p>
    <p>Price :
        <ui:outputCurrency value="{!v.item.Price__c}"/>
    </p>
    <p>Packed? :  
        <ui:outputCheckbox value="{!v.item.Packed__c}"/>
    </p>
</aura:component>
 
Francis CrumpFrancis Crump
I've tried all of the above and none of them work.  error message is 
Challenge Not yet complete... here's what's wrong: 
The Apex controller CampingListController doesn't have a 'getItems()' or 'saveItem(Camping_Item__c item)' method.
Faiza Naz 10Faiza Naz 10
Trailhead is changed a liltle more. you can find solution here.
​http://faizanaz90.blogspot.com/2017/11/lightning-components-basics-connect-to.html
Stalin CampusStalin Campus
Resolved this issue . Changed the code in CampingListForm . No need to change anything else.
CampingListForm

<aura:component >
    <aura:registerEvent name="addItem" type="c:addItemEvent"/>
    
    <aura:attribute name="newItem" type="Camping_Item__c"
     default="{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Quantity__c': 1,
                    'Price__c': 0,
                    'Packed__c': false }"/>
        <!-- CREATE NEW ITEM FORM -->
    <form class="slsd-form--stacked">
            <lightning:input aura:id="campingform" label="Camping Name"
                                     name="campingname"
                                     value="{!v.newItem.Name}"
                                     required="true"/> 
                    <lightning:input type="number" aura:id="campingform" label="Quantity"
                                     name="campingQuantity"
                                     min="1"                                    
                                     step="1"
                                     value="{!v.newItem.Quantity__c}"
                                     messageWhenRangeUnderflow="Enter an Quantity that's at least 1."/>
                      <lightning:input type="number" aura:id="campingform" label="Price"
                                     name="campingPrice"
                                     min="0.1"
                                     formatter="currency"
                                     step="0.1"
                                     value="{!v.newItem.Price__c}"
                                     messageWhenRangeUnderflow="Enter an Price that's at least 0.1."/>
                    <lightning:input type="checkbox" aura:id="campingform" label="Packed ?"  
                                     name="campingPacked"
                                     checked="{!v.newItem.Packed__c}"/>
                    <lightning:button label="Create Camping" 
                                      class="slds-m-top--medium"
                                      variant="brand"
                                      onclick="{!c.clickCreateItem}"/>
        </form>
    <!-- / CREATE NEW ITEM FORM -->
</aura:component>
shashi kumar 58shashi kumar 58
Please follow the below url for the answer:-

https://developer.salesforce.com/forums/?id=906F0000000kEd9IAE
shashi kumar 58shashi kumar 58
Lightning Components Basics
Connect to Salesforce with Server-Side Controllers 
Step 1:- campingList.cmp
<aura:component controller="CampingListController">
    
    <aura:attribute name="items" type="Camping_Item__c[]"/>
    <aura:attribute name="newItem" type="Camping_Item__c"
                    default="{'sobjectType':'Camping_Item__c',
                             'Name':'',
                              'Quantity__c':0,
                              'Price__c':0,
                              'Packed__c':false}"/>
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    
    <!-- NEW Campaing FORM -->
    <div class="slds-col slds-col--padded slds-p-top--large">
           
        <!-- [[ Campaing form goes here ]] -->
        
        <div aria-labelledby="newCampaingForm">
            
            <!-- BOXED AREA -->
            <fieldset class="slds-box slds-theme--default slds-container--small">
                
                <legend id="newCampaingForm" class="slds-text-heading--small
                                                    slds-p-vertical--medium">
                    Add Expense
                </legend>
                
                <!-- CREATE NEW Campaing FORM -->
                <form class="slds-form--stacked">
                    
                    <div class="slds-form-element slds-is-required">
                        <div class="slds-form-element__control">
                            <ui:inputText aura:id="campingName" label="Camping Name"
                                          class="slds-input"
                                          labelClass="slds-form-element__label"
                                          value="{!v.newItem.Name}"
                                          required="true"/>
                        </div>
                    </div>
                    
                    <div class="slds-form-element slds-is-required">
                        <div class="slds-form-element__control">
                            <ui:inputNumber aura:id="quantity" label="Quantity"
                                            class="slds-input"
                                            labelClass="slds-form-element__label"
                                            value="{!v.newItem.Quantity__c}"
                                            required="true"/>       
                        </div>
                    </div>
                    
                    <div class="slds-form-element">
                        <div class="slds-form-element__control">
                            <ui:inputCurrency aura:id="price" label="Price"
                                              class="slds-input"
                                              labelClass="slds-form-element__label"
                                              value="{!v.newItem.Price__c}"/>
                        </div>
                    </div>
                    
                    <div class="slds-form-element">
                        <ui:inputCheckbox aura:id="packed" label="Packed ?"
                                          class="slds-checkbox"
                                          labelClass="slds-form-element__label"
                                          value="{!v.newItem.Packed__c}"/>
                    </div>
                    
                    <div class="slds-form-element">
                        <ui:button label="Create Expense"
                                   class="slds-button slds-button--brand"
                                   press="{!c.createCampingList}"/>
                    </div>
                    
                </form>
                <!-- / CREATE NEW EXPENSE FORM -->
                
            </fieldset>
            <!-- / BOXED AREA -->
            
        </div>
        <!-- / CREATE NEW EXPENSE -->
    </div>
    
    <!-- ITERATIING ITEM LISTS -->
    <div class="slds-card slds-p-top--medium">
        
        <!-- PAGE HEADER -->
        <div class="slds-page-header" role="banner">
            <div class="slds-grid">
                <div class="slds-col">
                    <h1 class="slds-text-heading--medium">Camping List</h1>
                </div>
            </div>
        </div>
        <!-- / PAGE HEADER -->
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="item">
                    <c:campingListItem item="{!item}"/>
                </aura:iteration>
            </div>
        </section>
    </div>    
    <!-- / ITERATIING ITEM LISTS -->
 
</aura:component>

Step 2:- campingListController.js
({
    // Load expenses from Salesforce
    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);
    },
    
    createCampingList : function(component, event, helper) {
    
    if(helper.validateCampingForm(component)){
    // Create the new expense
    var newCampingItem = component.get("v.newItem");
    helper.createItem(component,newCampingItem);
}    

 /*      
        if(isCampingValid){
            var newCampingItem = component.get("v.newItem");
            //helper.createCamping(component,newCampingItem);
            var campings = component.get("v.items");
            var item = JSON.parse(JSON.stringify(newCampingItem));
            
            campings.push(item);
            
            component.set("v.items",campings);
            component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                                       'Name': '',
                                       'Quantity__c': 0,
                                       'Price__c': 0,
                                       'Packed__c': false });
        }
        
        */
 
 }
 })
Step 3:-Apex class
public with sharing class CampingListController {
    @AuraEnabled
    public static List<Camping_Item__c> getItems() {
        String[] fields = new String[]{'Id','Name','Packed__c','Price__c','Quantity__c'};
        Map<String,Schema.SObjectField> fieldDescriptor = Schema.SObjectType.Camping_Item__c.fields.getMap();

        for(String field : fields) {
            if(!fieldDescriptor.get(field).getDescribe().isAccessible()) {
                throw new System.NoAccessException();
                return null;
            }            
        }        
        return [SELECT Id,Name,Packed__c,Price__c,Quantity__c FROM Camping_Item__c];
    }


    @AuraEnabled  
    public static Camping_Item__c saveItem(Camping_Item__c item) {  
        upsert item;
        return item;
    }

}

Step 4:--CampingListHelper
({
    createItem : function(component, camping) {
        var action = component.get("c.saveItem");
        action.setParams({
            "item": camping
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                console.log('State== Success');
                var campings = component.get("v.items");
                campings.push(response.getReturnValue());
                component.set("v.items", campings);
                
                component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                                               'Name': '',
                                               'Quantity__c': 0,
                                               'Price__c': 0,
                                               'Packed__c': false });
            }
        });
        $A.enqueueAction(action);
    },
    
    validateCampingForm: function(component) {
        
        //Error Checking
        var isCampingValid = true;
        
        var nameField = component.find("campingName");
        var quantityField = component.find("quantity");
        var priceField = component.find("price");
        
        //Name should not be blank
        if(nameField.get("v.value") == '' ){
            nameField.set("v.errors",[{message:"Name can't be blank"}]);
            isCampingValid = false;
        }
        else{
            nameField.set("v.errors",null);
        }
        
        //Quantity should not be blank
        if( quantityField.get("v.value") == '' ){
            quantityField.set("v.errors",[{message:"Quantity can't be blank"}]);
            isCampingValid = false;
        }
        else{
            quantityField.set("v.errors",null);
        }
        
        //Price Shoud not be blank
        if(priceField.get("v.value") == ''){
            priceField.set("v.errors",[{message:"Price can't be blank"}]);
            isCampingValid = false;
        }
        else{
            priceField.set("v.errors",null);
        }
        return(isCampingValid);
    },
})

Step 5:-Add event name as :- addItemEvent.evt
<aura:event type="COMPONENT">
    <aura:attribute name="item" type="Camping_Item__c"/>
</aura:event>

Step 6:- Add Helpper for CampingListFormHelper.js

​  
({
    createItem : function(component,item) {
        var createEvent = component.getEvent("addItem");        
        createEvent.setParams({ "item": item });
        createEvent.fire();        
        component.set("v.newItem",{'sobjectType':'Camping_Item__c','Name': '','Quantity__c': 0,'Price__c': 0,'Packed__c': false});
    }
})
Step 7:- Add new component name as:- campingListItem.cmp
<aura:component implements="force:appHostable">
    <aura:attribute name="item" type="Camping_Item__c" />
    <p>Name :
        <ui:outputText value="{!v.item.Name}"/>
    </p>
    <p>Quantity :
        <ui:outputNumber value="{!v.item.Quantity__c}" />
    </p>
    <p>Price :
        <ui:outputCurrency value="{!v.item.Price__c}"/>
    </p>
    <p>Packed? :  
        <ui:outputCheckbox value="{!v.item.Packed__c}"/>
    </p>
</aura:component>
Step 8: Add controller campingListController.js
({
    // Load expenses from Salesforce
    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);
    },
    
    createCampingList : function(component, event, helper) {
    
    if(helper.validateCampingForm(component)){
    // Create the new expense
    var newCampingItem = component.get("v.newItem");
    helper.createItem(component,newCampingItem);
}    

 /*      
        if(isCampingValid){
            var newCampingItem = component.get("v.newItem");
            //helper.createCamping(component,newCampingItem);
            var campings = component.get("v.items");
            var item = JSON.parse(JSON.stringify(newCampingItem));
            
            campings.push(item);
            
            component.set("v.items",campings);
            component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                                       'Name': '',
                                       'Quantity__c': 0,
                                       'Price__c': 0,
                                       'Packed__c': false });
        }
        
        */
 
 }
 })
Add new Component CampingListForm.cmp
<aura:component controller="CampingListController" >

    <!-- Attribute Definition -->
    <aura:attribute name="newItem"  type="Camping_Item__c" default="{ 'sobjectType': 'Camping_Item__c',
                                                                      'Name':'',
                                                                      'Quantity__c': 0,
                                                                      'Price__c': 0,
                                                                      'Packed__c': false}"  />  
    
    <!-- Register Handlers -->
    <aura:registerEvent name="addItem" type="c:addItemEvent"/>


    <!-- New Camping Form-->  
    <div class="slds-col slds-col--padded slds-p-top--large" >  
        <fieldset class="slds-box slds-theme--default slds-container--small">
            <legend id="newexpenseform" class="slds-text-heading--small" >
                Add Camping List
            </legend>

            <form class="slds-form--stacked">
                
                <!-- Name -->
                <div class="slds-form-element slds-is-required" >
                    <div class="slds-form-element__control" >
                        <lightning:input aura:id="itemform"
                           label="Name"
                           name="itemname"
                           value="{!v.newItem.Name}"
                           required="true"/>
                    </div>
                </div>
                            
                <!-- Quantity -->
                <div class="slds-form-element__label" >
                    <div class="slds-form-element__control" >  
                       <lightning:input type="number"
                       aura:id="itemform"
                       label="Quantity"
                       name="quantity"
                       value="{!v.newItem.Quantity__c}"
                       min="1"
                       required="true"/>
                    </div>                
                </div>

                <!-- Price -->
                <div class="slds-form-element slds-is-required" >
                    <div class="slds-form-element__control" >
                       <lightning:input type="number"
                       aura:id="itemform"
                       label="Price"
                       name="price"
                       value="{!v.newItem.Price__c}"
                       formatter="currency"
                       step="0.01"/>
                    </div>
                </div>

                <!-- Packed -->
                <div class="slds-form-element" >                  
                    <lightning:input type="checkbox"
                       aura:id="itemform"
                       label="Packed?"
                       name="packed"
                       checked="{!v.newItem.Packed__c}"/> 
                </div>

                <!-- Button Create Expense -->
                <div class="slds-form-element">
                    <lightning:button label="Create Camping Item"
                        variant="brand"
                        onclick="{!c.clickCreateItem}"/>  
                </div>

            </form>            
        </fieldset>   
    </div>

</aura:component>

Add ListFormController.js
 
({
    clickCreateItem : function(component, event, helper) {
        var validCampign = true;

        // Name cannot be blank
        var nameField   = component.find("name");
        var campaignname = nameField.get("v.value");
        if($A.util.isEmpty(campaignname)) {
            validCampign = false;
            nameField.set("v.errors",[{message: "Camping name can't be blank."}]);     
        }
        else {
            nameField.set("v.errors",null);
        }

        // Quantity cannot be blank.
        var qty = component.find("quantity");
        var quantity = qty.get("v.value");
        if($A.util.isEmpty(quantity)) {
            validCampign = false;
            qty.set("v.errors",[{message: "Quantity can't be blank."}]);
        }
        else {
            qty.set("v.errors",null);            
        }
		
        // Price cannot be blank.
        var priceField = component.find("price");
        var price      = priceField.get("v.value");
        if($A.util.isEmpty(price)) {
            validCampign = false;
            priceField.set("v.errors",[{message: "Price can't be blank."}]);
        }
        else {
            priceField.set("v.errors",null);            
        }
		
        // No validation errors
        if(validCampign) {
            var newCampaign = component.get("v.newItem");
            helper.createItem(component, newCampaign);
        }               
        
    }

})

Enjoy and finish if you have any question please let me know..

Don't forgot to like.....
 
kgrasukgrasu
I do have a diffrent issue with this challenge. Create camping app works fine and the records are stored in the salesforce camping object, but I don´t pass the challenge.

Error message as below:-
Challenge Not yet complete... here's what's wrong: 
The campingList JavaScript controller isn't calling 'saveItem' in the Apex class to save the record or setting the 'item' as a parameter for the 'saveItem' action.
Sudharsan CSudharsan C
CampingList.cmp(Component):

<aura:component controller="CampingListController" implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >
    <aura:attribute name="items" type="Camping_Item__c[]" />
    <aura:attribute name="newItem" type="Camping_Item__c" default="{'Price__c': 0, 'Packed__c': false, 'Quantity__c': 0, 'Name':'Test', 'sobjectType': 'Camping_Item__c'}" />
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
    <lightning:layout>
        <lightning:layoutItem padding="around-small" size="4">
            <!-- CREATE NEW EXPENSE -->
            <div aria-labelledby="newCampingItem">
                <!-- BOXED AREA -->
                <fieldset class="slds-box slds-theme--default slds-container--small">
                    <legend id="newCampingItem" class="slds-text-heading--small
                                                       slds-p-vertical--medium">
                        Add Camping Item
                    </legend>
                    
                    <!-- CREATE NEW EXPENSE FORM -->
                    <form class="slds-form--stacked">          
                        <lightning:input aura:id="campingItemForm" label="Name"
                                         name="itemName"
                                         value="{!v.newItem.Name}"
                                         required="true"/>
                        
                        <lightning:input type="number" aura:id="campingItemForm" label="Quantity"
                                         name="itemQuantity"
                                         min="1"
                                         step="1"
                                         value="{!v.newItem.Quantity__c}"
                                         messageWhenRangeUnderflow="Enter the quantity atleast 1"/>
                        
                        <lightning:input type="number" aura:id="campingItemForm" label="Price"
                                         name="itemPrice"
                                         min="0.1"
                                         formatter="currency"
                                         step="0.01"
                                         value="{!v.newItem.Price__c}" />
                        
                        <lightning:input type="checkbox" aura:id="campingItemForm" label="Packed ?"  
                                         name="itemPacked"
                                         checked="{!v.newItem.Packed__c}"/>
                        
                        <lightning:button label="Create Item"
                                          class="slds-m-top--medium"
                                          variant="brand"
                                          onclick="{!c.clickCreateItem }"/>
                    </form>
                    <!-- / CREATE NEW EXPENSE FORM -->
                    
                </fieldset>
                <!-- / BOXED AREA -->
            </div>
            <!-- / CREATE NEW EXPENSE -->
        </lightning:layoutItem>
        
        <lightning:layoutItem padding="around-small" size="3">
            <aura:Iteration items="{!v.items}" var="item">
                <c:campingListItem item="{!item}" />
            </aura:Iteration>
        </lightning:layoutItem>
    </lightning:layout>
    <!--
 <ol>
     <li>Bug Spray</li>
        <li>Bear Repellant</li>
        <li>Goat Food</li>
    </ol>
-->
</aura:component>


CampingListcontroller.js: (Controller)

({
    clickCreateItem : function(component, event, helper) {
        var isFormValid = component.find("campingItemForm").reduce(function(isValid, inputCmp){
            inputCmp.showHelpMessageIfInvalid();        
            return isValid && inputCmp.get("v.validity").valid;
        });
        
        if (isFormValid) {
            
            var newCampingItem = component.get("v.newItem");
            helper.createItem(component,newCampingItem);
           
        }
    },
    
    doInit : function (component, event, helper) {
        var action = component.get("c.getItems");
        action.setCallback(this,function (response) {
            var campingItems = response.getReturnValue();
            component.set("v.items",campingItems);
        });
        $A.enqueueAction(action);
    }
})

CampingListHelper.js (Helper):

({
    createItem : function(component,newCampingItem) {
        var action = component.get("c.saveItem");
        action.setParams({
            "campingItem" : newCampingItem
        });
        
        action.setCallback(this,function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                var parsedCampingItem = JSON.parse(JSON.stringify(newCampingItem));
                console.log(JSON.parse(JSON.stringify(parsedCampingItem)), JSON.stringify(parsedCampingItem));
                var campingItems = JSON.parse(JSON.stringify(component.get("v.items")));
                campingItems.push(parsedCampingItem);
                component.set("v.items",campingItems);
                component.set("v.newItem", {'Price__c': 0, 'Packed__c': false, 'Quantity__c': 0, 'Name':'', 'sobjectType': 'Camping_Item__c'})
            }
        });
        $A.enqueueAction(action);
    }
})


APEX CLASS:
CampingListController.apxc


public with sharing class CampingListController {

    /**
     * This method will insert the camping item record
     *
     */
    @AuraEnabled
    public static void saveItem (Camping_Item__c campingItem) {
        //insert the campingItem record
        insert campingItem;
    }
    
     /**
     * This method fetches the Camping_Item__c records and return as list
     */
    @AuraEnabled
    public static List<Camping_Item__c> getItems() {
        //fetch the active records using soql query
        List<Camping_Item__c> campingItems = [SELECT Id,Name,Price__c,Packed__c,Quantity__c FROM Camping_Item__c];
        //return the list of camping items
        return campingItems;
    }
    
    
}