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
JustinShankleJustinShankle 

Lightning Components Basics Input Data Using Forms

I had so much trouble with this one section that I feel like I have to post this to help out those who are struggling as much as me. Three hours for a simple controller... The problem is the way trailhead tests the challenge is hard for them and they have very strict criteria that is not implicit in the challenge directions.

camingList.cmp
<aura:component >
    
	<aura:attribute name="newItem" type="Camping_Item__c"  default="{'sobjectType': 'Camping_Item__c', 'Quantity__c': 0, 'Price__c': 0.00}" required="true"/>
	<aura:attribute name="items" type="Camping_Item__c[]"/>
	<lightning:layout class="slds-page-header slds-page-header--object-home">
        <lightning:layoutItem >
            <lightning:icon iconName="standard:scan_card" alternativeText="My Campign Items"/>
        </lightning:layoutItem>
        <lightning:layoutItem padding="horizontal-small">
            <div class="page-section page-header">
                <h1 class="slds-text-heading--label">Camping Items</h1>
                <h2 class="slds-text-heading--medium">My Camping Items</h2>
            </div>
        </lightning:layoutItem>
    </lightning:layout>
    <!-- / PAGE HEADER -->

    <!-- NEW EXPENSE FORM -->
    <lightning:layout >
        <lightning:layoutItem padding="around-small" size="6">

        <!-- CREATE NEW EXPENSE -->
    <div aria-labelledby="newitemform">

        <!-- BOXED AREA -->
        <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>
  
        <!-- CREATE NEW EXPENSE FORM -->
        <form class="slds-form--stacked">          
            <lightning:input aura:id="itemform" label="Item Name"
                             name="itemname"
                             value="{!v.newitem.Name}"
                             required="true"/> 
            <lightning:input type="itemform" aura:id="itemform" label="Amount"
                             name="itemprice"
                             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 aura:id="itemform" label="Quantity"
                             name="itemquantity"
                             value="{!v.newitem.Quantity__c}"
							 min="1"
							 type="number"
							 required="true"
							 messageWhenRangeUnderflow="Enter minimum 1 Quantity"/>
                             />
            <lightning:input type="checkbox" aura:id="itemform" label="Packed?"  
                             name="packed"
							 class="slds-checkbox"
                             checked="{!v.newitem.Packed__c}"/>
            <lightning:button label="Create Camping Item" 
                              class="slds-m-top--medium"
                              variant="brand"
                              onclick="{!c.clickCreateItem}"/>
        </form>
        <!-- / CREATE NEW EXPENSE FORM -->
  
      </fieldset>
      <!-- / BOXED AREA -->

    </div>
    <!-- / CREATE NEW EXPENSE -->

        </lightning:layoutItem>
    </lightning:layout>
    <!-- / NEW EXPENSE FORM -->
	
	<div class="slds-card slds-p-top--medium">
        <header class="slds-card__header">
            <h3 class="slds-text-heading--small">Camping</h3>
        </header>
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="cmp">
                    <c:campingListItem item="{!cmp}"/>
                </aura:iteration>
            </div>
        </section>
	</div>
</aura:component>

campingList.Controller
({
		handleAddItem: function(component, event, helper) {
    var addItm = event.getParam("item");
    helper.createItem(component, addItm);

},

    createItem: function(component, newItem) {
    var action = component.get("c.clickCreateItem");
    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(response.getReturnValue());
            component.set("v.items", items);
        }
    });
    $A.enqueueAction(action);
},
    clickCreateItem: function(component, event, helper) {
		
        var validItem = component.find('itemform').reduce(function (validSoFar, inputCmp) {
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid();
            return validSoFar && inputCmp.get('v.validity').valid;
        }, true);
        
        
        // ... hint: more error checking here ...

        // If we pass error checking, do some real work
        if(validItem){
            // Create the new expense
            var newItem = component.get("v.newItem");
            console.log("Create Camping Item: " + JSON.stringify(newCamping));
            handleAddItem(component, newItem);
        }
		component.set("v.newItem",{'sobjectType':'Camping_Item__c',
                'Name': '',
                'Quantity__c': 0,
                'Price__c': 0,
                'Packed__c': false});
    }
})
camping.cmp
<aura:component implements="flexipage:availableForRecordHome">
	<br/><br/><br/>
    <c:campingHeader />
    <c:campingList />
</aura:component>

campingHeader.cmp
<aura:component >
	<lightning:layout class="slds-page-header">
		<lightning:layoutItem >
			<lightning:icon iconName="action:goal"/>
		</lightning:layoutItem>

		<div class="slds-page-header">
			<h1 class="slds-text-heading--label"> Camping List </h1>
		</div>
	</lightning:layout>
</aura:component>

campingListItem.cmp
 
<aura:component >
    <aura:attribute name="item" type="Camping_Item__c"/>
    
    <p>Name:
        <ui:outputText value="{!v.item.Name}"/>
    </p>
    <p>Price:
        <ui:outputCurrency value="{!v.item.Price__c}"/>
    </p>
    <p>Quantity:
        <ui:outputNumber value="{!v.item.Quantity__c}"/>
    </p>
    <p>Packed:
        <ui:outputCheckbox value="{!v.item.Packed__c}"/>
    </p>
</aura:component>

Any other controllers or helper classes are not needed. That is the trick here. Using helper classes when not specified fails the challenge. I hope you will read through and understand this code as I have to realize why it is this way and not simply copypasta.

Enjoy!
 
Best Answer chosen by JustinShankle
JustinShankleJustinShankle
Solved!

All Answers

JustinShankleJustinShankle
Solved!
This was selected as the best answer
Rafael Suarez MarquezRafael Suarez Marquez
Congrats Justin. It passes.  A few notes though:
  • Good catch on cleaning up the campingList helper  
  • Your code locks up data entry to the boxes... Shows how messed up is the trailhead validation
    • Check Line 39, you have your input type mixed up....   should be " <lightning:input type="number" aura:id="itemform"
  • Your Header code's duplicated. It's in campingHeader.cmp and in campingList.cmp
  • Did you mean  camping.app instead of camping.cmp for the preview harness?
  • There's an extra  "/> " at line 53 in campingList
  • The js action from lines 2-22 in the campingList controller  is not used
  • How much do you love the lack of documentation on the 'component.find /reduce' function ? 
Mike ChandlerMike Chandler
Do the Trailhead gods know that this module is a bit like drinking from a firehose? I'm not sure there's a ton that I can actually retain from this module.  I suspect I'll be returning to it for days to come...
Kashona DotsonKashona Dotson
In reference to Rafael's comment, how do you get this to work? I cleaned up the list Rafael presented and am unable to enter any value into the fields.