SuperBadge: Lightning Component Framework Specialist
Step2:
FriendswithBoats.app:
<aura:application extends="force:slds">
<lightning:layout class="flexipageHeader slds-page-header uiBlock oneAnchorHeader">
<lightning:layoutItem padding="horizontal-small">
<lightning:icon iconName="custom:custom54" />
</lightning:layoutItem>
<h1>
Friends with Boats
</h1>
</lightning:layout>
<lightning:layout>
<lightning:layoutItem padding="around-small" size="6">
<lightning:card title="Find a Boat" class="slds-m-top_10px" >
<c:BoatSearchForm />
</lightning:card>
</lightning:layoutItem>
</lightning:layout>
<lightning:layout>
<lightning:card title="Matching Boats" class="slds-m-top_10px" >
<c:BoatSearchResults />
</lightning:card>
</lightning:layout>
</aura:application>
BoatSearchForm
<aura:component description="BoatSearchForm"
controller="BoatSearchFormController" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction" access="global" >
<!-- dynamically load the BoatTypes -->
<aura:attribute name="BoatTypes" type="BoatType__c[]" />
<aura:attribute name="selectedType" type="String" default="foo"/>
<aura:attribute name="renderNew" type="Boolean" default="true"/>
<article class="slds-card slds-m-bottom_large">
<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}" text="{!boatType.Name}"/>
</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}"/>
<!--
The form’s controller checks whether the event.force:createRecord event
is supported by a standalone app and either shows or hides the New button
according to best practices.
-->
<aura:if isTrue="{!v.renderNew}">
<lightning:button class="slds-button" variant="neutral" label="New" onclick="{!c.newBoat}"/>
</aura:if>
</div>
</lightning:layoutItem>
</lightning:layout>
</div>
</div>
</article>
</aura:component>
BoatSearchForm Controller
({
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
})
BoatSearchForm Helper
({
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);
},
renderNewButton: function (component) {
var action = component.get('c.getUITheme');
action.setCallback(this, function (response) {
if (response.getState() === 'SUCCESS') {
if (response.getReturnValue() == 'Theme4d' && $A.get('e.force:createRecord')) {
component.set('v.showNewButton', true);
}
}
});
$A.enqueueAction(action);
},
})
BoatSearch
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction" access="global" >
<lightning:card title="Find a Boat" class="slds-m-top_10px" >
<c:BoatSearchForm />
</lightning:card>
<lightning:card title="Matching Boats" >
<c:BoatSearchResults aura:id="BSRcmp"/>
</lightning:card>
</aura:component>
BoatSearchController
({
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);
}
})
Step 3:
BoatTile Component
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name="boat" type="Boat__c" />
<lightning:button class="tile">
<div class="innertile" style="{!'background-image:' + 'url(' + v.boat.Picture__c+')'}">
<div class="lower-third">
<h1 class="slds-truncate">{!v.boat.Contact__r.Name}</h1>
</div>
</div>
</lightning:button>
</aura:component>
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;
}
Note: However, the other two classes are wrapped in .tile. It should be
.THIS.tile .innertile .THIS.tile .lower-third
OR
.THIS .innertile .THIS .lower-third
BoatSearchResults Component
<aura:component controller="BoatSearchResults"
implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction" access="global" >
<aura:attribute name="boats" type="Boat__c[]" />
<aura:attribute name="boatTypeId" type="String" description="" />
<aura:handler name="init" value="{!this}" action="{!c.doSearch}" />
<lightning:layout multipleRows="true">
<aura:if isTrue="{!v.boats.length > 0}">
<aura:iteration items="{!v.boats}" var="bot">
<lightning:layoutItem flexibility="auto" padding="around-small">
<c:BoatTile boat="{!bot}" />
</lightning:layoutItem>
</aura:iteration>
<aura:set attribute="else">
<div class="slds-align_absolute-center">
No boats found
</div>
</aura:set>
</aura:if>
</lightning:layout>
</aura:component>
BoatSearchResults Controller
({
doSearch: function(component, event, helper) {
helper.onSearch(component);
}
})
BoatSearchReults Helper
({
onSearch : function(component) {
var action = component.get("c.getBoats");
//debugger;
action.setParam({"boatTypeId":""});
action.setCallback(this, function(response){
//debugger;
var status = response.getState();
if(status === "SUCCESS") {
if(! $A.util.isEmpty(response.getReturnValue())){
component.set("v.boats",response.getReturnValue());
}
}
});
$A.enqueueAction(action);
}
})
BoatSearchResults Apex Class
public class BoatSearchResults{
@AuraEnabled
public static list<Boat__c> getBoats(string boatTypeId){
if(string.IsBlank(boatTypeId)){
return [SELECT Id, Name,Picture__c,Contact__c,Contact__r.Name from Boat__c];
}
else{
return [SELECT Id, Name,Picture__c,Contact__c,Contact__r.Name from Boat__c where BoatType__c =: boatTypeId];
}
}
}
Step 4:
Create an event. Developer Console--->File---> New ---> Lightning Event.
Event name is formsubmit.
formsubmit Event:
<aura:event type="COMPONENT" description="Boat Event" >
<aura:attribute name="formData" type="Object[]"/>
</aura:event>
BoatSearchForm Component:
Add this line and call function on button.
<aura:registerEvent name="formSubmitEvent" type="c:formsubmit"/>
<lightning:button class="slds-button" variant="brand" label="Search" onclick="{!c.onFormSubmit}"/>
BoatSearchForm Controller
Add this function.
onFormSubmit : function(component, event, helper){
console.log("event received by BoatSearchController.js");
var boatTypeId = component.get("v.selectedType");
console.log("Search button pressed " + boatTypeId);
var formSubmit = component.getEvent("formSubmitEvent");
//var formsubmit = component.getEvent("formsubmit");
console.log("formSubmit Var" + formSubmit);
formSubmit.setParams({"formData":{"boatTypeId" : boatTypeId}});
formSubmit.fire();
},
BoatSearch Component:
<aura:handler name="formsubmit" event="c:formsubmit" action="{!c.onFormSubmit}" phase="capture"/>
BoatSearch Controller
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);
},
BoatSearchResult Component:
<aura:method name="search" description="accepts boatTypeId and executes search that refreshes the boats attribute">
<aura:attribute name="boatTypeId" type="Id"/>
</aura:method>
BoatSearchResult Helper
({
onSearch : function(component) {
var boatTypeID = component.get('v.boatTypeId');
var action = component.get("c.getBoats");
//debugger;
//action.setParam({"boatTypeId":""});
action.setParams({"boatTypeId" : boatTypeID});
console.log("***boatType: " +boatTypeID);
action.setCallback(this, function(response){
//debugger;
var status = response.getState();
if(status === "SUCCESS") {
if(! $A.util.isEmpty(response.getReturnValue())){
component.set("v.boats",response.getReturnValue());
}
}
else{
var errors = response.getError();
if (errors[0] && errors[0].message){
console.log("***Error message: " + errors[0].message);
}
}
});
$A.enqueueAction(action);
}
})
Step 5:
Create BoatSelect event.
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>
Boat Tile Component
<aura:component >
<aura:attribute name="boat" type="Boat__c" /> <aura:attribute name="selected" type="boolean" default="false"/> <aura:registerEvent name="BoatSelect" type="c:BoatSelect"/> <aura:registerEvent name="BoatSelected" type="c:BoatSelected"/> <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>
BoatTile Controller
onBoatClick : function(component, event, helper) { var cmpEvent = component.getEvent("BoatSelect"); //var boatId = component.get("v.boat.Id"); var boatId = event.getSource().get("v.name"); cmpEvent.setParams({ "boatId" : boatId }); cmpEvent.fire(); },
BoatTile Css
.THIS.selected { border: 3px solid rgb(0, 112, 210); }
BoatSearchResult Component
Add below code in component.
<aura:handler name="BoatSelect" event="c:BoatSelect" action="{!c.onBoatSelect}"/>
Replace aura iteration code with below one.
<c:BoatTile boat="{!boat}" selected="{!boat.Id == v.selectedBoatId ? 'true' : 'false' }"/>
BoatSearchResult Controller
Add onBoatSelect Function in controller.
onBoatSelect : function(component, event, helper) { var boatId = event.getParam("boatId"); console.log(boatId); component.set("v.selectedBoatId",boatId); },
Step 6:
Create BoatBoatSelected event.
BoatBoatSelected.evt
<aura:event type="APPLICATION" description="Boat Event"> <aura:attribute name="boat" type="Boat__c" /> </aura:event>
BoatDetail Component
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" 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>
BoatDetail Controller
({
onFullDetails: function(component, event, helper) {
var navEvt = $A.get("e.force:navigateToSObject");
navEvt.setParams({
"recordId": component.get("v.boat.Id")
});
navEvt.fire();
},
})
BoatDetail CSS
.THIS.boatproperty {
margin-bottom: 3px;
}
.THIS .label {
font-weight: bold;
display: block;
}
.THIS.imageview {
background-repeat: no-repeat;
background-size: contain;
height: 200px;
margin: 2px;
}
.THIS {
}
BoatDetails Component
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name="id" type="Id" access="public"/>
<aura:attribute name="boat" type="Boat__c" access="public"/>
<aura:handler event="c:BoatSelected" action="{!c.onBoatSelected}"/>
<force:recordData aura:id="service"
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"
targetFields="{!v.boat}"
recordUpdated="{!c.onRecordUpdated}"/>
<lightning:tabset >
<lightning:tab label="Details">
<aura:if isTrue="{!not(empty(v.boat))}">
<c:BoatDetail boat="{!v.boat}"/>
</aura:if>
</lightning:tab>
<lightning:tab label="Reviews">
</lightning:tab>
<lightning:tab label="Add Review">
</lightning:tab>
</lightning:tabset>
</aura:component>
BoatDetails Controller
({
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) {
},
})
BoatTile Component
Add this line in Boat tile component
<aura:registerEvent name="boatselected" type="c:BoatSelected"/>
BoatTile Controller
({
onBoatClick : function(component, event, helper) {
var cmpEvent = component.getEvent("BoatSelect");
//var boatId = component.get("v.boat.Id");
var boatId = event.getSource().get("v.name");
cmpEvent.setParams({
"boatId" : boatId
});
cmpEvent.fire();
//New event
var BoatSelectedEvt = $A.get('e.c:BoatSelected');
BoatSelectedEvt.setParams({
"boat" : boat
});
BoatSelectedEvt.fire();
},
})
Add BoatDetails component in your lightning page.
Step 7
Create an event name BoatReviewAdded
BoatReviewAdded Event
<aura:event type="COMPONENT" description="Boat Review Add Event"> <aura:attribute name="BoatReviewAdded" type="Boat__c"/> </aura:event>
AddBoatReview Component
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name="boat" type="Boat__c" />
<aura:attribute name="boatReview" type="BoatReview__c" access="public"/>
<aura:attribute name="boatReviewRecord" type="Object" access="public"/>
<aura:attribute name="id" type="Id" />
<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"
recordId="{!v.id}"
targetRecord="{!v.boatReviewRecord}"
fields="Id,Name,Comment__c,Boat__c"
targetFields="{!v.boatReview}"
targetError="{!v.recordError}"
recordUpdated="{!c.onRecordUpdated}"/>
<lightning:layout multipleRows="true">
<lightning:layoutItem padding="horizontal-medium" size="12">
<lightning:input name="title" label="Title" value="{!v.boatReview.Name}"/>
</lightning:layoutItem>
<lightning:layoutItem padding="horizontal-medium" size="12">
<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 padding="horizontal-medium" size="12">
<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>
AddBoatReview Controller
({
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 cmpEvent = component.getEvent("BoatReviewAdded");
cmpEvent.fire();
var resultsToast = $A.get("e.force:showToast");
if (resultsToast) {
resultsToast.setParams({
"title": "Submitted",
"message": "The review was saved."
});
resultsToast.fire();
}
else {
alert('The review was saved.');
}
}
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);
}
var boatReviewAddedEvnt=component.getEvent("boatReviewAdded");
boatReviewAddedEvnt.fire();
helper.onInit(component,event,helper);
});
},
doInit: function(component, event, helper) {
helper.onInit(component,event);
},
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');
}
}
},
})
AddBoatReview Helper
({
onInit : function(component,event) {
component.find("service").getNewRecord(
"BoatReview__c", null,false,
$A.getCallback(function() {
var rec = component.get("v.boatReview");
var error = component.get("v.recordError");
console.log('hey', JSON.stringify(rec));
if (error || (rec === null)) {
console.log("Error initializing record template: " + error);
} else{
rec.Boat__c = component.get("v.boat").Id;
//component.set("v.boatReview", rec);
component.set("v.boatReview.Boat__c",component.get("v.boat").Id);
console.log("Record template initialized: " + rec.sobjectType);
}
})
);
}
})
BoatDetails Component
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name="id" type="Id" />
<aura:attribute name="boat" type="Boat__c" />
<aura:attribute name="selectedTabId" type="string" />
<aura:registerEvent name="BoatReviewAdded" type="c:BoatReviewAdded"/>
<aura:handler event="c:BoatSelected" action="{!c.onBoatSelected}"/>
<aura:handler name="BoatReviewAdded" event="c:BoatReviewAdded" action="{!c.onBoatReviewAdded}" />
<force:recordData aura:id="service"
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"
targetFields="{!v.boat}"
recordUpdated="{!c.onRecordUpdated}"/>
<lightning:tabset variant="scoped" selectedTabId="{!v.selectedTabId}" aura:id="tabsDetail">
<lightning:tab label="Details" id="details">
<aura:if isTrue="{!not(empty(v.boat))}">
<c:BoatDetail boat="{!v.boat}"/>
</aura:if>
</lightning:tab>
<lightning:tab label="Reviews" id="boatReviewTab">
</lightning:tab>
<lightning:tab label="Add Review" id="addReview">
<c:AddBoatReview boat="{!v.boat}"/>
</lightning:tab>
</lightning:tabset>
<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>
BoatDetails Controller
({
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 started");
component.find("tabsDetail").set("v.selectedTabId", "boatReviewTab");
//component.set("v.selTabId", "boatReviewTab");
},
})
Step 8:
BoatReviews Component
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId"
controller="BoatReviews" access="global" >
<aura:attribute name="boat" type="Boat__c" />
<aura:attribute name ="boatReviews" type= "BoatReview__c[]" access="private"/>
<aura:handler name="change" value="{!v.boat}" action="{!c.refresh}"/>
<!-- set up the aura:method for refresh -->
<aura:method name="refresh" action="{!c.doInit}" access="public"
description="BoatDetailsController.js invokes refresh whenever boat is updated">
</aura:method>
<!--<aura:handler name="init" value="{!this}" action="{!c.doInit}"/> -->
<ui:scrollerWrapper class="scrollerSize">
<!--Scrollable content here -->
<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">
<aura:iteration items="{!v.boatReviews}" var="boatReview">
<li class="slds-feed__item">
<div class="slds-media__body">
<div class="slds-grid slds-has-flexi-truncate">
<a href="javascript:void(0)" onclick="{!c.onUserInfoClick}"
data-userid="{!boatReview.CreatedBy.Id}">
{!boatReview.CreatedBy.Name}
</a>
— {!boatReview.CreatedBy.CompanyName}
</div>
<p><lightning:formattedDateTime value="{!boatReview.CreatedDate}"
year="numeric" month="short" day="numeric"
hour="2-digit" minute="2-digit" hour12="true"/></p>
</div>
</li>
</aura:iteration>
</ul>
</div>
</ui:scrollerWrapper>
</aura:component>
BoatReviews Controller
({
doInit: function(component, event, helper) {
helper.onInit(component,event);
},
refresh : function(component,event,helper){
console.log("refresh called")
this.doInit;
},
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()
},
})
BoatReviews Helper
({
onInit : function(component,event) {
var boat = component.get("v.boat");
var action = component.get("c.getAll");
//action.setParams({"boatId " : boatId });
action.setParams({"boatId" : boat.Id});
console.log("***boatId: " +boatId);
action.setCallback(this, function(response){
var status = response.getState();
if(status === "SUCCESS") {
//if(! $A.util.isEmpty(response.getReturnValue())){
component.set("v.boatReviews", response.getReturnValue());
//component.set("v.boats",response.getReturnValue());
// }
}
else{
var errors = response.getError();
if (errors[0] && errors[0].message){
console.log("***Error message: " + errors[0].message);
}
}
});
$A.enqueueAction(action);
}
})
BoatDetails Component
Add these lines in code
<lightning:tab label="Reviews" id="boatReviewTab">
<c:BoatReviews boat="{!v.boat}"/>
</lightning:tab>
BoatDetails Controller
Add these lines in onBoatReviewAdded function
var BoatReviews = component.find("boatReviewTab");
//BRcmp is the aura:id for the component when invoked in BoatDetails.cmp
BoatReviews.refresh();
public class BoatReviews{
@AuraEnabled
public static list<BoatReview__c> getAll(Id boatId ){
return [SELECT Id, Name,Comment__c,Rating__c,LastModifiedDate,CreatedDate,CreatedBy.Name,
CreatedBy.SmallPhotoUrl, CreatedBy.CompanyName from BoatReview__c];
}
}
BoatReviews Apex Class
public class BoatReviews{
@AuraEnabled
public static list<BoatReview__c> getAll(Id boatId ){
return [SELECT Id, Name,Comment__c,Rating__c,LastModifiedDate,CreatedDate,CreatedBy.Name,
CreatedBy.SmallPhotoUrl, CreatedBy.CompanyName from BoatReview__c];
}
}
Step 9
FiveStarRating Component
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global" >
<aura:attribute name='value' type='Integer' default='0' />
<aura:attribute name='readonly' type='boolean' default='false' />
<ltng:require styles="{!$Resource.fivestar + '/rating.css'}" scripts="{!$Resource.fivestar + '/rating.js'}"
afterScriptsLoaded="{!c.afterScriptsLoaded}" />
<aura:handler name='change' value="{!v.value}" action="{!c.onValueChange}" />
<ul aura:id='ratingarea' class="{!v.readonly ? 'readonly c-rating' : 'c-rating' }" />
</aura:component>
FiveStarRating Controller
({
afterScriptsLoaded : function(component, event, helper) {
//var domEl = document.getElementsByTagName("UL")[0];
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);
}
},
})
AddBoatReview Component
<lightning:layoutItem padding="horizontal-medium" size="12">
<label class="slds-form-element__label" for="input-id-01">Description</label>
<c:FiveStarRating value="{!v.boatReview.Rating__c}" readonly="false"/>
</lightning:layoutItem>
BoatReview Component
<c:FiveStarRating aura:id="FiveStarRating" value="{!BoatReview.Rating__c}" readonly="true"/>
Step 10
PlotMapMarker Event
<aura:event type="APPLICATION" description="PlotMapMarker">
<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>
Map Component
<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:attribute access="private" name="leafletMap" type="Object" />
<aura:handler event="c:PlotMapMarker" action="{!c.onPlotMapMarker}"/>
<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}">
<div style="width:100%; height:100%" class="slds-align_absolute-center">Please make a selection</div>
</div>
</lightning:card>
</aura:component>
Map Controller
({
jsLoaded: function(component) {
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});
var leafletMap = helper.getLeafletMap(component, latitude, longitude);
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(leafletMap);
L.marker([latitude, longitude]).addTo(leafletMap)
.bindPopup(label)
.openPopup();
}
})
Map Helper
({
getLeafletMap : function(component, latitude, longitude) {
var leafletMap = component.get('v.leafletMap');
if (!leafletMap) {
var mapContainer = component.find('map').getElement();
leafletMap = L.map(mapContainer, {zoomControl: false, tap: false})
.setView([latitude, longitude], 13);
component.set('v.leafletMap', leafletMap);
component.set("v.location, "{'latitude' : latitude, 'longitude' : longitude});
}
return leafletMap;
}
})
Map Design
<design:component label="Map">
<design:attribute name="width" label="Width" description="The width of the map as a percentage (100%) or pixels (100px)" />
<design:attribute name="height" label="Height" description="The height of the map as a percentage (100%) or pixels (100px)" />
</design:component>
BoatTile Component
Add this line
<aura:registerEvent name="PlotMapMarker" type="c:PlotMapMarker"/>
BoatTile Controller
Add these lines in onBoatClick function
var boat = component.get('v.boat');
var 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);
Happy Coding!!!
No comments:
Post a Comment