Friday, 26 January 2018

SuperBadge: Lightning Component Framework Specialist

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>
                                            &nbsp; &mdash; &nbsp; {!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();

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: '&copy; <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