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
Frederick H LaneFrederick H Lane 

Lightning Component Specialist - Step 5 Error

I'm getting an error; " 
The BoatSearchResults controller must have an onBoatSelect function that stores the boatId in the component’s selectedBoatId attribute."
Here's my code;
BoatSelect.evt
<aura:event type="COMPONENT" description="fires when a user clicks a boat on BoatSearchResults.cmp">
    <aura:attribute name="BoatId" type="Id"/>
</aura:event>
BoatTile.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >

              <aura:attribute name="boat" type="Boat__c" />

    <aura:attribute name="selected" type="Boolean" default="false" />



    <aura:registerEvent name="BoatSelect" type="c:BoatSelect"/>

   

    <lightning:button name="{!v.boat.Id}" class="{!v.selected? 'tile selected' : 'tile'}"
                       onclick="{!c.onBoatClick}" >
        <div style="{! 'background-image:url(\'' + v.boat.Picture__c + '\'); '}" class="innertile">
          <div class="lower-third">
           <h1 class="slds-truncate">{!v.boat.Contact__r.Name}</h1>
          </div>
        </div>
    </lightning:button>
</aura:component>
BoatTile.css
.THIS {
}

.THIS.tile {
    position:relative;
    display: inline-block;
    width: 100%;
    height: 220px;
    padding: 1px !important;
}

.THIS .innertile {
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    width: 100%;
    height: 100%;
}

.THIS .lower-third {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    color: #FFFFFF;
    background-color: rgba(0, 0, 0, .4);
    padding: 6px 8px;
}

.THIS.selected {
    border: 3px solid rgb(0, 112, 210);
}

BoatSearchResults.aspx
public with sharing class BoatSearchResults  {
    @AuraEnabled
     public static List <Boat__c> getBoats(String boatTypeId) {
      if(boatTypeId != '')  {
             return [SELECT id, BoatType__c, picture__c, name,contact__r.Name
                    FROM Boat__c
                    WHERE BoatType__c =:boatTypeId];
      } else {
          return [SELECT id, BoatType__c, picture__c, name,contact__r.Name
                    FROM Boat__c];
      }
         }
         }

BoatSearchResults.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes"
                access="global" controller="BoatSearchResults">
    <aura:handler name="init" action="{!c.doSearch}" value="{!this}"/>
    <aura:attribute name="boatTypeId" type="String" />
    <aura:attribute name="boats" type="Boat__c[]" />
    <aura:handler name="BoatSelect" event="c:BoatSelect" action="{!c.onBoatSelect}"/>
     <aura:attribute name="selectedBoatId" type="Id"/>

    <lightning:layout multipleRows="true" >

        <aura:iteration items="{!v.boats}" var="boat">
            <lightning:layoutItem  padding="around-small">
                <c:BoatTile boat="{!boat}"
                            selected="{!boat.Id == v.selectedBoatId ? 'true' : 'false' }"/>
            </lightning:layoutItem>
        </aura:iteration>

        <aura:if isTrue="{!v.boats.length==0}">
            <lightning:layoutItem class="slds-align_absolute-center" flexibility="auto" padding="around-small">
                <ui:outputText value="No boats found" />
            </lightning:layoutItem>
        </aura:if>

    </lightning:layout>
</aura:component>

BoatTileController.js
({
    onBoatClick : function(component, event, helper) {
        var cmpEvent = component.getEvent("BoatSelect");
        var boatId = event.getSource().get("v.name");
        cmpEvent.setParams({
            "boatId" : boatId
        });
        cmpEvent.fire();
    }
})

BoatSearchResultsController.js
({
 doSearch : function(component, event, helper) {
  alert(component.get("v.boatTypeId")); //<---here I am getting undefined
        helper.onSearch(component); //calling helper method
 },
     search: function(component, event, helper){
        var params = event.getParam('arguments');
        alert(params.boatTypeId); //<---getting proper value
        alert(component.set("v.boatTypeId", params.boatTypeId)); //<---here I am getting undefined
        var a = component.get('c.doSearch');
        $A.enqueueAction(a);
    }
})

BoatSearchResultsHelper.js
({
    onSearch : function(component, event) {
        var boatTypId = component.get("v.boatTypeId");
        alert(boatTypId); //<---here I am getting undefined
        console.log("boatTypId--> " + boatTypId);
        var action = component.get("c.getBoats");
        action.setParams({boatTypeId:boatTypId});

        //add the callback behavior for when the response is received
        action.setCallback(this, function(response){
            var state = response.getState();
            console.log("state " + state);
            if(state === "SUCCESS"){
                var res = response.getReturnValue();
                component.set("v.boats", res);
                //console.log("v.boats ->"+ JSON.stringify(res));
            }
            else{
                console.log("Failed with state: " + state);
            }
        });

        //send action off to be executed
        $A.enqueueAction(action);
    },
})
BoatSearchResultsHelper.js
({
    onSearch : function(component) {
        var action = component.get("c.getBoats");
        action.setParam({"boatTypeId":""});
        action.setCallback(this, function(response){
        var status = response.getState();
            if(status === "SUCCESS"){
             if(! $A.util.isEmpty(response.getReturnValue())){
                    component.set("v.boatTypeId",response.getReturnValue());
                } else {
                     component.set("v.recordError","No boats found");
                }
            }
        });
        $A.enqueueAction(action);
    }
})
 
Best Answer chosen by Frederick H Lane
Frederick H LaneFrederick H Lane
Many thanks. There was also a .evt missing

All Answers

Raj VakatiRaj Vakati
Refer this link for code

https://github.com/grivero/Lightning-Component-Framework-Specialist/blob/master/src/aura/BoatSearchResults/BoatSearchResults.cmp
Raj VakatiRaj Vakati
<aura:component implements="flexipage:availableForAllPageTypes" controller="BoatSearchResults">
        
    <!-- ATT -->
    <aura:attribute name="boatTypeId" type="String" />
    <aura:attribute name="boats" type="Boat__c[]" />
    <aura:attribute name="selectedBoatId" type="Id"/>
    <!-- HANDLERS -->
    <aura:handler name="BoatSelect" event="c:BoatSelect" action="{!c.onBoatSelect}"/>
    <aura:handler name="init" action="{!c.doSearch}" value="{!this}"/>
    <!-- AURA METHOD -->
    <aura:method name="search" description="public invokable method to search by boatTypeId">
        <aura:attribute name="boatTypeId" type="String" />
    </aura:method>
    
    <lightning:layout multipleRows="true" >
            
        <aura:iteration items="{!v.boats}" var="boat">
            <lightning:layoutItem  padding="around-small">
                <c:BoatTile boat="{!boat}" selected="{!boat.Id == v.selectedBoatId ? true : false }"/>
            </lightning:layoutItem>
        </aura:iteration>

        <aura:if isTrue="{!empty(v.boats)}">
            <lightning:layoutItem class="slds-align_absolute-center" flexibility="auto" padding="around-small">
                <ui:outputText value="No boats found" />
            </lightning:layoutItem>
        </aura:if>

    </lightning:layout>    

</aura:component>
© 2018 GitHub, Inc.
 
({
	doSearch : function(component, event, helper) {		
		helper.onSearch(component,"");
	},
   
    search: function(component, event, helper){

        let params = event.getParam('arguments');
        const boatTypeId = params.boatTypeId;
        console.log("boatTypeId extracted in BoatSearchResultsController: " + boatTypeId);
        component.set("v.boatTypeId", boatTypeId);
        helper.onSearch(component,boatTypeId);
		return "search complete.";
		
    },
    
    onBoatSelect : function(component, event, helper) {
        const boatId = event.getParam("boatId");
        console.log("selected boat id in BoatSearchResult: " + boatId);
        component.set("v.selectedBoatId",boatId);
    }
	
})
 
({
	
	onSearch : function(component,boatTypeId) {
		// Request from server
		console.info("boatTypeId in BoatSearchResultHelper.onSearch: " + boatTypeId);
		let action = component.get("c.getBoats");
		// assign empty boatTypeId
		if($A.util.isEmpty(boatTypeId)){
			action.setParams({"boatTypeId":""});		
		}else{			
			action.setParams({"boatTypeId":boatTypeId});		
		} 
		
		action.setCallback(this, function(result){

			var status = result.getState();
            if(status === "SUCCESS"){
             	if(!$A.util.isEmpty(result.getReturnValue())){
					let boats = result.getReturnValue();            		
					component.set("v.boats",boats);
				}else{
					component.set("v.boats",null);
				}
			}
			
        });
        $A.enqueueAction(action);
	}

})
© 2018 GitHub, Inc.

 
Frederick H LaneFrederick H Lane
That worked thanks. The only other piece I realised was missing was;
BoatReviewAdded.evt

<aura:event type="APPLICATION" description="Event template">
    <aura:attribute name="BoatReviewAdded" type="Boat__c"/>
</aura:event>

BoatSelected.evt

<aura:event type="APPLICATION" description="BoatSelected fired from BoatTileController's onBoatClick handler">
    <aura:attribute name="boat" type="Boat__c"/>
</aura:event>

BoatSelect.evt

<aura:event type="COMPONENT" description="Event template" >
    <aura:attribute name="boatId" type="String"/>
</aura:event>
Frederick H LaneFrederick H Lane
Many thanks. There was also a .evt missing
This was selected as the best answer
Maxab JMaxab J
It worked for me on https://rgbadvisor.com/