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?

4
  • 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
    Commented May 22, 2018 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
    Commented May 22, 2018 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. Commented May 22, 2018 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. Commented May 22, 2018 at 21:57

4 Answers 4

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.

In addition as oobarbazanoo pointed out in the comments, for trailhead purposes only you need to have the code in the controller and not in the helper. Normally it would be fine to have it in the helper.

5
  • 4
    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. Commented May 23, 2018 at 13:51
  • 2
    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 Commented Sep 26, 2018 at 0:52
  • had the exact same problem with the controller....you must make the call to the apex class in the controller. not in the helper... at least to pass the module Commented Jan 10, 2021 at 1:24
  • traihaead sux and so does salesforce.
    – PAS
    Commented Nov 30, 2022 at 3:48
  • @oobarbazanoo, Thanks a million. I couldn't understand why code is not passing as I was able to add things in the application.
    – PAS
    Commented Nov 30, 2022 at 3:49
1

I got the same error while doing the challenge. My code was correct but the problem is that I was passing values to a method in campingListHelper from campingListController while the challenge wanted us to put the helper method inside controller.

0

in campingListController.js, you have updateExpense in:

handleAddItem: function(component, event, helper) {
    var item = event.getParam("item");
    helper.updateExpense(component, item);
}

I assume "expense" is not correct? Probably from copy and paste from expense example.

0
0

también me encontré con este problema y leí qué desafío nos pide que hagamos línea por línea.

El desafío de Trailhead no nos pide que llamemos al método HELPER desde campingListController.js. Simplemente nos dice que realicemos la acción de guardar en el método campingListcontroller.js en su lugar. Esto no es muy intuitivo ya que el módulo trailhead realiza la operación de guardado en el método HELPER.

Simplemente copie y pegue el código de campingListHelper.js a campingListController.js para pasar el desafío. Esto es raro, pero así es como es.

Espero que esto ayude a otros.

Gracias,

1
  • 1
    Bienvenidos a SFSE. La idioma de este sitio es Ingles - ¿puede traducir su puesto, por favor?
    – David Reed
    Commented May 31, 2022 at 17:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .