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

Facing problem with Trailhead Lightning Module Challenge: Connect Components with Events

Hi All,

I am facing problem in completing the challenge for the Lightning module - Connect Components with Events. Below are the code I have written:
campingList.cmp
<aura:component controller="sudipta.CampingListController" implements="force:appHostable,flexipage:availableForAllPageTypes" >
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="addItem" action="{!c.handleAdditem}" event="c:addItemEvent"/>

    <aura:attribute name="newItem" type="Camping_Item__c" description="Single Camping Item" required="true" 
                                                        default="{'sObjectType':'sudipta__Camping_Item__c',
                                                                'Name':'Item1',
                                                                'sudipta__Quantity__c':10,
                                                                'sudipta__Price__c':100,
                                                                'sudipta__Packed__c':false}"/>
    <aura:attribute name="items" type="Camping_Item__c[]" description="All Camping Item" />
    
    <!-- PAGE HEADER -->
    <div class="slds-page-header" role="banner">
        <div class="slds-grid">
            <div class="slds-col">
                <p class="slds-text-heading--label">Campings</p>
                <h1 class="slds-text-heading--medium">My Campings</h1>
            </div>
        </div>
    </div>
    <!-- PAGE HEADER -->
	
	<!-- NEW CAMPING FORM -->
    <div class="slds-grid">
        <div class="slds-col slds-col--padded sls-p-top--large">
            <c:campingListForm />
        </div>
        
        <div class="slds-col slds-col--padded sls-p-top--large">
            <div area-labelledby="allcamping">
                <fieldset class="slds-box slds-theme--default slds-container--small">
                	<legend id="allcamping" class="slds-text-heading--small slds-p-vertical--medium">All Campings</legend>
                    
                    <aura:iteration items="{!v.items}" var="singleItem">
                        <div class="slds-box slds-theme--default slds-container--small">
                        	<c:campingListItem item="{!singleItem}" />
                        </div>
                    </aura:iteration>
                </fieldset>
            </div>
        </div>
    </div>
    
    
</aura:component>
campingListController:
({
	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 if(state === "ERROR"){
                console.log('Failed with below state: ' + state);
                var errors = response.getError();
                if (errors) {
                	if (errors[0] && errors[0].message) {
                    	console.log("Error message: " + errors[0].message);
                    }else{
                        console.log("Unknown Error");
                    }
                }
            }
        });
    
   		$A.enqueueAction(action); 
	},

    handleAdditem : function(component, event, helper){
        helper.createCampings(component, event.getParam("item"));
    }
})
campingListHelper:
({
    createCampings : function(component,camping) {
        var allCampings = component.get("v.items");
        var newCamping = JSON.parse(JSON.stringify(camping));
        
        // set the sobjectType!
        camping.sobjectType='sudipta__Camping_Item__c';
        
        var action = component.get("c.saveItem");
        action.setParams({"item" : camping});
        
        action.setCallback(this, function(response){
            var state = response.getState();
            if(component.isValid() && state === "SUCCESS"){
                allCampings.push(newCamping);
                component.set("v.items",allCampings);
            }else if(state === "ERROR"){
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + errors[0].message);
                    }else{
                        console.log("Unknown Error");
                    }
                }
            }
        });        
        
        $A.enqueueAction(action);
    }
})

Now the other component:
campingListForm.cmp:
<aura:component controller="sudipta.CampingListController" implements="force:appHostable,flexipage:availableForAllPageTypes">
    <aura:attribute name="newItem" type="Camping_Item__c" description="Single Camping Item" required="true" 
                                                        default="{'sObjectType':'sudipta__Camping_Item__c',
                                                                'Name':'Item1',
                                                                'sudipta__Quantity__c':10,
                                                                'sudipta__Price__c':100,
                                                                'sudipta__Packed__c':false}"/>
    <aura:registerEvent name="addItem" type="c:addItemEvent"/>
                                                              
    <div area-labelledby="newcampingform">
        <fieldset class="slds-box slds-theme--default slds-container--small">
            <legend id="newcampingform" class="slds-text-heading--small slds-p-vertical--medium">Add Camping</legend>
            
            <form class="slds-form--stacked">
                <div class="slds-form-element slds-is-required">
                    <div class="slds-form-element__control">
                        <ui:inputText aura:id="campName" label="Campingn 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="campQuantity" label="Quantity" class="slds-input" labelClass="slds-form-element__label"
                                        value="{!v.newItem.sudipta__Quantity__c}" required="true"/>
                    </div>
                </div>
                
                <div class="slds-form-element">
                    <div class="slds-form-element__control">
                        <ui:inputCurrency aura:id="campPrice" label="Price" class="slds-input" labelClass="slds-form-element__label"
                                          value="{!v.newItem.sudipta__Price__c}"/>
                    </div>
                </div>
                
                <div class="slds-form-element">
                    <div class="slds-form-element__control">
                        <ui:inputCheckbox aura:id="campPacked" label="Packed?" class="slds-checkbox" labelClass="slds-form-element__label"
                                          value="{!v.newItem.sudipta__Packed__c}"/>
                    </div>
                </div>
                
                <div class="slds-form-element">
                    <ui:button label="Create Camping" class="slds-button slds-button--destructive" press="{!c.submitForm}"/>
                </div>
            </form>
        </fieldset>
    </div>
</aura:component>

campingListForm Controller:
({
	submitForm : function(component, event, helper) {
		console.log('Enterred the saveCamping');
		var validCamping = true;
        var campingName = component.find("campName").get("v.value");
        var campingQuantity = component.find("campQuantity").get("v.value");
        var campingPrice = component.find("campPrice").get("v.value");
        
        if($A.util.isEmpty(campingName)){
            validCamping = false;
            component.find("campName").set("v.errors",[{message: "Name can't be blank"}]);
        }else{
            component.find("campName").set("v.errors",null);
        }
        if($A.util.isEmpty(campingQuantity)){
            validCamping = false;
            component.find("campQuantity").set("v.errors",[{message: "Quantity can't be blank"}]);
        }else{
            component.find("campName").set("v.errors",null);
        }
        if($A.util.isEmpty(campingPrice)){
            validCamping = false;
            component.find("campPrice").set("v.errors",[{message: "Price can't be blank"}]);
        }else{
            component.find("campName").set("v.errors",null);
        }
        
        if(validCamping){
            var newCamping = component.get("v.newItem");
            helper.createItem(component,newCamping);
        }
	}
})

campingListForm Helper:
 
({
	createItem : function(component,item) {
		console.log("Inside the helper function");
		var addEvent = component.getEvent("addItem");
		addEvent.setParams({"item" : item});
		addEvent.fire();
		component.set("v.newItem",{'sObjectType':'sudipta__Camping_Item__c',
                                           'Name': '',
                                           'sudipta__Quantity__c': 0,
                                           'sudipta__Price__c': 0,
                                           'sudipta__Packed__c': false});
	}
})

Controller: CampingListController
public with sharing class CampingListController {
	@AuraEnabled
    public static List<sudipta__Camping_Item__c> getItems(){
        return [SELECT	ID,
               			NAME,
                		sudipta__Quantity__c,
                		sudipta__Price__c,
                		sudipta__Packed__c
                FROM sudipta__Camping_Item__c];
    }
    
    @AuraEnabled
    public static sudipta__Camping_Item__c saveItem(sudipta__Camping_Item__c item){
        System.Debug('Inside saveCamping Method');
        Database.SaveResult[] result = Database.insert(new List<sudipta__Camping_Item__c>{item}, false);
        for (Database.SaveResult sr : result) {
            if(sr.isSuccess()){
                System.Debug('Operation is successful');
            }else{
                for(Database.Error err : sr.getErrors()) {
                	System.Debug('Error message: ' + err.getMessage());
                    System.Debug('Failed for below fields: ' + err.getFields());
                }
            }
        }
        return item;
    }
}
Event: addItemEvent
<aura:event type="COMPONENT" description="Event template" >
	<aura:attribute name="item" type="sudipta__Camping_Item__c"/>
</aura:event>

What is happening is that:
Data is getting saved in Salesforce.
New Data is also inserted into the "items" variable and thus getting displayed in the UI.

But Trailhead is giving me the below error:
User-added image

I really would like to know what is the issue or  bad programming I did so that Trailhead is not allowing me to complete the module. 
I will really appreciate if you can help me here. Thanks.
Best Answer chosen by Sudipta Deb
Sudipta DebSudipta Deb
Hi All,

Finally, I did the below changes to make things work -
  • Trailhead module will work only on those orgs where there is no namespace. So I need to remove the namespace i.e. sudipta__ from all the places to make the trailhead happy. 
  • I changed the order to event and action attribute while declaring the handler in campingList component and it worked. The new code -
<aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}" />
I need to check whether the order of attributes is really important while declaring event handler. 
Sharing the solution here. Hoping it will help others. Thanks.