Lightning Component Framework Super Badge
Lightning Component Framework Specialist super badge step 2
Build the query-by-example form
Create a form displaying a drop-down that lists each boat type, along with Search and New buttons, using BoatSearchForm.cmp, BoatSearchResults.cmp, and
BoatSearch.cmp, as described in the business requirements.
Add these components to a Lightning page named Friends with Boats, and activate the page as a new tab in Lightning Experience and the Salesforce App.
Lastly, create a Lightning application named FriendswithBoats.app that has a layout that is similar to the Lightning page.
BoatSearchForm.cmp
<aura:component description="BoatSearchForm" controller="BoatSearchFormController" implements="flexipage:availableForAllPageTypes">
<aura:registerEvent name="launchNewBoatForm" type="c:launchNewBoatForm"/>
<!-- Handle component init in a client-side controller -->
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:handler name="launchNewBoatForm" event="c:launchNewBoatForm" action="{!c.handleNewBoatForm}"/>
<!-- dynamically load the BoatTypes -->
<aura:attribute name="BoatTypes" type="BoatType__c[]" />
<aura:attribute name="selectedType" type="String" default="foo"/>
<aura:attribute name="standAlone" type="Boolean" default="true"/>
<article class="slds-card slds-m-bottom_medium">
<div class="slds-media__body">
<div>
<lightning:layout horizontalAlign="center" verticalAlign="center" >
<lightning:layoutItem padding="horizontal-medium">
<!-- Create a dropdown menu with options -->
<lightning:select aura:id="boatTypes" label="" name="selectType"
onchange="{!c.handleChange}">
<option value="">All Types</option>
<aura:iteration items="{!v.BoatTypes}" var="boatType">
<option value="{!boatType.Id}">{!boatType.Name}</option>
</aura:iteration>
</lightning:select>
</lightning:layoutItem>
<lightning:layoutItem>
<div class="slds-button-group" role="group">
<lightning:button class="slds-button" variant="brand" label="Search" onclick="{!c.search}"/>
<br></br>
<aura:if isTrue="{!v.standAlone}">
<lightning:button class="slds-button" variant="neutral" label="New" onclick="{!c.newBoat}"/>
</aura:if>
</div>
</lightning:layoutItem>
</lightning:layout>
</div>
</div>
</article>
</aura:component>
BoatSearchController.js
({
doInit : function(component, event, helper){
helper.loadBoatTypes(component);
},
handleChange : function(component, event, helper){
console.log(component.find("boatTypes").get("v.value"));
component.set("v.selectedType", component.find("boatTypes").get("v.value"));
},
search : function(component, event, helper){
var selectedType = component.get("v.selectedType");
console.log("Search button pressed " + selectedType)
},
newBoat : function(component, event, helper){
var boatTypeId = component.get("v.selectedType");
console.log("New button pressed " + boatTypeId);
var requestNewBoat = component.getEvent("launchNewBoatForm");
requestNewBoat.setParams({"boatTypeId": boatTypeId});
requestNewBoat.fire();
},
handleNewBoatForm: function(component, event, helper){
console.log("handleNewBoatForm handler called.")
var boatTypeId = component.get("v.selectedType");
console.log(boatTypeId);
var createNewBoat = $A.get("e.force:createRecord");
createNewBoat.setParams({
"entityApiName": "Boat__c",
})
if(! boatTypeId==""){
createNewBoat.setParams({
"defaultFieldValues": {'BoatType__c': boatTypeId}
})
}
createNewBoat.fire();
},
//more handlers here
})
BoatSearchHelper.js
({
loadBoatTypes: function(component){
//create the action
console.log("Helper started");
var action = component.get("c.getBoatTypes");
//add the callback behavior for when the response is received
action.setCallback(this,function(response){
var state = response.getState();
if (state === "SUCCESS"){
component.set("v.BoatTypes", response.getReturnValue());
console.log(response.getReturnValue());
}
else {
console.log("Failed with state: " + state);
}
});
//send action off to be executed
$A.enqueueAction(action);
},
})
Apex Controller:
public with sharing class BoatSearchFormController
{
@AuraEnabled
public static List<BoatType__c> getBoatTypes()
{
return [SELECT Id, Name from BoatType__c ORDER BY Name];
}
}
FriendswithBoats.app
<aura:application extends="force:slds">
<lightning:layout >
<lightning:card title="Find a Boat" class="slds-m-top_10px" >
<c:BoatSearchForm />
</lightning:card>
</lightning:layout>
</aura:application>
Event:
launchNewBoatForm.eve
<aura:event type="APPLICATION" description="Event template" >
<aura:attribute name="boat" type="Boat__c"/>
</aura:event>
Event:
launchNewBoatForm.eve
<aura:event type="APPLICATION" description="Event template" >
<aura:attribute name="boat" type="Boat__c"/>
</aura:event>
After that to create Lightning App Builder --- Create APP
Create a Lightning page named Friends with Boats that uses the Main Column and Right Sidebar Layout. Put the BoatSearch component in the main column. Activate the page as a new tab in Lightning Experience and the Salesforce App.
Implement the BoatTile and BoatSearchResults components
Create a new BoatTile component and update your BoatSearchResults container to loop through all the results returned from an Apex controller BoatSearchResults to display an unfiltered list of every boat that HowWeRoll leases.
Define the method getBoats() in BoatSearchResults, to return search results as described in the business requirements. BoatSearchResults.cmp displays search results with a helper method, onSearch(), and displays each result as a BoatTile component.
BoatSearchResults.cmp
<aura:component controller="BoatSearchResults">
<aura:handler name="init" action="{!c.doSearch}" value="{!this}"/>
<aura:attribute name="boats" type="Boat__c[]" />
<lightning:layout horizontalAlign="center" verticalAlign="center" multipleRows='true'>
<lightning:layoutItem flexibility="grow" class="slds-m-right_small" >
<aura:iteration items="{!v.boats}" var="boatVar">
<c:BoatTile boat="{!boatVar}"/>
</aura:iteration>
<aura:if isTrue="{!v.boats.length > 0}">
<aura:iteration items="{!v.boats}" var="bot">
<lightning:layoutItem flexibility="grow" class="slds-m-around_small">
<c:BoatTile boat="{!bot}" />
</lightning:layoutItem>
</aura:iteration>
<aura:set attribute="else">
<lightning:layoutItem class="slds-align_absolute-center" flexibility="auto" padding="around-small">
<ui:outputText value="No boats found" />
</lightning:layoutItem>
</aura:set>
</aura:if>
</lightning:layoutItem>
</lightning:layout>
</aura:component>
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);
},
})
BoatTile.cmp
<aura:component implements="flexipage:availableForAllPageTypes" access="global" >
<aura:attribute name="boat" type="Boat__c" />
<lightning:button class="tile">
<!-- Image -->
<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>
BoatTileHelper.js
.THIS.tile {
position:relative;
display: inline-block;
width: 100%;
height: 220px;
padding: 1px !important;
}
.THIS .innertile {
position: relative;
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.apex
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];
}
}
}
Implement the search filter
Create a FormSubmit event to allow your BoatSearchFormto pass the selected boat type to the BoatSearchResultscomponent, which queries Apex and stores the results.
Handle FormSubmit with a controller action, onFormSubmit, and pass formData.boatTypeId from the controller to search, a public method on the BoatSearchResults component. The search function uses a helper function, onSearch(), and controller function, doSearch(), to get the list of boats.
1. 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:method name="search" description="Sample method with parameter">
<aura:attribute name="boatTypeId" type="String" />
</aura:method>-->
<!-- set up the aura:method for search -->
<aura:method name="search" description="accepts boatTypeId
and executes search that refreshes the boats attribute">
<aura:attribute name="boatTypeId" type="Id"/>
</aura:method>
<!-- Display errors, if any -->
<!--
<aura:if isTrue="{!not(empty(v.errorString))}">
<div class="recordError">
<ui:message title="Error" severity="error" closable="true">
{!v.errorString}
</ui:message>
</div>
</aura:if>-->
<aura:if isTrue="{!not(empty(v.boats))}">
<lightning:layout multipleRows="true" horizontalAlign="center">
<aura:iteration items="{!v.boats}" var="boatVar">
<lightning:layoutItem flexibility="grow" class="slds-m-right_small" >
<c:BoatTile boat="{!boatVar}"/>
</lightning:layoutItem>
</aura:iteration>
</lightning:layout>
<aura:set attribute="else">
<div class="slds-align_absolute-center">No boats found</div>
</aura:set>
</aura:if>
</aura:component>
2. BoatSearchForm.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" controller="BoatSearchFormController" >
<aura:handler name="formsubmit"
event="c:FormSubmit"
action="{!c.onFormSubmit}"
phase="capture"/>
<aura:attribute name="searchOptions" type='String[]' default='All'/>
<aura:attribute name='searchOptionToIdMap' type='Map' default="{All:''}" />
<aura:attribute name='showNewButton' type='Boolean' default='false'/>
<lightning:layout horizontalAlign="center" >
<lightning:layoutItem class="slds-grid_vertical-align-center" >
<lightning:select aura:id='typeSelect' name='selectItem' label='' onchange=''>
<aura:iteration items='{!v.searchOptions}' var='option'>
<option value='{!option}' text='{!option}'></option>
</aura:iteration>
</lightning:select>
</lightning:layoutItem>
<lightning:layoutItem class="slds-grid_vertical-align-center" >
<lightning:button label="Search" variant="brand" onclick='{!c.onFormSubmit}' />
<aura:if isTrue='{!v.showNewButton}'>
<lightning:button variant='neutral' label='New' onclick='{!c.createBoat}'/>
</aura:if>
</lightning:layoutItem>
</lightning:layout>
</aura:component>
3. BoatSearch.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >
<lightning:card title="Find a Boat" class="slds-m-top_10px" >
<c:BoatSearchForm />
</lightning:card>
<lightning:card title="Matching Boats" >
<c:BoatSearchResults />
</lightning:card>
<aura:handler name="formsubmit"
event="c:FormSubmit"
action="{!c.onFormSubmit}"
phase="capture"/>
</aura:component>
4. BoatSearchFormController.js
({ createBoat: function (component,event,helper) {
var createRecordEvent = $A.get('e.force:createRecord');
if (createRecordEvent) {
var typeName = component.find('typeSelect').get('v.value');
var typeMap = component.get('v.searchOptionToIdMap');
var typeId = null;
if (typeName && typeMap && typeMap[typeName]) {
typeId = typeMap[typeName];
}
createRecordEvent.setParams({
'entityApiName': 'Boat__c',
'defaultFieldValues': {
'BoatType__c': typeId
}
});
createRecordEvent.fire();
}
},
onFormSubmit : function(component, event, helper){
var boatTypeId = component.get("v.selectedType");
console.log("Search button pressed " + boatTypeId);
var formSubmit = component.getEvent("FormSubmit");
formSubmit.setParams({"formData":
{"boatTypeId" : boatTypeId}
});
formSubmit.fire();
},
})
5. BoatSearchResults.apxc
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];
}
}
}
6. 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);
}
})
7. Event FormSubmit.evt
<aura:event type="COMPONENT" description="Boat Event">
<aura:attribute name="formData" type="Object"/>
</aura:event>
8. BoatSearchController.js
({
onFormSubmit : function(component, event, helper){
console.log("event received by BoatSearchController.js");
var formData = event.getParam("formData");
var boatTypeId = formData.boatTypeId;
var BSRcmp = component.find("BSRcmp");
var auraMethodResult = BSRcmp.search(boatTypeId);
console.log("auraMethodResult: " + auraMethodResult);
}
})
Highlight the selected boat
Fire a new BoatSelect event when a BoatTile is clicked, which sets the selectedBoatId on BoatSearchResults and in turn toggles the selected attribute on the right BoatTile, triggering the addition of a CSS class that shows a dark blue border around the selected boat as shown in the requirements.
Do this by defining a click handler on the BoatTile’s lightning:button that invokes controller function onBoatClick, and raises the BoatSelect event, as laid out in the business requirements.
1. BoatDetail.cmp
1.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>
2.BoatTile.css
.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-color: rgb(0, 112, 210);
border-style: solid;
border-width: 5px;
}
3.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];
}
}
}
4.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>
5.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();
}
})
6.BoatSearchResults.js
({
doSearch : function(component, event, helper) {
component.get("v.boatTypeId");
helper.onSearch(component, event, helper);
},
search: function(component, event, helper){
var params = event.getParam('arguments');
console.log("boatTypeId extracted: " + params.boatTypeId);
component.set("v.boatTypeId", params.boatTypeId);
helper.onSearch(component);
return "search complete.";
},
onBoatSelect : function(component, event, helper) {
var boatId = event.getParam("boatId");
console.log(boatId);
component.set("v.selectedBoatId",boatId);
}
})
7.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);
}
})
8.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>
Display boat details
Create two new components—BoatDetails and BoatDetail—as well as a new event BoatSelected. Raise the new event from BoatTile, and leverage Lightning Data Service to output boat details. Deploy the BoatDetails component in the top right corner of the Lightning page.
1. BoatDetail.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
<aura:attribute name="boat" type="Boat__c"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:attribute name="showButton" type="Boolean" default="false"/>
<lightning:button label="Full Details" onclick="{!c.onFullDetails }" />
<lightning:card iconName="utility:anchor">
<aura:set attribute="title">
{!v.boat.Contact__r.Name}'s boat
</aura:set>
<aura:set attribute="actions">
<aura:if isTrue='{!v.showButton}'>
<lightning:button label="Full Details" onclick="{!c.onFullDetails}"/>
</aura:if>
</aura:set>
<p class="slds-p-horizontal_small">
<lightning:layout >
<lightning:layoutItem flexibility="grow" size="6" mediumDeviceSize="6" largeDeviceSize="6">
<div class="slds-p-horizontal--small">
<div class="boatproperty">
<span class="label">Boat Name:</span>
<span><ui:outputText value="{!v.boat.Name}"/></span>
</div>
<div class="boatproperty">
<span class="label">Type:</span>
<span><ui:outputText value="{!v.boat.BoatType__r.Name}"/></span>
</div>
<div class="boatproperty">
<span class="label">Length:</span>
<span><ui:outputText value="{!v.boat.Length__c}"/> ft</span>
</div>
<div class="boatproperty">
<span class="label">Est. Price:</span>
<span><lightning:formattedNumber value="{!v.boat.Price__c}" currencyCode="USD" style="currency" currencyDisplayAs="symbol"/></span>
</div>
<div class="boatproperty">
<span class="label">Description:</span>
<span><ui:outputRichText class="slds-text-longform" value="{!v.boat.Description__c}"/></span>
</div>
</div>
</lightning:layoutItem>
<lightning:layoutItem flexibility="grow" size="6" mediumDeviceSize="6" largeDeviceSize="6">
<div class="imageview" style="{!'background-image:url(\'' + v.boat.Picture__c + '\')'}"/>
</lightning:layoutItem>
</lightning:layout>
</p>
</lightning:card>
</aura:component>
2. BoatDetailController.js
({
onFullDetails: function(component, event, helper) {
var navEvt = $A.get("e.force:navigateToSObject");
navEvt.setParams({
"recordId": component.get("v.boat.Id")
});
navEvt.fire();
}
})
3. BoatDetails.cmp
<aura:component description="BoatDetails"
implements="flexipage:availableForAllPageTypes">
<aura:attribute name="boat" type="Boat__c"/>
<aura:attribute name="recordError" type="String"/>
<aura:attribute name="id" type="Id" default="" access="public"/>
<aura:handler event="c:BoatSelected" action="{!c.onBoatSelected}"/>
<force:recordData aura:id="service"
recordId="{!v.id}"
mode="VIEW"
fields= "Id,
Name,
Description__c,
Price__c, Length__c,
Contact__r.Name,
Contact__r.Email,
Contact__r.HomePhone,
BoatType__r.Name,
Picture__c"
targetFields="{!v.boat}"
targetError="{!v.recordError}"
recordUpdated="{!c.onRecordUpdated}" />
<aura:if isTrue="{! !empty(v.boat)}">
<article class="slds-card">
<lightning:tabset>
<lightning:tab label="Details" id="details">
<c:BoatDetail boat="{!v.boat}"/>
</lightning:tab>
<lightning:tab label="Reviews" id="boatreviewtab">
Sample review
</lightning:tab>
<lightning:tab label="Add Review" id="addReview">
Sample add review
</lightning:tab>
</lightning:tabset>
</article>
</aura:if>
</aura:component>
4. BoatDetailsController.js
({
init: function(component, event, helper) {
component.set("v.enableFullDetails", $A.get("e.force:navigateToSObject"));
},
onFullDetails: function(component, event, helper) {
var navEvt = $A.get("e.force:navigateToSObject");
navEvt.setParams({
"recordId": component.get("v.boat.Id")
});
navEvt.fire();
},
onBoatSelected : function(component, event, helper) {
var boatSelected = event.getParam("boat");
component.set("v.id",boatSelected.Id);
var service = component.find("service");
service.reloadRecord() ;
},
onRecordUpdated : function(component, event, helper) {
},
onBoatReviewAdded : function(component, event, helper) {
console.log("Event received");
component.set("v.selTabId", "boatreviewtab");
}
})
5. BoatTile.cmp
<aura:component >
<aura:attribute name="boat" type="Boat__c" />
<aura:attribute name="selected" type="boolean" default="false"/>
<aura:registerEvent name="boatselected" type="c:BoatSelected"/>
<aura:attribute name="selectedBoatId" type="Id"/>
<aura:registerEvent name="BoatSelect" type="c:BoatSelect"/>
<lightning:button onclick="{!c.onBoatClick}"
class="{! v.selected ? 'tile selected' : 'tile' }">
<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>
6. BoatTileController.js
({
onBoatClick : function(component, event, helper) {
var BoatSelectEvent = component.getEvent('BoatSelect');
var boat = component.get('v.boat');
BoatSelectEvent.setParams({
"boatId" : boat.Id
});
BoatSelectEvent.fire();
//var BoatSelectedEvt = component.getEvent('boatselected');
var BoatSelectedEvt = $A.get('e.c:BoatSelected');
BoatSelectedEvt.setParams({
"boat" : boat
});
BoatSelectedEvt.fire();
}
})
7. BoatTile.css
.THIS.selected {
border: 3px solid rgb(0, 112, 210);
}
.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-color: rgb(0, 112, 210);
border-style: solid;
border-width: 5px;
}
8. BoatSearch.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >
<lightning:card title="Find a Boat" class="slds-m-top_10px" >
<c:BoatSearchForm />
</lightning:card>
<lightning:card title="Matching Boats" >
<c:BoatSearchResults />
</lightning:card>
<aura:handler name="formsubmit"
event="c:FormSubmit"
action="{!c.onFormSubmit}"
phase="capture"/>
</aura:component>
9. BoatSearchController.js
({
onFormSubmit : function(component, event, helper){
console.log("event received by BoatSearchController.js");
var formData = event.getParam("formData");
var boatTypeId = formData.boatTypeId;
var BSRcmp = component.find("BSRcmp");
var auraMethodResult = BSRcmp.search(boatTypeId);
console.log("auraMethodResult: " + auraMethodResult);
}
})
10. BoatSearchResults.apxc
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];
}
}
}
11. BoatSearchForm.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" controller="BoatSearchFormController" >
<aura:handler name="formsubmit"
event="c:FormSubmit"
action="{!c.onFormSubmit}"
phase="capture"/>
<aura:attribute name="searchOptions" type='String[]' default='All'/>
<aura:attribute name='searchOptionToIdMap' type='Map' default="{All:''}" />
<aura:attribute name='showNewButton' type='Boolean' default='false'/>
<lightning:layout horizontalAlign="center" >
<lightning:layoutItem class="slds-grid_vertical-align-center" >
<lightning:select aura:id='typeSelect' name='selectItem' label='' onchange=''>
<aura:iteration items='{!v.searchOptions}' var='option'>
<option value='{!option}' text='{!option}'></option>
</aura:iteration>
</lightning:select>
</lightning:layoutItem>
<lightning:layoutItem class="slds-grid_vertical-align-center" >
<lightning:button label="Search" variant="brand" onclick='{!c.onFormSubmit}' />
<aura:if isTrue='{!v.showNewButton}'>
<lightning:button variant='neutral' label='New' onclick='{!c.createBoat}'/>
</aura:if>
</lightning:layoutItem>
</lightning:layout>
</aura:component>
12. BoatSearchFormController.js
({ createBoat: function (component,event,helper) {
var createRecordEvent = $A.get('e.force:createRecord');
if (createRecordEvent) {
var typeName = component.find('typeSelect').get('v.value');
var typeMap = component.get('v.searchOptionToIdMap');
var typeId = null;
if (typeName && typeMap && typeMap[typeName]) {
typeId = typeMap[typeName];
}
createRecordEvent.setParams({
'entityApiName': 'Boat__c',
'defaultFieldValues': {
'BoatType__c': typeId
}
});
createRecordEvent.fire();
}
},
onFormSubmit : function(component, event, helper){
var boatTypeId = component.get("v.selectedType");
console.log("Search button pressed " + boatTypeId);
var formSubmit = component.getEvent("FormSubmit");
formSubmit.setParams({"formData":
{"boatTypeId" : boatTypeId}
});
formSubmit.fire();
},
})
12.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>
13. BoatSearchResultsController.js
({ search: function(component, event, helper)
{
var params = event.getParam('arguments');
component.set("v.boatTypeId", params.boatTypeId);
component.get("c,doSearch"); },
doSearch : function (component, event, helper)
{ component.get("v.boatTypeId"); helper.onSearch(component); },
onBoatSelect : function(component, event, helper) {
var boatId = event.getParam("boatId");
console.log(boatId);
component.set("v.selectedBoatId",boatId);
}
})
finally add those component in your Lighting page Ex :- Friends with Boats
Add boat reviews
Instantiate an AddBoatReview component inside the Add Review tab and display the form. When a user clicks Submit, save the record using Lightning Data Service and fire a BoatReviewAdded event that the BoatDetails parent component listens for so that it can switch the active tab to Reviews. Don’t worry about displaying the reviews yet.
BoatDetails.cmp<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" ><aura:attribute name="selTabId" type="Id"/> <aura:attribute name="boat" type="Boat__c"/> <aura:attribute name="id" type="Id" /> <aura:attribute name="recordError" type="String"/> <aura:handler event="c:BoatSelected" action="{!c.onBoatSelected}" /> <aura:registerEvent name="BoatReviewAdded" type="c:BoatReviewAdded"/> <aura:handler name="BoatReviewAdded" event="c:BoatReviewAdded" action="{!c.onBoatReviewAdded}"/> <force:recordData aura:id="service" layoutType="FULL" recordId="{!v.id}" fields="Id,Name,Description__c,Price__c,Length__c,Contact__r.Name, Contact__r.Email,Contact__r.HomePhone,BoatType__r.Name,Picture__c" targetError="{!v.recordError}" targetFields="{!v.boat}" mode="EDIT" recordUpdated="{!c.onRecordUpdated}" /> <aura:if isTrue="{!not(empty(v.id))}"> <lightning:tabset variant="scoped" selectedTabId="{!v.selTabId}" aura:id="details"> <lightning:tab label="Details" id="details" > <c:BoatDetail boat="{!v.boat}"/> </lightning:tab> <lightning:tab label="Reviews" id="boatreviewtab" > <c:BoatReviews boat="{!v.boat}" aura:id="BRcmp"/> </lightning:tab> <lightning:tab label="Add Review" id="addReview" > <c:AddBoatReview boat="{!v.boat}"/> </lightning:tab> </lightning:tabset> </aura:if> <aura:if isTrue="{!not(empty(v.recordError))}"> <div class="recordError"> <ui:message title="Error" severity="error" closable="true"> {!v.recordError} </ui:message> </div> </aura:if> </aura:component>BoatDetailscontroller.js({ init: function(component, event, helper) { component.set("v.enableFullDetails", $A.get("e.force:navigateToSObject")); }, onFullDetails: function(component, event, helper) { var navEvt = $A.get("e.force:navigateToSObject"); navEvt.setParams({ "recordId": component.get("v.boat.Id") }); navEvt.fire(); }, onBoatSelected : function(component, event, helper) { var boatSelected = event.getParam("boat"); component.set("v.id",boatSelected.Id); var service = component.find("service"); service.reloadRecord() ; }, onRecordUpdated : function(component, event, helper){ //invoke a refresh on the reviews tab, calling public method refresh //BRcmp is the aura:id for the component when invoked in BoatDetails.cmp var boat = component.get("v.boat"); console.log("onRecordUpdated called | boat: " + boat.Id); var BRcmp = component.find("BRcmp"); console.log(BRcmp); var auraMethodResult = BRcmp.refresh(); console.log("auraMethodResult: " + auraMethodResult); }, onBoatReviewAdded : function(component, event, helper) { console.log('Event received'); component.find("details").set("v.selectedTabId", "boatreviewtab"); } })BoatReviews.cmp<aura:component controller="BoatReviews"> <aura:attribute name="boat" type="Boat__c" access="public"/> <aura:attribute name="boatReviews" type="BoatReview__c[]" access="private"/> <aura:handler name="init" action="{!c.doInit}" value="{!this}"/> <aura:handler name="change" value="{!v.boat}" action="{!c.doInit}"/> <aura:method name="refresh" action="{!c.doInit}"> </aura:method> <aura:if isTrue="{!v.boatReviews.length==0}"> <lightning:layoutItem class="slds-align_absolute-center" flexibility="auto"padding="around-small"> <ui:outputText value="No reviews available" /> </lightning:layoutItem> </aura:if> <div class="slds-feed"> <ul class="slds-feed__list"> <ui:scrollerWrapper class="scrollerSize"> <aura:iteration items="{!v.boatReviews}" var="review"> <li class="slds-feed__item slds-scrollable_y"> <article class="slds-post"> <header class="slds-post__header slds-media"> <div class="slds-media__figure"> <a href="javascript:void(0);" class="slds-avatar slds-avatar_circle slds-avatar_large"> <img alt="{!review.CreatedBy.Name}" src="{!review.CreatedBy.SmallPhotoUrl}" title="{!review.CreatedBy.Name}" /> </a> </div> <div class="slds-media__body"> <div class="slds-grid slds-grid_align-spread slds-has-flexi-truncate"> <p><a data-userid="{!review.CreatedBy.Id}" href="javascript:void(0);"title="{!review.CreatedBy.Name}" onclick="{!c.onUserInfoClick}"> {!review.CreatedBy.Name} </a> — {!review.CreatedBy.CompanyName} </p> </div> <p class="slds-text-body_small"> <lightning:formattedDateTime value="{!review.LastModifiedDate}" year="numeric" month="short" day="numeric" hour="2-digit" minute="2-digit" second="2-digit" /> </p> </div> </header> <div class="slds-post__content slds-text-longform"> <p class="slds-text-title_caps">{!review.Name}</p> <p class="slds-text-body_small"><lightning:formattedRichTextvalue="{!review.Comment__c}"/> </p> </div> <footer class="slds-post__footer"> <ul class="slds-post__footer-actions-list slds-list_horizontal"> <li class="slds-col slds-item slds-m-right_medium"> <c:FiveStarRating aura:id="FiveStarRating"value="{!BoatReview.Rating__c}" readonly="true"/> </li> </ul> </footer> </article> </li> </aura:iteration> </ui:scrollerWrapper> </ul> </div> </aura:component>
BoatReviewscontroller.js({ onUserInfoClick : function(component,event,helper){ var userId = event.currentTarget.getAttribute("data-userid"); var navEvt = $A.get("e.force:navigateToSObject"); navEvt.setParams({ "recordId" : userId, }); navEvt.fire() }, doInit : function(component,event,helper){ console.log("BRCjs: doInit"); helper.onInit(component, event); }, refresh : function(component,event,helper){ console.log("refresh called") this.doInit; } })BoarReviewsHelper.js({ onInit : function(component, event){ var boat = component.get("v.boat"); console.log("BRHjs:onInit started: boatId is " + boat.Id); var action = component.get("c.getAll"); action.setParams({"boatId" : boat.Id}); console.log("boatId: " + boat.Id); //add the callback behavior for when the response is received action.setCallback(this,function(response){ var state = response.getState(); if (state === "SUCCESS"){ component.set("v.boatReviews", response.getReturnValue()); console.log("APEX success"); } else { console.log("Failed with state: " + state); } }); //send action off to be executed in APEX $A.enqueueAction(action); }, })FiveStarRating.cmp<aura:component implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction" access="global" ><aura:attribute name="value" type="Integer" default="0" /><aura:attribute name="readonly" type="Boolean" default="false" /><aura:handler name="change" value="{!v.value}" action="{!c.onValueChange}"/><ltng:require styles="{!$Resource.fivestar.rating.css}" /><ltng:require scripts="{!$Resource.fivestar.rating.js}"afterScriptsLoaded="{!c.afterScriptsLoaded}" /><ul aura:id="ratingarea" class="{!v.readonly ? 'readonly c-rating' : 'c-rating'}"></ul></aura:component>FiveStarRatingcontroller.js({afterScriptsLoaded : function(component, event, helper) {var domEl = component.find("ratingarea").getElement();var currentRating = component.get('v.value');var readOnly = component.get('v.readonly');var maxRating = 5;var callback = function(rating) {component.set('v.value',rating);}component.ratingObj = rating(domEl,currentRating,maxRating,callback,readOnly);},onValueChange: function(component,event,helper) {if (component.ratingObj) {var value = component.get('v.value');component.ratingObj.setRating(value,false);}}})BoatReviews.apexpublic with sharing class BoatReviews{@AuraEnabledpublic static List<BoatReview__c> getAll(String boatTypeId){return [SELECT Id, Comment__c,Rating__c,CreatedBy.Id,CreatedBy.Name,CreatedBy.SmallPhotoUrl,CreatedBy.CompanyName,LastModifiedDate,CreatedDate from BoatReview__c where Boat__c =: boatTypeId];}}AddBoatreview.cmp<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" ><aura:attribute name="boatReview" type="BoatReview__c" access="public"/><aura:attribute name="boatReviewRecord" type="Object" access="public"/><aura:attribute name="boat" type="Boat__c"/><aura:attribute name="recordError" type="String" access="private"/><aura:registerEvent name="BoatReviewAdded" type="c:BoatReviewAdded"/><aura:handler name="init" value="{!this}" action="{!c.doInit}"/><force:recordData aura:id="service"targetError="{!v.recordError}"targetRecord="{!v.boatReviewRecord}"targetFields="{!v.boatReview}"fields="Id,Name,Comment__c,Boat__c"recordUpdated="{!c.onRecordUpdated}"/><lightning:layout multipleRows="true"><lightning:layoutItem size="12" padding="around-small"><lightning:input name="title" label="Title" value="{!v.boatReview.Name}"/></lightning:layoutItem><lightning:layoutItem size="12" padding="around-small"><label class="slds-form-element__label" for="input-id-01">Description</label><lightning:inputRichText value="{!v.boatReview.Comment__c}" disabledCategories="FORMAT_FONT"/></lightning:layoutItem><lightning:layoutItem size="12" padding="around-small"><label class="slds-form-element__label" for="input-id-01">Description</label><c:FiveStarRating value="{!v.boatReview.Rating__c}" readonly="false"/></lightning:layoutItem><lightning:layoutItem size="12" class="slds-align--absolute-center"><lightning:button iconName="utility:save" label="Submit" onclick="{!c.onSave}"/></lightning:layoutItem></lightning:layout><aura:if isTrue="{!not(empty(v.recordError))}"><div class="recordError"><ui:message title="Error" severity="error" closable="true">{!v.recordError}</ui:message></div></aura:if></aura:component>AddBoatReviewcontroller.js({doInit : function(component, event, helper) {helper.onInit(component,event);},onSave : function(component, event, helper) {component.set("v.boatReview.Boat__c",component.get("v.boat.Id"));component.find("service").saveRecord(function(saveResult){if(saveResult.state==="SUCCESS" || saveResult.state === "DRAFT"){var resultsToast = $A.get("e.force:showToast");if(resultsToast){resultsToast.setParams({"title": "Saved","message": "Boat Review Created"});resultsToast.fire();}else{alert('Boat Review Created');}}else if (saveResult.state === "ERROR") {var errMsg='';console.log('Problem saving record, error: ' + JSON.stringify(saveResult.error));for (var i = 0; i < saveResult.error.length; i++) {errMsg += saveResult.error[i].message + "\n";}component.set("v.recordError", errMsg);}else{console.log('Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error));}var boatReviewAddedEvnt=component.getEvent("boatReviewAdded");boatReviewAddedEvnt.fire();helper.onInit(component,event,helper);});},onRecordUpdated : function(component, event, helper) {var eventParams = event.getParams();if(eventParams.changeType === "CHANGED") {var changedFields = eventParams.changedFields;var saveResultsToast = $A.get("e.force:showToast");if(saveResultsToast!='undefined'){saveResultsToast.setParams({"title": "Saved","message": "Boat Review Saved"});saveResultsToast.fire();}else{alert('Boat Review Saved');}}}})AddBoatReviewHealper.js({onInit : function(component,event) {component.find("service").getNewRecord("BoatReview__c", // sObject type (entityAPIName)null, // recordTypeIdfalse, // skip cache?$A.getCallback(function() {var rec = component.get("v.boatReview");var error = component.get("v.recordError");var boat=component.get("v.boat");if(error || (rec === null)) {console.log("Error initializing record template: " + error);}else {component.set("v.boatReviewRecord.Boat__c",boat.Id);component.set("v.boatReview.Boat__c",boat.Id);}}));}})BoatTitle.cmp<aura:component ><aura:attribute name="boat" type="Boat__c" /><aura:attribute name="selected" type="boolean" default="false"/><aura:registerEvent name="boatselected" type="c:BoatSelected"/><aura:attribute name="selectedBoatId" type="Id"/><aura:registerEvent name="BoatSelect" type="c:BoatSelect"/><aura:registerEvent type="c:PlotMapMarker" name="PlotMapMarker"/><lightning:button onclick="{!c.onBoatClick}"class="{! v.selected ? 'tile selected' : 'tile' }"><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>BoatTitlecontroller.jc({onBoatClick : function(component, event, helper) {var boatId = event.getSource().get("v.name");console.log('Selected boat Id',boatId);var formSubmit = component.getEvent("BoatSelect");formSubmit.setParams({"boatId" : boatId});formSubmit.fire();console.log('Selectedboad in boattile component!!',component.get("v.boat"));// Fire Additional Apllication event step #6 BoatSelectedvar appEvent = $A.get("e.c:BoatSelected");appEvent.setParams({"boat": component.get("v.boat")});appEvent.fire();var boat = component.get('v.boat');//send geolocation to map.cmp through the PlotMapMarker Application eventvar lat = boat.Geolocation__Latitude__s;var long = boat.Geolocation__Longitude__s;var label = boat.Name;var sObjectId;var plotMapMarkerAppEvent = $A.get("e.c:PlotMapMarker");plotMapMarkerAppEvent.setParams({"lat" : lat,"long" : long,"label" : label,"SObjectId" : boat.Id});plotMapMarkerAppEvent.fire();console.log('lat ',lat);}})Map.cmp<aura:component implements="flexipage:availableForAllPageTypes" access="global" ><aura:attribute name="width" type="String" default="100%" /><aura:attribute name="height" type="String" default="200px" /><aura:attribute name="location" type="SObject"/><aura:attribute name="jsLoaded" type="boolean" default="false"/><aura:handler event="c:PlotMapMarker" action="{!c.onPlotMapMarker}" /><aura:registerEvent type="c:PlotMapMarker" name="PlotMapMarker"/><ltng:require styles="{!$Resource.Leaflet + '/leaflet.css'}"scripts="{!$Resource.Leaflet + '/leaflet-src.js'}"afterScriptsLoaded="{!c.jsLoaded}" /><lightning:card title="Current Boat Location" ><div aura:id="map" style="{!'width: ' + v.width + '; height: ' + v.height + '; border:none;'}"><div style="width:100%; height:100%" class="slds-align_absolute-center">Please make a selection</div></div></lightning:card></aura:component>Mapcontroller.js({jsLoaded: function(component) {console.debug('here map jsLoaded');component.set("v.jsLoaded", true);},onPlotMapMarker: function(component, event, helper) {var id = event.getParam('sObjectId');var latitude = event.getParam('lat');var longitude = event.getParam('long');var label = event.getParam('label');//component.set("v.location", {'latitude' : latitude, 'longitude' : longitude});component.set('v.location', {'lat' : latitude,'long' : longitude});console.log('latitude !!',latitude);}})PlotMapMarker.event<aura:event type="APPLICATION" description="PlotMapMarker test"><aura:attribute name="sObjectId" type="String"/><aura:attribute name="lat" type="String"/><aura:attribute name="long" type="String"/><aura:attribute name="label" type="String"/></aura:event>
For Step2 challenge. I am getting below error:
ReplyDeleteNo EVENT named markup://c:launchNewBoatForm found : [markup://c:BoatSearchForm]: Source
Is there a styling that needs to be created "launchnewboatform".
Can you please help me.
Create the event
DeleteI am getting this error in Step 3:
DeleteChallenge Not yet complete... here's what's wrong:
The getBoats() method isn't working properly. Define getBoats() In the BoatSearchResults Apex controller to accept an optional boatTypeId and return all boats if no boatTypeId is passed, and a filtered list of boats if a boatTypeId is passed.
I am getting this error in Step 3:
ReplyDeleteChallenge Not yet complete... here's what's wrong:
The getBoats() method isn't working properly. Define getBoats() In the BoatSearchResults Apex controller to accept an optional boatTypeId and return all boats if no boatTypeId is passed, and a filtered list of boats if a boatTypeId is passed.
Hi Naren,
ReplyDeleteI am also getting same issue. Did you sort out the problem, please let me know
Krishna.
In Step 4 i am getting error
ReplyDeleteThis page has an error. You might just need to refresh it.
Action failed: c:BoatSearchForm$controller$onFormSubmit [Cannot read property 'setParams' of null]
Failing descriptor: {c:BoatSearchForm$controller$onFormSubmit}
This comment has been removed by the author.
ReplyDeleteCould please add for step 10
ReplyDeleteIn Step 10 :
ReplyDeleteI am facing following error please help me out for that.
Challenge Not yet complete... here's what's wrong:
We couldn't find a design resource in the Map bundle.
I have the same issue, did you solve it yet?
DeleteIs this issue resolved ?
DeletePlease reply the solution for step 10 , i am getting the same error.
cLick on design in MAP lightning component and create design module
Deleteand add Map component in app
Hi Amar,
DeleteThanks for your reply , i follow the same step , but i am getting the different error.
Challenge Not yet complete... here's what's wrong:
The BoatTile component doesn't register the event of type PlotMapMarker with name PlotMapMarker.
Please let me know answer of the above question.
Hello
ReplyDeleteI'm getting the fallowing error in Step 7.
Challenge Not yet complete... here's what's wrong:
The BoatReviewAdded event isn't configured correctly. There is something missing or improperly configured in the BoatReviewAdded.evt file.
So, I created an event like below
still getting please help me.
Please post a solution for this error ASAP. I tried all ways that I know but no use.
DeletePlease help
Change Event Type for boatReviewAdded from "APPLICATION" to "COMPONENT"
DeleteIn Step3, I am getting below error:
ReplyDeleteChallenge Not yet complete... here's what's wrong:
The getBoats() method isn't working properly. Define getBoats() In the BoatSearchResults Apex controller to accept an optional boatTypeId and return all boats if no boatTypeId is passed, and a filtered list of boats if a boatTypeId is passed.
Please someone tell what I should do.
Thanks - Naresh
Could please add for step 10
ReplyDeleteStatic resource in fivestar has be below as it is zip file
ReplyDelete<ltng:require scripts="{!$Resource.fivestar + '/rating.js'}"
where is BSRcmp?
ReplyDeleteLightning Component Framework Specialist - Step 4-Super badge
ReplyDelete8. BoatSearchController.js
({
onFormSubmit : function(component, event, helper){
console.log("event received by BoatSearchController.js");
var formData = event.getParam("formData");
var boatTypeId = formData.boatTypeId;
var BSRcmp = component.find("BSRcmp");
var auraMethodResult = BSRcmp.search(boatTypeId);
console.log("auraMethodResult: " + auraMethodResult);
}
})
from where "BSRcmp" is coming: i am confused
Failed to save BoatDetails.cmp: A aura:handler that specifies an event="" attribute must handle an application event. Either change the aura:event to have type="APPLICATION" or alternately change the aura:handler to specify a name="" attribute.: Source
ReplyDeleteI am getting this error how to resolve it help me frnds.
I´m having this problem in Challeng n°5
ReplyDeleteWe couldn't find the appropriate CSS for the BoatTile component. Be sure to include it in its own file, rather than inline.
Use This--
Delete.THIS.selected {
border: 3px solid rgb(0, 112, 210);
}
it should resolve your problem
Thank you for this tip!!
DeleteI am getting below error in step 8 :
ReplyDeleteThe BoatReviews Apex controller's getAll() function doesn't return the right list of the fields from the BoatReview custom object.
It is fine, nonetheless evaluate the information and facts around this correct. Mercruiser Alpha One outdrive replacement
ReplyDeleteHi Guys, I am facing the below issue please help me
ReplyDeletehallenge Not yet complete... here's what's wrong:
Map.design doesn't enable a business user to set the width and height of the map.
Add the below lines to the Map.svg file
DeleteHi which below line.Can u plz tell me.Even I am facing the same issue.
DeleteStep 10: add below code in boattile.cmp:
ReplyDeleteaura:registerEvent type="c:PlotMapMarker" name="PlotMapMarker"/
DeleteGuys anyone got this weird error(mentioned below) for Step 10? I have put the component on the page using app builder and I can see the component on the page and yet for some reason it keeps showing me this error. I tried everything since 2 days but nothing is helping. Anyone familiar with this?
ReplyDeleteChallenge Not yet complete... here's what's wrong:
The Map component isn't available in Lightning App Builder.
Hi Manju is it solved ? I already spent 1 day and still not working .
ReplyDeletein challenge 6 facing error reply soon
ReplyDeleteChallenge Not yet complete... here's what's wrong:
The BoatDetails component's tabs shouldn't display if the component’s boat attribute is undefined. Either use the empty function or check for undefined.
Deleteuse this it will work.
Hi Team,
ReplyDeleteI got struck in 10 th module.Getting below error.
Map.design doesn't enable a business user to set the width and height of the map.
Can anyone help me.
Thankyou.
Getting below error when checking for Challenge 10
ReplyDeleteMapController.js must have an event handler named onPlotMapMarker that uses the latitude and longitude that were passed through the event to update the boat’s location. Make sure the component was created according to the requirements, including the zoom level, map markers, titles, and labels.
Any help is appreciated and Thankyou
Challenge Not yet complete... here's what's wrong:
ReplyDeleteThe getBoats() method isn't working properly. Define getBoats() In the BoatSearchResults Apex controller to accept an optional boatTypeId and return all boats if no boatTypeId is passed, and a filtered list of boats if a boatTypeId is passed.