3

The Trailhead challenge check is telling me the following:

Challenge Not yet complete... here's what's wrong: The campingList JavaScript controller isn't calling 'saveItem' in the Apex class to save the record or setting the 'item' as a parameter for the 'saveItem' action.

The app is really not working as expected, but I can not guess why is that.

Here is my code.

campingApp.app :

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

camping.cmp :

<aura:component controller="CampingListController">
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:attribute name="items" type="Camping_Item__c[]" default="[]"/>
    <aura:attribute name="item" type="Camping_Item__c" default="{ 
                        'sobjectType': 'Camping_Item__c',
                        'campingItemName': 'campingItemNameDefault',
                        'Price__c': 0,
                        'Quantity__c': '0',
                        'Packed__c': false }"/>
    <c:campingHeader />
    <!-- <c:campingItemForm item="{!v.item}" items="{!v.items}" /> -->
    <c:campingList items="{!v.items}"/>
</aura:component>

CampingListController.apxc :

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

campingController.js :

({
    logArray : function(component, event, helper) {
        var items = component.get("v.items");
        console.log(JSON.stringify(items));
    },
    doInit: function(component, event, helper) {
        var action = component.get("c.getItems");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.items", response.getReturnValue());
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
        $A.enqueueAction(action);
    }
})

campingHeader.cmp :

<aura:component >
<lightning:layout class="slds-page-header slds-page-header--object-home">
        <lightning:layoutItem >
            <lightning:icon iconName="action:goal"/>
        </lightning:layoutItem>
        <lightning:layoutItem padding="horizontal-small">
            <div class="page-section page-header">
                <h1 class="slds-text-heading--label">Camping list</h1>
            </div>
        </lightning:layoutItem>
    </lightning:layout>

</aura:component>

campingList.cmp :

<aura:component controller="CampingListController">
    <aura:attribute name="items" type="Camping_Item__c[]" />
    <aura:handler name="addItemEvent" event="c:addItemEvent"
        action="{!c.handleAddItem}"/>
    <c:campingListForm/>
    <aura:iteration items="{!v.items}" var="item">
        <c:campingListItem item="{!item}" />
    </aura:iteration>

</aura:component>

campingListController.js :

({
    clickCreateItem : function(component, event, helper) {
        var isValidItem = component.find('campingItemForm').reduce(function(total,currentValue, index,arr)
                                                           {
                                                               currentValue.showHelpMessageIfInvalid();
                                                               return total && currentValue.get('v.validity').valid;
                                                           }, true);
        if(isValidItem) {
            helper.createItem(component);
        }
    },
    doInit: function(component, event, helper) {
        var action = component.get("c.getItems");
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (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");
        helper.updateExpense(component, item);
    }
})

campingListHelper.js :

({
    createItem : function(component, item) {
        var action = component.get("c.saveItem");

            //var newItem = component.get("v.newItem");
            action.setParams({
                "item": item
            });

            action.setCallback(this, function(response){
                var state = response.getState();
                if (state === "SUCCESS") {
                    var items = component.get("v.items");
                    items.push(response.getReturnValue());
                    item = {sobjectType: "Camping_Item__c"};;
                    component.set("v.newItem", item);
                    component.set("v.items", items);
                }
            });

            $A.enqueueAction(action);
    }
})

campingListForm.cmp :

<aura:component controller="CampingListController">
    <aura:attribute name="newItem" type="Camping_Item__c" default="{ 
                        'sobjectType': 'Camping_Item__c',
                        'campingItemName': 'SSSSSSSSSSSSSSSSS',
                        'Price__c': 0,
                        'Quantity__c': '0',
                        'Packed__c': false }"/>
    <aura:registerEvent name="addItem" type="c:addItemEvent"/>
    <form class="slds-form--stacked">      
            <lightning:input aura:id="campingItemForm" label="Name"
                             name="Name"
                             value="{!v.newItem.Name}"/>
            <lightning:input type="number" aura:id="campingItemForm" label="Quantity"
                             name="Quantity"
                             min="1"
                             step="1"
                             value="{!v.newItem.Quantity__c}"
                             placeholder="0"/>
            <lightning:input type="number" aura:id="campingItemForm" label="Price"
                             name="Price"
                             formatter="currency"
                             value="{!v.newItem.Price__c}"/>
            <lightning:input aura:id="campingItemForm" type="checkbox" label="Packed"  
                             name="campingItemIsPacked"
                             checked="{!v.newItem.Packed__c}"/>
            <lightning:button label="Create Camping Item" 
                              class="slds-m-top--medium"
                              variant="brand"
                              onclick="{!c.clickCreateItem}"/>
        </form>
</aura:component>

campingListFormController.js :

({
    clickCreateItem : function(component, event, helper) {
        var isValidItem = component.find('campingItemForm').reduce(function(total,currentValue, index,arr)
                                                           {
                                                               currentValue.showHelpMessageIfInvalid();
                                                               return total && currentValue.get('v.validity').valid;
                                                           }, true);
        if(isValidItem) {
            helper.createItem(component);
        }
    }
})

campingListHelper.js :

({
    createItem : function(component) {
        var action = component.get("c.saveItem");

            var newItem = component.get("v.newItem");
            action.setParams({
                "item": newItem
            });

            action.setCallback(this, function(response){
                var state = response.getState();
                if (state === "SUCCESS") {
                    /* var items = component.get("v.items");
                    items.push(response.getReturnValue());
                    newItem = {sobjectType: "Camping_Item__c"};
                    component.set("v.newItem", newItem);
                    component.set("v.items", items); */

                    var addItemEvent = component.getEvent("addItem");
                    addItemEvent.setParams({"item": response.getReturnValue()});
                    addItemEvent.fire();
                    newItem = {sobjectType: "Camping_Item__c"};
                    component.set("v.newItem", newItem);
                }
            });

            $A.enqueueAction(action);
    }
})

addItemEvent.evt :

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

As you can tell from the line :

<aura:component controller="CampingListController">

in the campingList.cmp the .apxc controller is attached to the campingList.cmp.

Next, as you can tell from the line :

helper.updateExpense(component, item);

in the campingListController.js and then from the line :

var action = component.get("c.saveItem");

in the campingListHelper.js the .apxc function saveItem is called.

So, what is still needs to be checked is that the event arrives to the handler in campingList.cmp.

Here is the event which will be thrown from the campingListForm.cmp :

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

and in the campingListFormHelper.js it is actually thrown :

addItemEvent.fire();

Hence I am expecting the even to get caught in the campingList.cmp over here:

<aura:handler name="addItemEvent" event="c:addItemEvent"
        action="{!c.handleAddItem}"/>

and as a result call the saveItem function.

But the Trailhead continues to tell me that that call does not happen. And after debugging I see that it really does not. What am I doing wrong here?

  • I couldn't find the answer I posted for another user related to the same trailhead module. The short version is that that specific trailhead has bugs. I've had code that throws errors pass the challengers and code that works fail the challenges. If your app works, just go to the next module. Since your app actually doesn't work, there's obviously an issue, I'm just saying, be prepared for more issues in that module. – gNerb May 22 '18 at 17:47
  • 3
    As an addendum, also consider that the trailhead is technically out of date; it references deprecated style classes and may have been affected by changes to aura:valueChange in Summer '18. You might need to modify the files differently than it suggests. – sfdcfox May 22 '18 at 17:54
  • Thanks for the attention, but since I am learning basic things above I am almost sure there is something I am missing. – oobarbazanoo May 22 '18 at 18:22
  • A tip: open the developer console when you submit the challenge - you can often pinpoint the exact line that is failing in the debug log and fix it. Also note that for some reason, trailhead has been written to expect code in a very rigid format - for example, String[] will trip it up if it's expecting a List<String> - another example I had recently System.Queueable vs Queueable caused me not to get the challenge on first try. I think the trailhead team needs some more creativity + people better at regex expressions. – Caspar Harmer May 22 '18 at 21:57
3

You need to register the handler with the same name as when you register the event.

You have

<aura:registerEvent name="addItem" type="c:addItemEvent"/>
<aura:handler name="addItemEvent" event="c:addItemEvent"
        action="{!c.handleAddItem}"/>

You need

<aura:registerEvent name="addItem" type="c:addItemEvent"/>
<aura:handler name="addItem" event="c:addItemEvent"
        action="{!c.handleAddItem}"/>

For more information, take a look at the docs for handling component events.

This is assuming of course that you have confirmed that your event is actually firing.

  • 3
    That is one part of the problem. Another part is that the Trailhead wanted me to put the code of calling .apxc function directly in the controller, not in the helper. It is really against a good design and I hate that. Hope they will change the challenge check in future. – oobarbazanoo May 23 '18 at 13:51
  • 1
    Gah! This is horrible. I've been banging my head against the desk for 30 minutes trying to figure this out... Moved the code to the Controller from the Helper and now passes the module :P – Sam Whitmore Sep 26 '18 at 0:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.