+ Start a Discussion
Guiomar Fernández de BobadillaGuiomar Fernández de Bobadilla 

Lightning Components Basics - Connect Components with Events Challenge

Hi,

I can't pass the challenge (https://developer.salesforce.com/trailhead/force_com_dev_intermediate/lex_dev_lc_basics/lex_dev_lc_basics_events). This is the error when check challenge:

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


I tryed in the browser add new Camping Items and it's working correctly. The item is added to database and the list is updated.

This is my code:

campingList Component
<aura:component controller="CampingListController">
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}"/>
    
    <div class="slds-page-header" role="banner">

      <div class="slds-grid">

        <div class="slds-col">

          <p class="slds-text-heading--label">Camping Items</p>

          <h1 class="slds-text-heading--medium">My Camping Items</h1>

        </div>

      </div>

    </div>

      
  <div aria-labelledby="newitemform">

      <fieldset class="slds-box slds-theme--default slds-container--small">
    
        <c:campingListForm />
    
      </fieldset>

	</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">Camping List Items</h3>
        </header>
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="campItem">
                    <c:campingListItem item="{!campItem}"/>
                </aura:iteration>
            </div>
        </section>
    </div>

</aura:component>

campingList Controller.js
({
    
    doInit: function(component, event, helper) {
    
        var action = component.get("c.getItems");
    
        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);
            }
        });
    
        $A.enqueueAction(action);
    },    
    
    handleAddItem: function(component, event, helper) {
        var item = event.getParam("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") {        
                var theItems = component.get("v.items");
                theItems.push(item);
                component.set("v.items",theItems);
            }
        });
        $A.enqueueAction(action);
    }
    
})

CampingListController
public with sharing class CampingListController {

    @AuraEnabled 
    public static List<Camping_Item__c> getItems() {
        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;
    }
}

CampingListForm Component
<aura:component >
    
     <aura:attribute name="newItem" type="Camping_Item__c"
     default="{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Packed__c': false,
                    'Price__c': '0',
                    'Quantity__c': '0' }"/>
	<aura:registerEvent name="addItem" type="c:addItemEvent"/>
    
  <div aria-labelledby="newitemform">
      <fieldset class="slds-box slds-theme--default slds-container--small">
    
        <legend id="newitemform" class="slds-text-heading--small 
          slds-p-vertical--medium">
          Add Camping Item
        </legend>
    
        <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="Camping Item Name"
                      class="slds-input"
                      labelClass="slds-form-element__label"
                      value="{!v.newItem.Name}"
                      required="true"/>
              </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">
              <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">
              <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}"/>
    
              </div>
          </div>
    
          <div class="slds-form-element">
              <ui:button label="Create Camping Item"
                  class="slds-button slds-button--brand"
                  press="{!c.clickCreateCampingItem}"/>
          </div>
    
        </form>
    
      </fieldset>
</div>

</aura:component>

CampingListForm Controller.js
({    
    
    clickCreateCampingItem : function(component, event, helper) {
        
        var validCamping = true;

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

        
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        if ($A.util.isEmpty(price) || isNaN(price) || (price <= 0.0)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping Item price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
        var quantityField = component.find("quantity");
        var quantity = quantityField.get("v.value");
        if ($A.util.isEmpty(quantity) || isNaN(quantity) || (quantity <= 0)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping Item quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }

        if(validCamping){
            
            helper.createItem(component);
            
        }
        
    },
})

CampingListForm Helper.js
({
    
     createItem : function(component) {
        var newItem = component.get("v.newItem");
        var addEvent = component.getEvent("addItem");
        addEvent.setParams({"item" : newItem});
        addEvent.fire();
        component.set("v.newItem",
                     { 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Packed__c': false,
                    'Price__c': 0,
                    'Quantity__c': 0});
    }
})

Could anyone help me?

Thanks,
Regards.
Best Answer chosen by Guiomar Fernández de Bobadilla
Guiomar Fernández de BobadillaGuiomar Fernández de Bobadilla
I found the solution! 

In campingList Controller.js in lines #31, #32 and #33 change theItems to items

The action button must be submitForm, that is, in CampingListForm Controller.js line #3 and in CampingListForm Component​ in line #63 change clickCreateCampingItem to submitForm.

Regards!

All Answers

Guiomar Fernández de BobadillaGuiomar Fernández de Bobadilla
I found the solution! 

In campingList Controller.js in lines #31, #32 and #33 change theItems to items

The action button must be submitForm, that is, in CampingListForm Controller.js line #3 and in CampingListForm Component​ in line #63 change clickCreateCampingItem to submitForm.

Regards!
This was selected as the best answer
Parth Joshi 24Parth Joshi 24
Hi Guiomar Fernández de Bobadilla, 

Still m getting the same issue. can you please provide the solution of this?

Regards!
Parth Joshi
Guiomar Fernández de BobadillaGuiomar Fernández de Bobadilla
Hi Parth

This is my code which I passed the challenge! I hope it helps you.

campingList Component
<aura:component controller="CampingListController">
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}"/>
    
    <div class="slds-page-header" role="banner">

      <div class="slds-grid">

        <div class="slds-col">

          <p class="slds-text-heading--label">Camping Items</p>

          <h1 class="slds-text-heading--medium">My Camping Items</h1>

        </div>

      </div>

    </div>

      
  <div aria-labelledby="newitemform">

      <fieldset class="slds-box slds-theme--default slds-container--small">
    
        <c:campingListForm />
    
      </fieldset>

	</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">Camping List Items</h3>
        </header>
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="campItem">
                    <c:campingListItem item="{!campItem}"/>
                </aura:iteration>
            </div>
        </section>
    </div>

</aura:component>

campingList Controller.js
 
({
    
    doInit: function(component, event, helper) {
    
        var action = component.get("c.getItems");
    
        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);
            }
        });
    
        $A.enqueueAction(action);
    },    
    
    handleAddItem: function(component, event, helper) {
        var item = event.getParam("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") {        
                var items = component.get("v.items");
                items.push(item);
                component.set("v.items",items);
            }
        });
        $A.enqueueAction(action);
    }
    
})

CampingListController
public with sharing class CampingListController {

    @AuraEnabled 
    public static List<Camping_Item__c> getItems() {
        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;
    }
}

CampingListForm Component
<aura:component >
    
     <aura:attribute name="newItem" type="Camping_Item__c"
     default="{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Packed__c': false,
                    'Price__c': '0',
                    'Quantity__c': '0' }"/>
	<aura:registerEvent name="addItem" type="c:addItemEvent"/>
    
  <div aria-labelledby="newitemform">
      <fieldset class="slds-box slds-theme--default slds-container--small">
    
        <legend id="newitemform" class="slds-text-heading--small 
          slds-p-vertical--medium">
          Add Camping Item
        </legend>
    
        <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="Camping Item Name"
                      class="slds-input"
                      labelClass="slds-form-element__label"
                      value="{!v.newItem.Name}"
                      required="true"/>
              </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">
              <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">
              <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}"/>
    
              </div>
          </div>
    
          <div class="slds-form-element">
              <ui:button label="Create Camping Item"
                  class="slds-button slds-button--brand"
                  press="{!c.submitForm}"/>
          </div>
    
        </form>
    
      </fieldset>
</div>

</aura:component>

CampingListForm Controller.js
({    
    
    submitForm : function(component, event, helper) {
        
        var validCamping = true;

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

        
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        if ($A.util.isEmpty(price) || isNaN(price) || (price <= 0.0)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping Item price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
        var quantityField = component.find("quantity");
        var quantity = quantityField.get("v.value");
        if ($A.util.isEmpty(quantity) || isNaN(quantity) || (quantity <= 0)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping Item quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }

        if(validCamping){
            
            helper.createItem(component);
            
        }
        
    },
})

CampingListForm Helper.js
({

     createItem : function(component) {
        var newItem = component.get("v.newItem");
        var addEvent = component.getEvent("addItem");
        addEvent.setParams({"item" : newItem});
        addEvent.fire();
        component.set("v.newItem",
                     { 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Packed__c': false,
                    'Price__c': 0,
                    'Quantity__c': 0});
    }

})

Regards.
Ashutosh PradhanAshutosh Pradhan
Here is the answer:

1. campingList Component

<aura:component controller="CampingListController">
     <!--  <ol>
         <li>Bug Spray</li>
         <li>Bear Repellant</li>
         <li>Goat Food</li>
       </ol> -->
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}"/>

    <aura:attribute name="newItem" type="Camping_Item__c" required="true" default="{
                                                       'sobjectType': 'Camping_Item__c',
                                                          'Name': '',
                                                          'Quantity__c':0,
                                                           'Price__c':0,
                                                           'Packed__c':false }"/>

       <aura:attribute name="items" type="Camping_Item__c[]"/>
   
    <div class="slds-col slds-col--padded slds-p-top--large">
       <c:campingListForm/>
    </div>
    <div class="slds-grid slds-m-top--large">

       <fieldset class="slds-box slds-theme--default slds-container--small">
            <legend id="newexpenseform1" class="slds-text-heading--small slds-p-vertical--medium">
          Camping List
         </legend>
              <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>
        </fieldset>
    </div>   
</aura:component>

2. campingList Controller.js

({
    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);
       }
    });

    $A.enqueueAction(action);
},
    handleAddItem: function(component, event, helper) {
        var item = event.getParam("item");
        var action = component.get("c.saveItem");
        action.setParams({
            "campingItem": item
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {       
             var items = component.get("v.items");
            items.push(response.getReturnValue());
            component.set("v.items", items);
            }
        });
        $A.enqueueAction(action);
    },

    clickCreateItem : function(component, event, helper) {
         var validCamping = true;
        // Name must not be blank
        var nameField = component.find("name");
        var name = nameField.get("v.value");
        var quantityField = component.find("quantity");
        var quantity = nameField.get("v.value");
         var priceField = component.find("price");
        var price = nameField.get("v.value");
         if ($A.util.isEmpty(name)){
            validCamping = false;
            nameField.set("v.errors", [{message:"Camping name can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
        
         if ($A.util.isEmpty(quantity)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
        if ($A.util.isEmpty(price)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
         if(validCamping){
             var newItem = component.get("v.newItem");
             console.log("Create item: " + JSON.stringify(newItem));
             // var newItems = component.get("v.items");
              //newItems.push(JSON.parse(JSON.stringify(newItem)));
           //component.set("v.items", newItems);
           helper.createItem(component, newItem);

           component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c','Name': '','Quantity__c': 0,
                    'Price__c': 0,'Packed__c': false });


       }
    }
})

3. CampingListForm Component

<aura:component >
     <aura:registerEvent name="addItem" type="c:addItemEvent"/>

     <div aria-labelledby="newcampingform">
         <fieldset class="slds-box slds-theme--default slds-container--small">
    <legend id="newexpenseform" class="slds-text-heading--small slds-p-vertical--medium">
      Camping
    </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="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:inputText aura:id="price" label="Price"
                  class="slds-input"
                  labelClass="slds-form-element__label"
                  value="{!v.newItem.Price__c}"
                  placeholder="100$."/>
          </div>
      </div>
      <div class="slds-form-element">
          <div class="slds-form-element__control">
          <ui:inputCheckbox aura:id="reimbursed" label="Packed?"
              class="slds-checkbox"
              labelClass="slds-form-element__label"
              value="{!v.newItem.Packed__c}"/>
      </div>
          
      </div>
      
      <div class="slds-form-element">
          <ui:button label="Packed!"
              class="slds-button slds-button--brand"
              press="{!c.submitForm}"/>
      </div>
    </form>
    
  </fieldset>   
    
        
        
    </div>
</aura:component>

4. CampingListForm Controller.js

   ({
    submitForm : function(component, event, helper) {
             var validCamping = true;
        // Name must not be blank
        var nameField = component.find("name");
        var name = nameField.get("v.value");
        var quantityField = component.find("quantity");
        var quantity = nameField.get("v.value");
         var priceField = component.find("price");
        var price = nameField.get("v.value");
         if ($A.util.isEmpty(name)){
            validCamping = false;
            nameField.set("v.errors", [{message:"Camping name can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
        
         if ($A.util.isEmpty(quantity)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
        if ($A.util.isEmpty(price)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
         if(validCamping){
             //var newItem = component.get("v.newItem");
             //console.log("Create item: " + JSON.stringify(newItem));
             // var newItems = component.get("v.items");
              //newItems.push(JSON.parse(JSON.stringify(newItem)));
           //component.set("v.items", newItems);
           helper.createItem(component);
             
         }
    }
})

5. CampingListForm Helper.js

({
    createItem : function(component) {
        var newItem = component.get("v.newItem");
        var addEvent = component.getEvent("addItem");
        addEvent.setParams({"item" : newItem});
        addEvent.fire();
        component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c','Name': '','Quantity__c': 0,
                    'Price__c': 0,'Packed__c': false });

    }
})

6. Additem Event- Create new Lighting Event

<aura:event type="COMPONENT">
       <aura:attribute name="item" type="Camping_Item__c"/>
  </aura:event>

7. CampingListController

  public with sharing class CampingListController {
    @AuraEnabled
    public static List<Camping_Item__c> getItems() {
       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;
 }
}

Hope it help
Parth Joshi 24Parth Joshi 24
Hi Guiomar Fernández de Bobadilla, 

Thank you so much for you help. Actually there is one issue in my code so by resolving that passed this challenge.
Quang Tran NhatQuang Tran Nhat
i got this error, please help

Failed to save undefined: No EVENT named markup://c:addItemEvent found : [markup://c:CampingListForm]

Thanks
Sangeetha MadrasiSangeetha Madrasi
Hi Quang 

Hope you have not created the event for the component

Create an event ->>>
 
<aura:event type="COMPONENT" description="Event template" >
<aura:attribute name="item" type="Camping_Item__c"/>
</aura:event>

Let me know if it helped you and if it worked prove it by giving kudos

Thanks
Sangeetha Madrasi
email:sangee.atf@gmail.com
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
 
Vipanee Pengpol 18Vipanee Pengpol 18
I still get the following, error " The campingList component isn't handing the added item correctly." Any suggestions?
Vipanee Pengpol 18Vipanee Pengpol 18
I used the following code, 

1. CampingListController.js

({
    submitForm : function(component, event, helper) {
             var validCamping = true;
        // Name must not be blank
        var nameField = component.find("name");
        var name = nameField.get("v.value");
        var quantityField = component.find("quantity");
        var quantity = nameField.get("v.value");
         var priceField = component.find("price");
        var price = nameField.get("v.value");
         if ($A.util.isEmpty(name)){
            validCamping = false;
            nameField.set("v.errors", [{message:"Camping name can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
        
         if ($A.util.isEmpty(quantity)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
        if ($A.util.isEmpty(price)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
         if(validCamping){
             //var newItem = component.get("v.newItem");
             //console.log("Create item: " + JSON.stringify(newItem));
             // var newItems = component.get("v.items");
              //newItems.push(JSON.parse(JSON.stringify(newItem)));
           //component.set("v.items", newItems);
           helper.createItem(component);
             
         }
    }
        
})

2. CampingListForm.cmp
<aura:component >
     <aura:registerEvent name="addItem" type="c:addItemEvent"/>

     <div aria-labelledby="newcampingform">
         <fieldset class="slds-box slds-theme--default slds-container--small">
    <legend id="newexpenseform" class="slds-text-heading--small slds-p-vertical--medium">
      Camping
    </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="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:inputText aura:id="price" label="Price"
                  class="slds-input"
                  labelClass="slds-form-element__label"
                  value="{!v.newItem.Price__c}"
                  placeholder="100$."/>
          </div>
      </div>
      <div class="slds-form-element">
          <div class="slds-form-element__control">
          <ui:inputCheckbox aura:id="reimbursed" label="Packed?"
              class="slds-checkbox"
              labelClass="slds-form-element__label"
              value="{!v.newItem.Packed__c}"/>
      </div>
          
      </div>
      
      <div class="slds-form-element">
          <ui:button label="Packed!"
              class="slds-button slds-button--brand"
              press="{!c.submitForm}"/>
      </div>
    </form>
    
  </fieldset>   
        
    </div>

</aura:component>

3.CampingListFormController.js

({
    submitForm : function(component, event, helper) {
             var validCamping = true;
        // Name must not be blank
        var nameField = component.find("name");
        var name = nameField.get("v.value");
        var quantityField = component.find("quantity");
        var quantity = nameField.get("v.value");
         var priceField = component.find("price");
        var price = nameField.get("v.value");
         if ($A.util.isEmpty(name)){
            validCamping = false;
            nameField.set("v.errors", [{message:"Camping name can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
        
         if ($A.util.isEmpty(quantity)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
        if ($A.util.isEmpty(price)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
         if(validCamping){
             //var newItem = component.get("v.newItem");
             //console.log("Create item: " + JSON.stringify(newItem));
             // var newItems = component.get("v.items");
              //newItems.push(JSON.parse(JSON.stringify(newItem)));
           //component.set("v.items", newItems);
           helper.createItem(component);
             
         }
    }
})

4. campingListFormHelper.js

({
    createItem : function(component) {
        var newItem = component.get("v.newItem");
        var addEvent = component.getEvent("addItem");
        addEvent.setParams({"item" : newItem});
        addEvent.fire();
        component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c','Name': '','Quantity__c': 0,
                    'Price__c': 0,'Packed__c': false });

    }  
})

5. CampingListController.apxc
public class CampingListController {

  public with sharing class CampingListController {
    @AuraEnabled
    public static List<Camping_Item__c> getItems() {
       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;

    }
}

6. CampingListHelper.js

({
createItem : 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") {

            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);

        /*var theItems = component.get("v.items");

        // Copy the expense to a new object

        // THIS IS A DISGUSTING, TEMPORARY HACK

        var newItem = JSON.parse(JSON.stringify(item));
  

        theItems.push(newItem);

        component.set("v.items", theItems);

       console.log('theItems helper==',theItems);*/

    },

    validateItemForm: function(component) {

 

    // Simplistic error checking

    var validExpense = true;


    // Name must not be blank

    var fld = component.find("ciName");

    var fldVal = fld.get("v.value");

    if ($A.util.isEmpty(fldVal)){

        validExpense = false;

        fld.set("v.errors", [{message:"Name can't be blank."}]);

    }

    else {

        fld.set("v.errors", null);

    }

    // Amount must be set, must be a positive number

    var quantFld = component.find("ciQuantity");

    var quantFldVal = quantFld.get("v.value");

    if ($A.util.isEmpty(quantFldVal) || isNaN(quantFldVal) || (quantFldVal <= 0.0)){

        validExpense = false;

        quantFld.set("v.errors", [{message:"Enter qunatity."}]);

    }

    else {

        // If the amount looks good, unset any errors...

        quantFld.set("v.errors", null);

    }

        // Amount must be set, must be a positive number

    var priceFld = component.find("ciPrice");

    var priceFldVal = priceFld.get("v.value");

    if ($A.util.isEmpty(priceFldVal) || isNaN(priceFldVal) || (priceFldVal <= 0.0)){

        validExpense = false;

        priceFld.set("v.errors", [{message:"Enter price."}]);

    }

    else {

        // If the amount looks good, unset any errors...

        priceFld.set("v.errors", null);

    }

    return(validExpense);
    }
})

7. Add Event (I named it Component)

<aura:event type="COMPONENT">
       <aura:attribute name="item" type="Camping_Item__c"/>
  </aura:event>

Any help will be useful. 
vleandrovleandro
The one thing I learned from this module (and all the modules for this particular trail) was to read through it, do the exercises for the Expesne app, then take a break...the next day I could see more clearly and understand better when I went to make changes to the Camping app.  Great info in this thread for anyone else who may be struggling!
Derek Bennett 5Derek Bennett 5
Any insight on this error:

Challenge Not yet complete... here's what's wrong: 
The campingList JavaScript controller isn't calling the helper's 'createItem' function.
Akshay Deshmukh1Akshay Deshmukh1
Hi Derek, Can you please post code from which you are trying to call helper method? Thanks, Akshay
Derek Bennett 5Derek Bennett 5
Camplist Helper:

({
    
    doInit  : function(component, event, helper) {
        var action = component.get("c.getItems");
        action.setCallback(this, function(response){
            var state = response.getState();
           
            if (component.isValid() && state === "SUCCESS") {
           
               
                component.set("v.items", response.getReturnValue());
                 
            }
        });
        
        $A.enqueueAction(action);
    },
    
    CreateCamping : function(component, event, helper){
        
        helper.validateFields (component,component.find("name"));
        helper.validateFields (component,component.find("Price"));
        helper.validateFields (component,component.find("Quantity"));
        if(component.get("v.er") === false)
        {     
&nbsp;           //Here I removed the lines and shifted the code to the helperJs       
            console.log('Before:'+Items);            
            helper.CreateCampaign(component,Items);             
             console.log('After:'+Items);                    
        }
    }    
})
Ryan Adams 173Ryan Adams 173
I was able to use the code submitted by Ashutosh Pradhan above to successfully complete the Challenge.
Pratibha GadamPratibha Gadam
Thanks Akshay Deshmukh1. your solution helped me to pass the challenge.
Gavin Britto 2Gavin Britto 2
HI Pratibha,

I'm struggling to pass the challenge. I tried Akshay's suggestion, but I get the error, "The campingList component isn't handing the added item correctly."

Can you share your code so I can compare?
Akshay Deshmukh1Akshay Deshmukh1
Hi Gavin, Can you pls share your code? Thanks, Akshay
David Roberts 4David Roberts 4
Thanks to all above.
Here's a version with the additional changes that were made to the expenses example in the main article.
 

Camping

Harness

<aura:application extends="force:slds" implements="force:hasRecordID">
<c:camping />
</aura:application>
 

camping.cmp

<aura:component controller="CampingListController">
<!-- https://trailhead.salesforce.com/modules/lex_dev_lc_basics/units/lex_dev_lc_basics_events -->   
   
<!-- the Apex server side controller -->
    <aura:attribute name="items" type="Camping_Item__c[]"/>
 
    <c:campingHeader />
     <c:campingList items="{!v.items}"/>
              
</aura:component>

campingList.cmp

<aura:component controller="CampingListController">   
   
               <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="addItem" event="c:addItemEvent"
        action="{!c.handleAddItem}"/>
    <!-- use same type of event -->
    <aura:handler name="updateItem" event="c:addItemEvent"
        action="{!c.handleUpdateItem}"/>
 
 
<!-- FORM AREA -->
<div aria-labelledby="newitemform">
  <fieldset class="slds-box slds-theme--default slds-container--small">
    <c:campingListForm />
     </fieldset>
    </div>
   <!-- / FORM AREA -->   
  
     <aura:attribute name="items" type="Camping_Item__c[]"/>
 
    <!-- / LIST AREA -->  
   <div class ="slds-card slds-p-top--medium">
        <header class ="slds-card__header">
            <h3 class = "slds-text-heading--small">Existing Camping List Items</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 camping items from Salesforce
    doInit: function(component, event, helper) {
   
        console.log("call c.getItems: ");
        // Create the action
        var action = component.get("c.getItems");
        console.log("back from 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);
    }, //doInit
 
    handleAddItem: function(component, event, helper) {
        var newItem = event.getParam("item");
        helper.addItem(component, newItem);
  /*             
        var action = component.get("c.saveItem");
        action.setParams({
            "item": newItem
        });
       
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {       
                var items = component.get("v.items");
                items.push(item);
                component.set("v.items",items);
            }
        });
        $A.enqueueAction(action);
        */
    },
   
    handleUpdateItem: function(component, event, helper) {
        var updatedItem = event.getParam("item");
        helper.updateItem(component, updatedItem);
    }
    
})
 

campingListHelper.js

 
({
              
   addItem: function(component, campItem) {
       
        /* old version
        var action = component.get("c.addItem");
        action.setParams({ "campItem": item });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                var items = component.get("v.items");
                items.push(response.getReturnValue());
                component.set("v.items", items);
            }
        });
        $A.enqueueAction(action);
        */
      
       this.saveItem(component, campItem, function(response){
       var state = response.getState();
           if (component.isValid() && state === "SUCCESS") {
               var expenses = component.get("v.items");
               expenses.push(response.getReturnValue());
               component.set("v.items", items);
           }
       });
 
      
       
    },//addItem
   
    //server side action defined in CampingListController
    updateItem: function(component, item) {
        this.saveItem(component, item);
    },
 
    saveItem: function(component, expense, callback) {
        var action = component.get("c.saveItem");
        action.setParams({
            "item": item
        });
        if (callback) {
            action.setCallback(this, callback);
        }
        $A.enqueueAction(action);
    },//saveItem
 
   
 
    validateCampingItem: function(component, item) {
       
         // Simplistic error checking
        var validItem = true;
        console.log("validate item");
 
 
          // Name must not be blank
        var nameField = component.find("campItemName");
        var itemname = nameField.get("v.value");
        console.log("itemname="+itemname);
        if ($A.util.isEmpty(itemname)){
            validItem = false;
            nameField.set("v.errors", [{message:"Item name can't be blank."}]);
        }
        else {
            console.log("good name "+itemname);
            nameField.set("v.errors", null);
        }
 
 
                              // Price must not be blank
        var priceField = component.find("price");
        var itemprice = priceField.get("v.value");
        console.log("itemprice="+itemprice);
       
        if (isNaN(itemprice)){
            validItem = false;
            priceField.set("v.errors", [{message:"Item price can't be null."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        if ($A.util.isEmpty(itemprice)){
            validItem = false;
            priceField.set("v.errors", [{message:"Item price can't be null."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
       
        if (itemprice<0.01){
            validItem = false;
            priceField.set("v.errors", [{message:"Item price can't be zero."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
 
 
        // Quantity must not be blank
        var qtyField = component.find("quantity");
        var itemqty = qtyField.get("v.value");
        console.log("itemqty ="+itemqty);
       
        console.log("now check empty"+qtyField);
        if (isNaN(itemqty)){
            console.log("bad qty ="+itemqty);
            validItem = false;
            qtyField.set("v.errors", [{message:"Item quantity can't be null."}]);
        }
        else {
            console.log("good");
            qtyField.set("v.errors", null);
        }
        if ($A.util.isEmpty(itemqty)){
            console.log("bad qty ="+itemqty);
            validItem = false;
            qtyField.set("v.errors", [{message:"Item quantity can't be null."}]);
        }
        else {
            console.log("good");
            qtyField.set("v.errors", null);
        }
        if (itemqty<1){
            console.log("bad qty <1 ="+itemqty);
            validItem = false;
            qtyField.set("v.errors", [{message:"Enter an Item quantity."}]);
        }
        else {
            console.log("good");
            qtyField.set("v.errors", null);
        }
       
     
        return(validItem);
       
    } //validateCampingItem
})

campingHeader.cmp

<aura:component >          
    <!-- PAGE HEADER -->
    <div class="slds-page-header" role="banner">
      <div class="slds-grid">
        <div class="slds-col">
          <p class="slds-text-heading--label">Camping Items</p>
          <h1 class="slds-text-heading--medium">My Camping Items</h1>
        </div>
      </div>
    </div>
    <!-- / PAGE HEADER -->
 
</aura:component>
 

campingHeader.css

.THIS {
}
p.THIS {
    font-size: 18px;
}
 

campingListItem.cmp

 
<aura:component >
    <aura:attribute name="item" type="Camping_Item__c"/>
   
    <!-- use same event type or package format 'structure' to add or update -->
    <aura:registerEvent name="updateItem" type="c:addItemEvent"/>
    <!-- this component can send/fire events -->
    <!-- campingList receives and handles it -->
   
 <!-- SLDS markup to give style -->
    <div class="slds-card">
 
        <!-- Color the item green if the expense is reimbursed -->
        <div class="{!v.item.Packed__c == true ?
            'slds-theme--success' : 'slds-theme--warning'}">
 
            <header class="slds-card__header slds-grid grid--flex-spread">
                <a aura:id="item" href="{!'/' + v.item.Id}">
                    <h3>{!v.item.Name}</h3>
                </a>
            </header>
 
            <section class="slds-card__body">
                <div class="slds-tile slds-hint-parent">
       
   
    <!--<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}"/>-->
        <ui:inputCheckbox value="{!v.item.Packed__c}"
                          click="{!c.clickPacked}"/>
    </p>
                   
 <!-- SLDS markup to give style -->                  
                    </div>
            </section>
        </div>
    </div>
 
 
 
</aura:component>
 

campingListItemController.js

 
({
               dummy: function(component, event, helper) {
     
    },
   
    clickPacked: function(component, event, helper) {
        var campingItem = component.get("v.item");
        var updateEvent = component.getEvent("updateItem");
        updateEvent.setParams({ "item": campingItem });
        updateEvent.fire();
    }
  
})
 

...more to follow

 

 

 


 
 
David Roberts 4David Roberts 4
part 2:
 

campingListForm.js

 
<aura:component >
   
           <aura:attribute name="newItem" type="Camping_Item__c"
                    default="{'sobjectType' : 'Camping_Item__c',
                               'Name':'',
                               'Quantity__c' : '',
                               'Price__c' : ''}"/>
   
 
        <aura:registerEvent name="addItem" type="c:addItemEvent"/>
 
 
    <!-- CREATE NEW CAMPING ITEM FORM -->
    <fieldset class="slds-box slds-theme--default slds-container--small">
        <legend id="newitemform" class="slds-text-heading--small
          slds-p-vertical--medium">
          Add a Camping Item
        </legend>
 
       
    <form class="slds-form--stacked">
 
      <div class="slds-form-element slds-is-required">
          <div class="slds-form-element__control">
              <ui:inputText aura:id="campItemName" label="Camping Item 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 Camping Item"
              class="slds-button slds-button--brand"
              press="{!c.submitForm}"/>
      </div>
 
    </form>
        </fieldset>
    <!-- / CREATE NEW CAMPING ITEM FORM -->
   
</aura:component>

campingListFormController.js

 
({
               myAction : function(component, event, helper) {
                             
               },
   
    handleAddItem: function(component, event, helper) {
        var newItem = event.getParam("item");
        helper.addItem(component, newItem);
    },
 
   
    submitForm: function(component, event, helper) {
        console.log('submit if valid');
        var nameField = component.find("campItemName");
        console.log('nameField='+nameField+'<<<');
        var itemname = nameField.get("v.value");
        console.log('itemname='+itemname+'<<<');
        console.log('submit if valid...');
       
        if(helper.validateCampingListForm(component)){
            console.log('valid so add');
            // Add the new item
            //var newItem = component.get("v.newItem"); //created in helper
            //helper.addItem(component, newItem);
            helper.createItem(component);
        }
    },
 
})

campingListFormHelper.js

 
({
               helperMethod : function() {
                             
               },
   
   
    validateCampingListForm: function(component) {
       
 
        var validItem = true;
   
        // Name must not be blank (aura id)
        var nameField = component.find("campItemName");
        console.log('nameField='+nameField+'<<<');
        var itemname = nameField.get("v.value");
        console.log('itemname='+itemname+'<<<');
        if ($A.util.isEmpty(itemname)){
            validItem = false;
            nameField.set("v.errors", [{message:"Item name can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
   
        // Quantity must be set, must be a positive number
        var amtField = component.find("quantity");
        console.log('qtyField='+amtField+'<<<');
        var amt = amtField.get("v.value");
        console.log('quantity='+amt+'<<<');
        if ($A.util.isEmpty(amt) || isNaN(amt) || (amt <= 0.0)){
            validItem = false;
            amtField.set("v.errors", [{message:"Enter an item quantity."}]);
        }
        else {
            // If the quantity looks good, unset any errors...
            amtField.set("v.errors", null);
        }
       
        //also do price
        //Price must be set, must be a positive number
        amtField = component.find("price");
        console.log('priceField='+amtField+'<<<');
        amt = amtField.get("v.value");
        console.log('price='+amt+'<<<');
        if ($A.util.isEmpty(amt) || isNaN(amt) || (amt <= 0.0)){
            validItem = false;
            amtField.set("v.errors", [{message:"Enter an item price."}]);
        }
        else {
            // If the price looks good, unset any errors...
            amtField.set("v.errors", null);
        }
        console.log('validItem='+validItem+'<<<');
        return(validItem);
       
    },//validateCampingListForm
   
    createItem: function(component, newItem) {
          
        console.log('in addItem');
        var newItem = component.get("v.newItem");
        var addEvent = component.getEvent("addItem");
        addEvent.setParams({"item" : newItem});
        addEvent.fire();
 
       
        //component.set("v.newItem", default);
        component.set("v.newItem",{'sobjectType':'Camping_Item__c',
                'Name': '',
                'Quantity__c': 0,
                'Price__c': 0,
                'Packed__c': false});
    },//addItem
   
 
})

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', 'Quantity__c', 'Price__c', 'Packed__c' };
 
                              system.debug('fields: '+fieldsToCheck);
       
        Map<String,Schema.SObjectField> fieldDescribeTokens =
            Schema.SObjectType.Camping_Item__c.fields.getMap();
                              system.debug('map: '+fieldDescribeTokens);    
        for(String field : fieldsToCheck) {
            if( ! fieldDescribeTokens.get(field).getDescribe().isAccessible()) {
                throw new System.NoAccessException();
                return null;
            }
        }
       
        /*
        //temp for debug
        List<Camping_Item__c> checklist = [SELECT Id, Name, Quantity__c, Price__c, Packed__c
                FROM Camping_Item__c];
        system.debug(checklist);
        */
       
        // OK, they're cool, let 'em through
        return [SELECT Id, Name, Quantity__c, Price__c, Packed__c
                FROM Camping_Item__c];
    }
   
    @AuraEnabled
    public static Camping_Item__c saveItem(Camping_Item__c item) {
       
       
        system.debug('campitem = '+item);
        // Perform isUpdatable() checking first, then
        upsert item;
        return item;
    }
}
 

addItemEvent.evt

 
<aura:event type="COMPONENT">
    <aura:attribute name="item" type="Camping_Item__c"/>
</aura:event>
 
 
Carlos SiqueiraCarlos Siqueira

@Guiomar Fernández de Bobadilla:

I tried your solution posted on June 17th 2016 and keep getting this error message while trying to save the campingListForm component and
campingList component. Can anybody point me to where/how to create this addItemEvent ??

User-added image

User-added image

 
Muthukumaran Kathirvelu 6Muthukumaran Kathirvelu 6
Hi All,

This is regarding the below lightning trailhead. Have found a solution towards it. Hope it helps upcoming future trailhead learners as well.

CampingList.cmp:

<aura:attribute name="items" type="Camping_Item__c[]"/>

<aura:handler name="init" action="{!c.doInit}" value="{!this}" />  
<aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}"/>   


<!-- 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 -->

<!-- New Camping Form -->
    <c:campingListForm />
<!--/ New Camping Form -->

<!-- Camping List Items -->      
<aura:iteration items="{!v.items}" var="itm">
      <c:campingListItem item="{!itm}"/><br/>
</aura:iteration>

CampingListController.js:

({

    //-- Load Camping list Items.
    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());
                console.log("doInit: "+response.getReturnValue());
            }
        });

        //-- Send action off to be execute.
        $A.enqueueAction(action);
    },

     //-- Handle Create Expense Actions..
    handleAddItem: function(component, event, helper)
    {
        console.log("\nEntry into CampingListController.js -> handleAddItem()");        

        var item = event.getParam("item");
        console.log("\nCampingListController.js -> handleAddItem() item: "+ JSON.stringify(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")
            {       
               var items = component.get("v.items");
               console.log("Campaigns(items..) before  create: "+JSON.stringify(items));
               items.push(response.getReturnValue());
               console.log("Campaigns(items..) after  create: "+JSON.stringify(items));
               component.set("v.items",items);
            }
        });
        $A.enqueueAction(action);
    }

    /*handleAddItem: function(component, event, helper)
    {
        console.log("\nEntry into CampingListController.js -> handleAddItem()");        

        var newItem = event.getParam("item");
        console.log("\nCampingListController.js -> handleAddItem()\n the Item: "+ JSON.stringify(newItem));
        helper.createItem(component, newItem);        

    }*/


})

CampingListController.apxc (apex):

public with sharing class CampingListController
{
    @AuraEnabled
    public static List<Camping_Item__c> getItems()
    {
        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;
            }            
        }        
        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)
    {   
        System.debug('Campaign List Item from Apex: '+item);
        upsert item;
        System.debug('Campaign List Item from Apex Id: '+item.Id);
        return item;
    }

}

CampingListForm.cmp:

<aura:component controller="CampingListController" >

    <aura:attribute name="newItem"  type="Camping_Item__c" default="{ 'sobjectType': 'Camping_Item__c',
                                                                      'Name':'',
                                                                      'Quantity__c': 0,
                                                                      'Price__c': 0,
                                                                      'Packed__c': false}"  />  

    <aura:registerEvent name="addItem" type="c:addItemEvent"/>


    <!-- New Camping Form-->  
    <div class="slds-col slds-col--padded slds-p-top--large" >  
        <!-- Boxed Area-->
        <fieldset class="slds-box slds-theme--default slds-container--small">

            <legend id="newexpenseform" class="slds-text-heading--small" >
                Add Camping List
            </legend>

            <!-- Create New Expense Form -->
            <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>            
            <!--/ Create New Expense Form -->
        </fieldset>
        <!--/ Boxed Area-->     
    </div>
    <!--/ New Camping Form-->  

</aura:component>

CampingListFormController.js:

({
    clickCreateItem : function(component, event, helper)
    {
        //-- Simplistic error checking.
        console.log("\nIn CampingListFormController.js -> submitForm()");
        var validCampign = true;

        //-- Name must not 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 must not be blank.
        var qtyField = component.find("quantity");
        var quantity = qtyField.get("v.value");
        if($A.util.isEmpty(quantity))
        {
            validCampign = false;
            qtyField.set("v.errors",[{message: "Quantity can't be blank."}]);
        }
        else
        {
            qtyField.set("v.errors",null);            
        }

        //-- Price must not 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);            
        }

        //-- If we pass error checking, do some real work.
        if(validCampign)
        {
            //-- Create the new expense.
            var newCampaign = component.get("v.newItem");
            console.log("In CampingListFormController.js -> submitForm()\n The Item: " + JSON.stringify(newCampaign));
            helper.createItem(component, newCampaign);
        }               



    }

})

CampingListFormHelper.js:

({
    createItem : function(component,item)
    {   
        console.log("\nEntry into CampingListFormHelper.js -> createItem()");
        console.log("In CampingListFormHelper.js -> createItem() the item is: "+JSON.stringify(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});
    }
})

CampingApplication.app:

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

CampingListHelper.js:

({
    createItem : function(component,campaign)
    {
        console.log("\nEntry into CampingListHelper.js -> createItem()");        
        console.log("\nCampingListHelper.js -> createItem()\n the Item: "+ JSON.stringify(campaign));

        this.saveItem(component, campaign, function(response)
        {
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS")
            {
                var campaigns = component.get("v.items");
                console.log("Campaigns before create: "+JSON.stringify(campaigns));
                var retcamp = response.getReturnValue();
                campaigns.push(retcamp);
                console.log("Campaigns after  create: "+JSON.stringify(campaigns));
                component.set("v.items",campaigns);   
            }
        });        
    },

    saveItem : function(component,campaign,callback)
    {
        console.log("\nEntry into CampingListHelper.js -> saveItem()");        
        console.log("\nCampingListHelper.js -> saveItem() the campaign is: "+ JSON.stringify(campaign));

         var action = component.get("c.saveItem");
         console.log("Helper->saveItems"+campaign);
         action.setParams
         ({
            "campaign": campaign
         });

        if (callback)
        {
            action.setCallback(this, callback);
        }
        $A.enqueueAction(action);        

    }

})

Reference Links:

1) https://developer.salesforce.com/forums/ForumsMain?id=906F0000000kDmtIAE
2) https://salesforce.stackexchange.com/questions/149023/facing-problem-with-trailhead-lightning-module-challenge-connect-components-wit
3) https://salesforce.stackexchange.com/questions/192815/how-can-i-use-lightninginput-instead-of-uiinputsmthg-for-the-challenge-ente
4) https://salesforce.stackexchange.com/questions/191861/the-campinglist-component-appears-to-be-using-ui-components-instead-of-base-ligh

Hope you find this comment useful and clear this trailhead successfully. All the best.

Regards,
Muthukumaran K
Consultant (Salesforce)
Mark Tyler CrawfordMark Tyler Crawford
@Muthukumaran Kathirvelu 6

Your code worked, and allowed me to pass the challenge.
Vineeth ReddyVineeth Reddy
I have cleared this trailhead module. Here is the code below.
Hope this helps.

addItemEvent.evt (Lightning Event)
<aura:event type="COMPONENT">
    <aura:attribute name="item" type="Camping_Item__c"/>
</aura:event>

CampingListController.apxc (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;
    }

}

campingListForm.cmp (Lightning Component)
<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>

campingListFormController.js (Lightning Component Controller)
({
    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);
        }               
        
    }

})

campingListFormHelper.js (Lightning Component Helper)
({
    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});
    }
})

campingList.cmp (Lightning Component)
<aura:component controller="CampingListController">
    
    <!-- Attribute Definition -->
    <aura:attribute name="items" type="Camping_Item__c[]"/>
    
    <!-- Handlers -->
    <aura:handler name="init" action="{!c.doInit}" value="{!this}" />  
    <aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}"/>   
    
    <!-- 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>
    
    <!-- New Camping Form -->
    <c:campingListForm />
    
    <!-- Camping List Items -->      
    <aura:iteration items="{!v.items}" var="itm">
          <c:campingListItem item="{!itm}"/><br/>
    </aura:iteration>   
    
</aura:component>

campingListController.js (Lightning Component Controller)
({

    // load camping list items on initiation
    doInit: function(component,event,helper) {

        var action = component.get("c.getItems");
        action.setCallback(this,function(response) {
            var state = response.getState();
            if(component.isValid() && state === "SUCCESS") {
                component.set("v.items",response.getReturnValue());
                console.log("doInit: "+response.getReturnValue());
            }
        });

        $A.enqueueAction(action);
    },
	
    // handle adding items to camping list
    handleAddItem: function(component, event, helper) {     
        var item = event.getParam("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") {       
               var items = component.get("v.items");
               items.push(response.getReturnValue());
               component.set("v.items",items);
            }
        });
        $A.enqueueAction(action);
    }

})

campingListHelper.js (Lightning Component Helper)
({
    createItem : function(component,campaign) {
        
        this.saveItem(component, campaign, function(response) {
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                var campaigns = component.get("v.items");
                var retcamp = response.getReturnValue();
                campaigns.push(retcamp);
                component.set("v.items",campaigns);   
            }
        });        
    },

    saveItem : function(component,campaign,callback) {
        var action = component.get("c.saveItem");
        action.setParams ({
            "campaign": campaign
        });

        if (callback) {
            action.setCallback(this, callback);
        }
        $A.enqueueAction(action);        
    }

})

Happy trailblazing.

Regards,
Vineeth Batreddy




 
Orchay NaehOrchay Naeh
having troubles with Connect Components with Events

the code itself works yet i get an error of :
Challenge Not yet complete... here's what's wrong: 
The campingList component doesn't appear to have a Quantity input field in the form using a Lightning Base component.


i guess its a default message for somthing else, any suggestion will be appreciated, i am posting my classes below,

CampingList.cmp
<aura:component controller="CampingListController">
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}"/>
  
       <aura:attribute name="items" type="Camping_Item__c[]"/>
        
      <div class="slds-col slds-col--padded slds-p-top--small">
      <c:campingListForm />
     </div>
     
     <br/>
     <section class="slds-card__body">
     <div id="list" class="row">
         <aura:iteration items="{!v.items}" var="PerItem">
         <c:campingListItem item="{!PerItem}"/>
     </aura:iteration>
     </div>
     </section>
     
 </aura:component>

CampingList(Controller).js (helper - is empty) 
 ({
     doInit  : function(component, event, helper)
     {
                var action = component.get("c.getItems"); // c of apex controller
         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);
                }
         });
         $A.enqueueAction(action);
         },
   
     handleAddItem : function (component,event,helper)
     {
                var action = component.get("c.saveItem");
         var item = event.getParam("item");
         var lstItems = component.get("v.items");
   
         action.setParams({"item":item});
         action.setCallback(this,function(response)
         {
                var state = response.getState();
                if (component.isValid() && state === "SUCCESS")
                {
                lstItems.push(item);
                component.set("v.items",lstItems);
                console.log("After:"+lstItems);               
                }
         });
         $A.enqueueAction(action);  
     },
 })


CampingListForm.cmp
 <aura:component >
       <aura:registerEvent name="addItem" type="c:addItemEvent"/>
     <aura:attribute name="er" type="boolean" default="false"/>
     <aura:attribute name="newItem" type="Camping_Item__c"    default="{ 'sobjectType': 'Camping_Item__c',
                          'Name': '',
                          'Price__c': 0,
                          'Quantity__c': 0,                        
                          'Packed__c': false}"/>
   
         <form class="slds-form--stacked">         
           
                <lightning:input aura:id="Name" label="Camping Name"
                              name="name"
                              value="{!v.newItem.Name}"
                              required="true"/>
                       
                 <lightning:input type="toggle" aura:id="Packed" label="Packed?" 
                              name="Packed"
                               checked="{!v.newItem.Packed__c}"/>
           
                 <lightning:input type="number" aura:id="Price" label="Price"
                              name="Price"
                              min="0.1"
                              formatter="currency"
                               step="0.01"
                              value="{!v.newItem.Price__c}"
                              messageWhenRangeUnderflow="Enter an amount that's at least $0.10."/>
   
                 <lightning:input type="number" aura:id="Quantity" label="Quantity"
                             name="Quantity"
                              value="{!v.newItem.Quantity__c}"
                              placeholder="ABC Co."/>
   
                 <lightning:button label="Create Camping"
                               class="slds-m-top--medium"
                               variant="brand"
                               onclick="{!c.clickCreateItem}"/>
         </form>
 </aura:component>


CampingListForm(Controller).js
 ({
     clickCreateItem: function(component, event, helper) { 
        var a = component.get("v.newItem");
                
         helper.validateFields (component,component.find("Name"));
         helper.validateFields (component,component.find("Price"));
         helper.validateFields (component,component.find("Quantity"));
   
        var er = component.get("v.er");
         console.log(er);
   
         if(!er)
         {
             var newItem = component.get("v.newItem");           
             helper.CreateItem(component, newItem);
         }
        
        component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                         'Name': '',
                         'Quantity__c': 0,
                         'Price__c': 0,
                         'Packed__c': false });
    },
 })

CampingListForm(Helper).js
  ({
     CreateItem : function(component, newItem)
     {
         // creating event setting its parameters and fires it
         var createEvent = component.getEvent("addItem");
         // cleaning from fields
        component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                         'Name': '',
                        'Quantity__c': 0,
                         'Price__c': 0,
                         'Packed__c': false });
   
         createEvent.setParams({ "item": newItem });
         createEvent.fire();
     },
         
        validateFields : function (component,field) {
        var nameField = field;
        console.log('yes:'+nameField);
         var fieldname = nameField.get("v.value");
   
         if ($A.util.isEmpty(fieldname))
         {
            console.log('found empty nameField' + fieldname);
            component.set("v.er",true);
            nameField.set("v.errors", [{message:"this field can't be blank."}]);
         }
         else
         {
             console.log('found nameField' + fieldname);
            nameField.set("v.errors", null);
         }
    },
   
  })

CampingListItem.cmp
 <aura:component >
         
     <aura:registerEvent name="addItem" type="c:addItemEvent"/>
     <aura:attribute type="Camping_Item__c" name="item" required="true"/>
 
    Name:
         <lightning:formattedText value="{!v.item.Name}" /><br/>
     Price:
     <lightning:formattedNumber value="{!v.item.Price__c}" style="currency"/><br/>
     Quantity:
     <lightning:formattedNumber value="{!v.item.Quantity__c}" /><br/>
     Packed:
     <lightning:input type="toggle"
             label="Packed?"
             name="Packed"
             class="slds-p-around--small"
             checked="{!v.item.Packed__c}"
             messageToggleActive="Yes"
             messageToggleInactive="No"
             onchange="{!c.packItem}"/>
     
 </aura:component>

CampingListController.apxc
 public with sharing class CampingListController {
   
     @AuraEnabled
     public static List<Camping_Item__c> getItems() {
         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) {
         system.debug('^^^the new item '+item);
         upsert item;
         return item;
     }
 }


and camping class that includes campingHeader and campingList 
and i start it from harness.app

thank you
Naeh Orchay
Orchay NaehOrchay Naeh
nvm found the problem by comparing to @Vineeth Reddy was lacking parameters in quantity and apearantly in the form the packed should be a chceck box and not a toggle type
many thanks :)

Naeh Orchay
Rishabh Kumar 21Rishabh Kumar 21
campingList.cmp
<aura:component controller="CampingListController">
	
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    
    <aura:handler name="addItem" event="c:addItemEvent"
   	action="{!c.handleAddItem }"/>
    

    
    <aura:attribute name="items" type="Camping_Item__c[]"/>
    
    <ol>
    <li>Bug Spray</li>
    <li>Bear Repellant</li>
    <li>Goat Food</li>
    </ol>
    
       <!-- NEW ITEM FORM -->
    <div class="slds-col slds-col--padded slds-p-top--large">

        <c:campingListForm />

    </div>
    <!-- / NEW ITEM FORM -->    
   

    <div class="slds-card slds-p-top--medium">
        <header class="slds-card__header">
            <h3 class="slds-text-heading--small">Items</h3>
        </header>
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="items">
                    <c:campingListItem item="{!item}"/>
                </aura:iteration>
            </div>
        </section>
    </div>

</aura:component>

campingListController.js
({
    // Load items 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);
},

    
    handleAddItem: function(component, event, helper) {
    //   var newItem = event.getParam("item");
    //helper.addItem(component, newItem);
    var action = component.get("c.saveItem");
    		action.setParams({"item": newItem});
    		action.setCallback(this, function(response){
        		var state = response.getState();
        		if (component.isValid() && state === "SUCCESS") {
            		// all good, nothing to do.
            var items = component.get("v.items");
            items.push(response.getReturnValue());
            component.set("v.items", items);
        		}
    		});
    		$A.enqueueAction(action);
        		}
    
              
})

campingListHelper.js
({
   addItem: function(component, item) {
    this.saveItem(component, item, function(response){
        var state = response.getState();
        if (component.isValid() && state === "SUCCESS") {
            // all good, nothing to do.
         /*   var items = component.get("v.items");
            items.push(response.getReturnValue());
            component.set("v.items", items);*/
        }
    });
},
})

campingListForm.cmp
<aura:component >
	
        <aura:attribute name="newItem" type="Camping_Item__c"
     default="{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Quantity__c': 0,
                    'Price__c': 0,
                    'Packed__c': false }"/>
    <aura:registerEvent name="addItem" type="c:addItemEvent"/>
        <!-- CREATE NEW ITEM FORM -->
    <form class="slds-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>

​​campingListFormController.js
({
    
    submitForm: function(component, event, helper) {    
    if(helper.validateItemForm(component)){
        // Create the new item
        var newItem = component.get("v.newItem");
        helper.createItem(component, newItem);
    }
        
        }

})

campingListFormHelper.js
({
 addItem: function(component, newItem) {
    var addItem = component.getItem("addItem");
    addItem.setParams({ "item": item });
    addItem.fire();
            component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Quantity__c': 0,
                    'Price__c': 0,
                    'Packed__c': false } );
},
    

		validateItemForm: function(component) {
		
              // Simplistic error checking
        var validItem = true;

        // Name must not be blank
        var nameField = component.find("itemname");
        var itemname = nameField.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);
        }
        
        // Quantity must not be blank
        var quantityField = component.find("quantity");
        var quantity = nameField.get("v.value");
        if ($A.util.isEmpty(quantity)){
            validItem = false;
            quantityField.set("v.errors", [{message:"Quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
		// Price must not be blank
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        if ($A.util.isEmpty(price)){
            validItem = false;
            priceField.set("v.errors", [{message:"Price can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
            return (validItem);

	}
})

addItemEvent.evt
<aura:event type="COMPONENT">
    <aura:attribute name="item" type="Camping_Item__c"/>
</aura:event>


It will work.​​
TimCarlTimCarl
I believe there still is a bug in the "Connect Components with Events".  My code is working in every respect. To pass the Challenge, I just had to replace my <ui:> namespace with the <lightning:> namespece in the campingListForm.cmp.  I know what your thinking. They wanted you to use the slds in the first place. I knew that!  The application in my Browser worked only when using <ui:> namespace. I read every article on this forum about my TrailHead problem. I debugged my code till I could recite it in my sleep. To pass the challenge I used the <lightning: > but knowing the app really didn't work. I switched it back after passing the challenge and "Works!".    If I can I can help:  my github is at https://github.com/timcarl/Lightning-Components   The code is there for use. If you see something I don't, please let me know so I can learn.
Sumit Bhattacharya 10Sumit Bhattacharya 10
hello everyone,
i got this error, please help

"Connect Components with Events"

addItemEvent:
____________________________________________________________________________
<aura:event type="COMPONENT">
    <aura:attribute name="item" type="Camping_Item__c"/>
</aura:event>
-----------------------------------------------------------------------------------------------------------------------------------------
campingListForm.cmp:


<aura:component >
       
     <aura:attribute name="newItem" type="Camping_Item__c"
        default="{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Quantity__c': 0,
                    'Price__c': 0,
                    'Packed__c': false }"/>
    <aura:registerEvent name="addItem" type="c:addItemEvent"/>
       
    <form class="slds-form--stacked">
        
      <div class="slds-form-element slds-is-required">
      <div class="slds-form-element__control">
              <lightning:input aura:id="itemname" 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">
              <lightning:input 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">
              <lightning:input 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">
         <lightning:input aura:id="packed" label="Packed?"
              class="slds-checkbox"
              labelClass="slds-form-element__label"
              value="{!v.newItem.Packed__c}"/>
      </div>
 
      <div class="slds-form-element">
          <lightning:input label="Create Camping Item"
              class="slds-button slds-button--brand"
              press="{!c.submitForm}"/>
      </div>
 
    </form>
  

</aura:component>
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
campingListFormController.js:



({

     
    submitForm: function(component, event, helper) {   
    if(helper.validateItemForm(component)){
        // Create the new item
        var newItem = component.get("v.newItem");
        helper.createItem(component, newItem);
    }
         
        }
})

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
campingListFormHelper.js:


({
addItem: function(component, newItem) {
    var addItem = component.getItem("addItem");
    addItem.setParams({ "item": item });
    addItem.fire();
            component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Quantity__c': 0,
                    'Price__c': 0,
                    'Packed__c': false } );
                          
},
     
 
        validateItemForm: function(component) {
         
             
        var validItem = true;
 
     
        var nameField = component.find("itemname");
        var itemname = nameField.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);
        }
         
     
        var quantityField = component.find("quantity");
        var quantity = nameField.get("v.value");
        if ($A.util.isEmpty(quantity)){
            validItem = false;
            quantityField.set("v.errors", [{message:"Quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
     
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        if ($A.util.isEmpty(price)){
            validItem = false;
            priceField.set("v.errors", [{message:"Price can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }
            return (validItem);
    }
})
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
campingListController.apxc:

public with sharing class CampingListController {

    @auraenabled
    public static List<Camping_Item__c> getItems (){
        List<Camping_Item__c> CI = [select id, name,price__c,Quantity__c,Packed__c from Camping_Item__c ];
        return CI;
    }
    @auraenabled
    public static Camping_Item__c saveItem (Camping_Item__c item){
        insert item;
        return item;
    }
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

campingList.cmp:


<aura:component  controller="CampingListController">
   

    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:handler name="addItem" 
                  event="c:addItemEvent"
                  action="{!c.handleAddItem }"/>
    

    <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'}"/>
    
    
    <form class="slds-form--stacked">
        
        
        <lightning:input aura:id= "cmpform" label="Name" name= "itemname" value="{!v.newItem.Name}"/>        
        <lightning:input aura:id= "cmpform" label="Quantity" name="itemquantity" 
                         value="{!v.newItem.Quantity__c}" type="number" min= "1"/>
        
        <lightning:input aura:id= "cmpform" label="Price" name="itemprice" value="{!v.newItem.Price__c}"
                         formatter="currency"/>        
        <lightning:input type="checkbox" aura:id="cmpform" label="Packed?" 
                         checked="{!v.newItem.Packed__c}"/>     
        <lightning:button label="Submit" onclick="{!c.clickCreateItem}"/>
      
        
    </form>
    
    <aura:iteration items="{!v.items}" var="itm">
                    <c:campingListItem item="{!itm}"/><br/>
                </aura:iteration>,
      
</aura:component>
Sumit Bhattacharya 10Sumit Bhattacharya 10
please find this error
Sudhanshu BhardwajSudhanshu Bhardwaj
Best Answer
Gangadhar LGangadhar L
The below code is working for me. I believe it will work for all
addItemEvent Event

<aura:event type="COMPONENT" description="Event template" >
<aura:attribute name="item" type="Camping_Item__c"/>
</aura:event>
-------------------------------------------------------------------------------------------------------
campingListForm CMP

<aura:component >
    <aura:registerEvent name="addItem" type="c:addItemEvent"/>
    <!-- CREATE NEW ITEM FORM -->
    <form class="slds-form--stacked">

      <div class="slds-form-element slds-is-required">
          <div class="slds-form-element__control">
              <lightning:input aura:id="itemname" label="Name"
                  value="{!v.newItem.Name}"
                  required="true"/>

          </div>
     </div>

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

          </div>
      </div>

      <div class="slds-form-element">
          <div class="slds-form-element__control">
              <lightning:input  aura:id="price" label="Price" Formatter="currency"
                  value="{!v.newItem.Price__c}"
                  />
          </div>
      </div>

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

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

    </form>
    <!-- / CREATE NEW ITEM FORM -->
</aura:component>
---------------------------------------------------------------------------------------------
campingListForm Controller
({    
    
    submitForm : function(component, event, helper) {
        
        var validCamping = true;

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

        
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        if ($A.util.isEmpty(price) || isNaN(price) || (price <= 0.0)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping Item price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
        var quantityField = component.find("quantity");
        var quantity = quantityField.get("v.value");
        if ($A.util.isEmpty(quantity) || isNaN(quantity) || (quantity <= 0)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping Item quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }

        if(validCamping){
            
            helper.createItem(component);
            
        }
        
    },
})
--------------------------------------------------------------------------------------------------------------

campingListForm Helper

({

     createItem : function(component) {
        var newItem = component.get("v.newItem");
        var addEvent = component.getEvent("addItem");
        addEvent.setParams({"item" : newItem});
        addEvent.fire();
        component.set("v.newItem",
                     { 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Packed__c': false,
                    'Price__c': 0,
                    'Quantity__c': 0});
    }

})

-----------------------------------------------------------------------------------------------------------

campingList Component

<aura:component controller="CampingListController">
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}"/>
    
    <div class="slds-page-header" role="banner">

      <div class="slds-grid">

        <div class="slds-col">

          <p class="slds-text-heading--label">Camping Items</p>

          <h1 class="slds-text-heading--medium">My Camping Items</h1>

        </div>

      </div>

    </div>

      
  <div aria-labelledby="newitemform">

      <fieldset class="slds-box slds-theme--default slds-container--small">
    
        <c:campingListForm />
    
      </fieldset>

    </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">Camping List Items</h3>
        </header>
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="campItem">
                    <c:campingListItem item="{!campItem}"/>
                </aura:iteration>
            </div>
        </section>
    </div>

</aura:component>
---------------------------------------------------------------------------

campingList Controller

({
    
    doInit: function(component, event, helper) {
    
        var action = component.get("c.getItems");
    
        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);
            }
        });
    
        $A.enqueueAction(action);
    },    
    
    handleAddItem: function(component, event, helper) {
        var item = event.getParam("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") {        
                var items = component.get("v.items");
                items.push(item);
                component.set("v.items",items);
            }
        });
        $A.enqueueAction(action);
    }
    
})
----------------------------------------------------------------------------------------------------------

campingList Helper

({
    validateFields : function (component,field) {
        var nameField = field;
        var expname = nameField.get("v.value"); 
        if ($A.util.isEmpty(expname)){
           component.set("v.er",true);
           nameField.set("v.errors", [{message:"this field can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }
    },
    
    createItem : function (component,Item){ 
            
        var action = component.get("c.saveItem");
        action.setParams({"CampingItem":Item});
        action.setCallback(this,function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                console.log('save');
            }
        });
        $A.enqueueAction(action);  
//Below lines are shifted from controller Js to helperJs
        var Items = component.get("v.items"); 
        var Item = component.get("v.newItem"); 
        Items.push(Item);    
        component.set("v.items",Items); 
        component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                'Name': '',
                'Quantity__c': 0,
                'Price__c': 0,
                'Packed__c': false });
    }
})
-------------------------------------------------------
CampingListController Class
public with sharing class CampingListController {

    @AuraEnabled 
    public static List<Camping_Item__c> getItems() {
        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;
    }
}
 
Juliana LacerdaJuliana Lacerda
Someone can help me with this issue:
The campingList component appears to be using UI Components instead of Base Lightning Components in the form. You should be using only Base Lightning Components.
 
KapavariVenkatramanaKapavariVenkatramana
Gangadhar L Code WOrked....THanks for TIME>
Saket Ranjan 3Saket Ranjan 3
Aura component basics Trailhead  problem in connect  component with event :: i have done the task using <ui: input ...>  but trailhead is asking to complete it using <lightening: ...> which is leading me to the error ::  "  Action failed: c:campingListForm$controller$submitForm [Cannot read property 'config' of undefined] Failing descriptor: {c:campingListForm$controller$submitForm} ".
Madhumitha M 3Madhumitha M 3
Hi,
please help me to complete this task
How should i create the CampingListController
i show an error like this: duplicate value found: <unknown> duplicates value on record with id: <unknown>
while create the CampingListController
Swapnil HudeSwapnil Hude

Hi All, Tried several codes but getting below error - 

The campingList component appears to be using UI Components instead of Base Lightning Components in the form. You should be using only Base Lightning Components.

can you please help me.