salesforce lightning零基礎學習(十一) Aura框架下APP構造實現

前面的一些lightning文章講述了aura的基礎知識,aura封裝的經常使用js以及aura下的事件處理。本篇經過官方的一個superbadge來實現一個single APP的實現。javascript

superbadge的網址以下:https://trailhead.salesforce.com/en/content/learn/superbadges/superbadge_lcfcss

經過步驟安裝相關的app exchange便可安裝相關的表結構以及初始化數據,詳細能夠看這個superbadge的細節描述。安裝之後主要有3個表,Boat Type、Boat、BoatReview。相關表結構關係以下:html

 Boat Type在這個demo中用來存儲 船的類型,固然這個數據也能夠維護在custom setting中;java

 Boat在這個demo中用來存儲船的詳情信息;node

 Boat Review在這個demo中用來存儲船的一些評價信息。git

 接下來講一下想要實現的UI,這個superbadge主要想實現如下的功能:架構

1. 頭部展現這個APP 的頭部信息,包括圖標標題等;app

2. 搜索區域展現Boat Type數據,選中某個Boat Type點擊Search後在區域3展現數據;dom

3. 展現2步搜索出來的數據,點擊某個船的信息會在右面區域展現詳細信息以及地圖信息;ide

4. 展現一個tab,分別對應詳情,評價以及添加評價;

5. 根據不一樣的tab展現不一樣的子元素信息;

6. 展現3步選中的船的圖標的地理信息。

說完須要實現的功能再說一下實現所需的元素組件,官方在包中已經封裝好了實現這些功能對應的組件元素的名稱,名稱的結構以下所示:

FriendsWithBoats: 一個single APP, 包含了四部分組件,分別對應 BoatHeader 、 BoatSearch 、 BoatDetails 以及 Map;

BoatHeader:上圖中1部份內容,用於展現logo和標題;

BoatSearch:上圖中的2,3部份內容,包含兩個子組件,分別對應 BoatSearchForm、BoatSearchResults;

BoatDetails: 上圖中的4,5部份內容,包含3個子組件,分別對應 BoatDetail、BoatReviews、AddBoatReview;

Map:上圖中的6部份內容;

BoatSearchForm:上圖中的2部分,主要功能爲展現船的類型,而且根據類型進行搜索;

BoatSearchResults:上圖中的3部分,用來展現搜索出來的列表。包含一個子組件,名字爲BoatTile;

BoatDetail:對應4中切換到Details部分下的5部份內容;

BoatReviews:對應4中切換到Reviews部分下的5部份內容;

AddBoatReview:對應4中切換到Add Review部分下的5部份內容;

BoatTile:上圖中的3部分搜索出來列表的每一個子單元的內容展現;

FiveStarRating:AddBoatReview中會有對當前船進行評價,此元素標籤用於展現5星評價組件。

說完這些用到的component之外再說一下實現這些功能須要用到哪些事件。咱們以前在事件階段也說過,事件分紅兩種,COMPONENT/APPLICATION。若是兩種均可以實現功能的狀況下,官方推薦使用COMPONENT類型的。COMPONENT分紅bubble以及capture兩種類型,不一樣的傳播方式會執行不一樣的順序,詳情能夠參看之前的事件階段的博客。這個demo中,由於當咱們在matching boats區域選中某個子單元狀況下,信息要顯示在右側的區域詳情等地方。經過上面的bom圖能夠看到他們再也不同一個父子節點中,COMPONENT類型的event只能處理父子關係,這種兄弟關係或者類兄弟關係只能經過APPLICATION的event經過廣播訂閱機制去實現。下面說如下demo中設計到的幾個主要的事件階段:

 BoatSelect:用於當子單元選中之後的選中效果展現,邊框加樣式等操做(COMPONENT類型);

 BoatSelected:用於當子單元選中之後,將信息傳遞至BoatDetail中(APPLICATION類型);

plotMapMarker:用於當子單元選中之後,將選中的經緯度等信息傳到Map組件中(APPLICATION類型);

以上幾個事件用於 BoatTile中註冊事件。

formsubmit:用於當點擊search按鈕後,將表單提交而且對數據進行處理(COMPONENT類型);

以上事件用於BoatSearchForm中註冊事件。

BoatReviewAdded:用於當添加一條船的評論信息後,切換到BoatReview的tab而且刷新tab裏面的內容(COMPONENT類型)。

以上事件用於AddBoatReview中註冊事件。

這個APP中註冊的事件整理完之後整理一下這個執行的事件階段以及相關controller和component的實現。

事件的傳播順序爲 capture -> target -> bubble,因此上面的COMPONENT類型的事件在組件中的執行順序應該以下:

FriendsWithBoats -> BoatSearch -> BoatSearchForm -> BoatSearch -> FriendsWithBoats

FriendsWithBoats -> BoatSearch -> BoatSearchResults -> BoatTile -> BoatSearchResults -> BoatSearch -> FriendsWithBoats

FriendsWithBoats -> BoatDetails -> AddBoatReview -> BoatDetails -> FriendsWithBoats

相關Event的聲明以下:

BoatSelect.evt

1 <aura:event type="COMPONENT" description="Boat Event">
2     <aura:attribute name="boatId" type="String"/>
3 </aura:event>
BoatSelect.evt

BoatSelected.evt

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

plotMapMarker.evt

1 <aura:event type="APPLICATION" description="Event template" >
2     <aura:attribute name="sObjectId"  type="String"  />
3     <aura:attribute name="lat"  type="String"  />
4     <aura:attribute name="long"  type="String"  />
5     <aura:attribute name="label"  type="String"  />
6 </aura:event>
plotMapMarker.evt

FormSubmit.evt

1 <aura:event type="COMPONENT" description="Event template" > 
2     <aura:attribute name="formData" type="object"/>
3 </aura:event>
FormSubmit.evt

BoatReviewAdded.evt

1 <aura:event type="COMPONENT" description="Event template" />
BoatReviewAdded

 接下來按照上圖中的DOM結構從下往上構建代碼。

 BoatTile.cmp:註冊了三個事件,當點擊的時候會觸發三個事件從而根據相關的傳播路徑去執行相關的handler

 1 <aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
 2  <aura:attribute name="boat" type="Boat__c" />
 3  <aura:registerEvent name="BoatSelect" type="c:BoatSelect"/>
 4  <aura:registerEvent name="BoatSelected" type="c:BoatSelected" />
 5  <aura:registerEvent name="plotMapMarker" type="c:PlotMapMarker" />
 6  <aura:attribute name='selected' type='Boolean' default='false'/> 
 7  <lightning:button class="{!v.selected ? 'tile selected' : 'tile'}"  onclick="{!c.onBoatClick}">
 8         <div style="{!'background-image:url(\'' + v.boat.Picture__c + '\'); '}" class="innertile">
 9           <div class="lower-third">
10            <h1 class="slds-truncate">{!v.boat.Contact__r.Name}</h1>
11           </div>
12         </div>
13     </lightning:button>
14  </aura:component>
BoatTile.cmp

BoatTileController.js:須要注意的是,獲取COMPONENT/APPLICATION兩種類型的事件的方式不同。針對COMPONENT類型的事件,須要使用component.getEvent('registerEventName')方式獲取Event實例;針對APPLICATION類型的事件,須要使用$A.get("e.namespace:registerEventName"),這裏默認的namespace爲c,因此這個裏面的獲取方式爲:$A.get("e.c:BoatSelected");

 1 ({
 2     onBoatClick : function(component, event, helper) {
 3          var myEvent = component.getEvent("BoatSelect");
 4         var boat=component.get("v.boat");
 5         myEvent.setParams({"boatId": boat.Id});
 6         myEvent.fire();   
 7         
 8         var appEvent = $A.get("e.c:BoatSelected");          
 9         appEvent.setParams({
10             "boat": boat
11         });
12         appEvent.fire();   
13 
14         var plotEvent = $A.get("e.c:PlotMapMarker");        
15         plotEvent.setParams({
16             "lat": boat.Geolocation__Latitude__s,
17             "sObjectId": boat.Id,
18             "long": boat.Geolocation__Longitude__s,
19             "label":boat.Name
20         });
21         plotEvent.fire(); 
22     }  
23 })
BoatTileController.js

此元素組件實現了當點擊了搜索出來的列表的某個子單元之後,便會觸發三個事件,從而會根據綁定這些事件的元素組件按照事件傳播方式進行分別執行。

BoatTile.css

 1 .THIS.tile {
 2    position:relative;
 3    display: inline-block;
 4    background-size: cover;
 5    background-position: center;
 6    background-repeat: no-repeat;
 7 
 8    height: 220px;
 9    padding: 1px !important;
10    
11 }
12 .THIS.selected {
13 
14    border:3px solid rgb(0, 112, 210);
15 }
16 
17 .THIS .innertile {
18    background-size: cover;
19    background-position: center;
20    background-repeat: no-repeat;
21    width: 220px;
22    height: 100%;
23 }
24 
25 .THIS .lower-third {
26    position: absolute;
27    bottom: 0;
28    left: 0;
29    right: 0;
30    color: #FFFFFF;
31    background-color: rgba(0, 0, 0, .4);
32    padding: 6px 8px;
33 }
BoatTile.css

BoatTile.svg

1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 <svg width="120px" height="120px" viewBox="0 0 120 120" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
3     <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
4         <path d="M120,108 C120,114.6 114.6,120 108,120 L12,120 C5.4,120 0,114.6 0,108 L0,12 C0,5.4 5.4,0 12,0 L108,0 C114.6,0 120,5.4 120,12 L120,108 L120,108 Z" id="Shape" fill="#2A739E"/>
5         <path d="M77.7383308,20 L61.1640113,20 L44.7300055,63.2000173 L56.0543288,63.2000173 L40,99.623291 L72.7458388,54.5871812 L60.907727,54.5871812 L77.7383308,20 Z" id="Path-1" fill="#FFFFFF"/>
6     </g>
7 </svg>
BoatTile.svg

 BoatSearchResults.cmp:用於顯示搜索出來的列表以及增長了BoatTile事件中的handler,當BoatTile中的BoatSelect事件觸發之後,會執行其對應的controller.js中的onBoatSelect方法,將selectedBoatId賦值,由於aura架構的變量都是雙向綁定,會同時做用到子組件中從而實現選中後的樣式變化。

這裏面使用了一個組件名字叫作aura:method,這個用於定義一個component的API的方法,容許你直接在controller.js中直接調用你的相關的方法,一般用於在父組件中直接調用子組件的某個方法。本篇demo中會在BoatSearchController.js中調用這個方法。

 1 <aura:component controller="BoatSearchResults" implements="force:appHostable,flexipage:availableForAllPageTypes" access="global">
 2     
 3     <aura:attribute name="boats" type="Boat__c[]" />
 4    <!-- set up the aura:method for search -->
 5     <aura:attribute name="boatTypeId1" type="String"/>
 6     <aura:method name="search" access="global" action="{!c.search}" >
 7         <aura:attribute name="boatTypeId" type="String"/>
 8      </aura:method>
 9     <aura:handler name="BoatSelect" event="c:BoatSelect" action="{!c.onBoatSelect}"/>
10     <aura:attribute name="selectedBoatId" type="String" default="null"/>
11     
12     <lightning:layout multipleRows="true" horizontalAlign="center">
13         <aura:iteration items="{!v.boats}" var="boat">
14             <lightning:layoutItem flexibility="grow"  class="slds-m-right_small" >   
15                 <c:BoatTile boat="{!boat}" selected="{!boat.Id == v.selectedBoatId ? true : false}"/>
16             </lightning:layoutItem>
17         </aura:iteration>
18                
19         <aura:if isTrue="{!v.boats.length==0}">
20             <lightning:layoutItem class="slds-align_absolute-center" flexibility="auto" padding="around-small">   
21                 <ui:outputText value="No boats found" />
22             </lightning:layoutItem>
23         </aura:if>
24 
25     </lightning:layout>
26 </aura:component>
BoatSearchResults.cmp

BoatSearchResultController.js:聲明瞭兩個方法,一個是用於父組件調用查詢的方法,另一個是當事件觸發後執行的handler。

 1 ({
 2     doInit: function(component, event, helper) {
 3     },
 4     search: function(component, event, helper){
 5         var params = event.getParam('arguments');
 6         component.set("v.boatTypeId1", params.boatTypeId);
 7         helper.onSearch(component,event);
 8         return "search complete.";
 9     },
10     onBoatSelect: function(component, event, helper){
11         var boatId = event.getParam("boatId");
12         component.set("v.selectedBoatId", boatId);
13        
14     }
15 })
BoatSearchResultController.js

BoatSearchResultHelper.js

 1 ({
 2     onSearch : function(component) {
 3          var currentBoatType = component.get("v.boatTypeId1")
 4         var action = component.get("c.getBoats");
 5         if(currentBoatType == 'All Types'){
 6             currentBoatType = '';
 7         }
 8          var action = component.get("c.getBoats");
 9         action.setParams({
10               "boatTypeId":currentBoatType
11         });
12 
13         action.setCallback(this, function(response) {
14            
15             var state = response.getState();
16             if (component.isValid() && state === "SUCCESS") {
17                 component.set("v.boats", response.getReturnValue());
18             } else {
19                 console.log("Failed with state1: " + state);
20             }
21         });
22         $A.enqueueAction(action);
23     }
24 })
BoatSearchResultHelper.js

BoatSearchForm.cmp:顯示boattype的picklist以及註冊了搜索的事件

 1 <aura:component controller="BoatSearchResults" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
 2     <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
 3     <aura:attribute name="btypes" type="BoatType__c[]"/>
 4     <aura:attribute name='selectedType' type='string' default='All Type'/>
 5     <aura:registerEvent name="formsubmit" type="c:FormSubmit"/>
 6        
 7     <lightning:layout horizontalAlign="center" verticalAlign="end" >
 8         <lightning:layoutItem padding="horizontal-medium" class="slds-grid_vertical-align-center">
 9             <lightning:select aura:id="boatTypes" label="" name="selectType" onchange="{!c.handleChange}">
10                 <option value="">All Types</option>
11                 <aura:iteration items="{!v.btypes}" var="item">
12                     <option text="{!item.Name}" value="{!item.Id}" />
13                 </aura:iteration>
14             </lightning:select>
15         </lightning:layoutItem>
16         <lightning:layoutItem class="slds-grid_vertical-align-center" padding="horizontal-medium" >
17             <lightning:button class="slds-button" variant="brand" label="Search" onclick="{!c.onFormSubmit}"/>
18         </lightning:layoutItem> 
19   </lightning:layout>
20       
21 </aura:component>
BoatSearchForm.cmp

BoatSearchFormController.js

 1 ({
 2     doInit: function(component, event, helper) {
 3         var action = component.get("c.getboattypes");
 4         action.setCallback(this, function(response) {
 5             var state = response.getState();
 6             if (component.isValid() && state === "SUCCESS") {
 7                 component.set("v.btypes", response.getReturnValue());
 8             }
 9             else {
10                 console.log("Failed with state: " + state);
11             }
12         });
13 
14         // Send action off to be executed
15         $A.enqueueAction(action);
16     },
17     onFormSubmit:function(component, event, helper) {
18         
19         var boatTypeId = component.get("v.selectedType");
20         console.log("selected type : " + boatTypeId);
21         var formSubmit = component.getEvent("formsubmit");
22         formSubmit.setParams({"formData":
23                             {"boatTypeId" : boatTypeId}
24         });
25         formSubmit.fire();
26     },
27     handleChange:function(component, event, helper) {
28         var selectedBoatType = component.find("boatTypes").get("v.value");
29         console.log("selectedBoatType :  "+ selectedBoatType);
30         component.set("v.selectedType",selectedBoatType);
31     }
32     
33 })
BoatSearchController.js

BoatSearch.cmp

 1 <aura:component controller="BoatSearchResults" implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" >
 2     <aura:attribute name="boats" type="Boat__c[]" />
 3     <lightning:card title="Find a Boat" class="slds-m-bottom_10px">
 4         <c:BoatSearchForm />
 5     </lightning:card>
 6     <lightning:card title="Matching Boats"  >
 7             <c:BoatSearchResults aura:id="BSRcmp"/>
 8     </lightning:card>
 9     <aura:handler name="formsubmit"
10                   event="c:FormSubmit"
11                   action="{!c.onFormSubmit}"
12                   phase="capture"/>
13 </aura:component>
BoatSearch.cmp

BoatSearchController.js:三個核心的方法:初始化boat type,改變boat type的handler以及form submit 的handler

 1 ({
 2     onFormSubmit: function(component, event, helper){
 3         console.log("event received by BoatSearchController.js");
 4         var formData = event.getParam("formData");
 5         var boatTypeId = formData.boatTypeId;
 6         console.log("boatTypeId : "+boatTypeId);
 7         
 8         var BSRcmp = component.find("BSRcmp");
 9         var auraMethodResult = BSRcmp.search(boatTypeId);
10         console.log("auraMethodResult: " + auraMethodResult);
11     }
12 })
BoatSearchController.js

左側的功能已經實現。左側的功能主要是顯示全部的Boat Type,選擇一個Boat Type後點擊search進行事件處理調用子元素進行搜索操做以及進行賦值操做,當選擇子元素組件之後觸發兩個APPLICATION 的事件將選中的boat信息進行廣播。下面的內容爲右側的部分。

FiveStarRating.cmp

1 <aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
2     <aura:attribute name="value" type="Integer"  default='0'/>
3     <aura:attribute name="readonly" type="boolean" default='false' />
4     <ltng:require styles="{!$Resource.fivestar + '/rating.css'}" scripts="{!$Resource.fivestar + '/rating.js'}" afterScriptsLoaded="{!c.afterScriptsLoaded}" />
5     <aura:handler name="change" value="{!v.value}" action="{!c.onValueChange}"/>
6     <ul  class="{!v.readonly ? 'readonly c-rating' : 'c-rating'}" aura:id="ratingarea" >
7     </ul>
8 </aura:component>
FiveStarRating

FiveStarRatingController.js

 1 ({
 2     afterScriptsLoaded : function(component, event, helper) {
 3         debugger
 4         var domEl = component.find("ratingarea").getElement();
 5 
 6         var currentRating = component.get('v.value');
 7         var readOnly = component.get('v.readonly');
 8         var maxRating = 5;
 9         var callback = function(rating) {
10             component.set('v.value',rating);
11         }
12         component.ratingObj = rating(domEl,currentRating,maxRating,callback,readOnly); 
13     },
14     
15     onValueChange: function(component,event,helper) {
16         if (component.ratingObj) {
17             var value = component.get('v.value');
18             component.ratingObj.setRating(value,false);
19         }
20     }
21 })
FiveStarRatingController.js

AddBoatReview.cmp:一個form表單用來提交boat的評價信息,保存後觸發boatReviewAdded的事件進行事件觸發處理。

 1 <aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
 2     <aura:attribute name="boat" type="Boat__c"/>
 3     <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
 4     <aura:attribute name="boatReview" type="BoatReview__c"/>
 5     <aura:attribute access="private" name="recordError" type="String"/>
 6     <aura:registerEvent name="boatReviewAdded" type="c:BoatReviewAdded" />
 7     <force:recordData aura:id="service"
 8                       fields="Id,Name,Comment__c, Rating__c, Boat__c"
 9                       targetError="{!v.recordError}"
10                       targetFields="{!v.boatReview}"
11                       recordUpdated="{!c.onRecordUpdated}"
12                       />
13     
14     <lightning:layout multipleRows="true">
15         <lightning:layoutItem size="12" padding="around-small">
16             <lightning:input name="title" label="Title" value="{!v.boatReview.Name}"/>
17         </lightning:layoutItem>
18 
19         <lightning:layoutItem size="12" padding="around-small">
20             <label class="slds-form-element__label" for="input-id-01">Description</label>
21             <lightning:inputRichText value="{!v.boatReview.Comment__c}" disabledCategories="FORMAT_FONT"/>
22         </lightning:layoutItem>
23         <lightning:layoutItem size="12" padding="around-small">
24             <label class="slds-form-element__label" for="input-id-01">Rating</label>
25                 <ul class="slds-post__footer-actions-list slds-list_horizontal">
26                     <li class="slds-col slds-item slds-m-right_medium">
27                         <c:FiveStarRating value="{!v.boatReview.Rating__c}" />
28 
29                     </li>
30                 </ul>
31          </lightning:layoutItem>
32         <lightning:layoutItem size="12" class="slds-align--absolute-center">
33             <lightning:button iconName="utility:save" label="Submit" onclick="{!c.onSave}"/>
34         </lightning:layoutItem>
35     </lightning:layout>
36 </aura:component>
AddBoatReview.cmp

 AddBoatReviewController.js

 1 ({
 2     doInit: function(component, event, helper) {
 3         helper.onInit(component, event,helper);
 4     },
 5     onSave : function(component, event, helper) {
 6         var boat = component.get("v.boat");
 7         var boatr = component.get("v.boatReview");
 8         
 9         component.set("v.boatReview.Boat__c",boat.Id);
10         
11         component.find("service").saveRecord(function(saveResult){
12             if(saveResult.state==="SUCCESS" || saveResult.state === "DRAFT") {   
13                var resultsToast = $A.get("e.force:showToast");
14                 if(resultsToast) {
15                     resultsToast.setParams({
16                         "title": "Saved",
17                         "message": "Boat Review Created"
18                     });
19                     resultsToast.fire(); 
20                 } else {
21                     alert('Boat Review Created');
22                 }
23             } else if (saveResult.state === "ERROR") {
24                 var errMsg='';
25                 for (var i = 0; i < saveResult.error.length; i++) {
26                     errMsg += saveResult.error[i].message + "\n";
27                 }
28                 component.set("v.recordError", errMsg);
29             } else {
30                 console.log('Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error));
31             }
32             var boatReviewAddedEvnt=component.getEvent("boatReviewAdded");
33             boatReviewAddedEvnt.fire();
34             helper.onInit(component,event,helper);           
35         });
36     },
37     onRecordUpdated: function(component, event, helper) {
38     }
39 })
AddBoatReviewController.js

AddBoatReviewHelper.js:初始化表單的初始值信息

 1 ({
 2     onInit : function(component, event,helper) {
 3         component.find("service").getNewRecord(
 4             "BoatReview__c", // sObject type (entityAPIName)
 5             null,      // recordTypeId
 6             false,     // skip cache?
 7             $A.getCallback(function() {
 8                 var rec = component.get("v.boatReview");
 9                 var error = component.get("v.recordError");
10                 var boat=component.get("v.boat");
11                 if(error || (rec === null)) {
12                     console.log("Error initializing record template: " + error);
13                 }
14                 else {
15                     component.set("v.boatReview.Boat__c",boat.Id);
16                     var test=component.get("v.boatReview");
17                 }
18             })
19         );
20     }
21 })
AddBoatReviewHelper.js

 BoatReviews.cmp:聲明一個aura:method方法供父類調用實現當 保存完boat的評價後能夠跳轉到評價列表,初始化評價信息列表。

 1 <aura:component controller="BoatReviews" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
 2     <aura:attribute name="boat" type="Boat__c" />
 3     <aura:attribute name="boatReviews" type="BoatReview__c[]" access="private" />
 4     <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
 5     <!-- set up the aura:method for refresh -->
 6     <aura:method name="refresh"
 7                  action="{!c.doInit}"
 8                  description="invokes refresh whenever boat is updated" access="public">
 9     </aura:method>
10     <aura:handler name="change" value="{!v.boat}" action="{!c.doInit}"/>
11 
12    <aura:dependency resource="markup://force:navigateToSObject" type="EVENT"/>
13     <ui:scrollerWrapper class="scrollerSize">
14         <!--Scrollable content here -->
15         <aura:if isTrue="{!v.boatReviews.length==0}">
16             <lightning:layoutItem class="slds-align_absolute-center" flexibility="auto" padding="around-small">   
17                 <ui:outputText value="No Reviews Available" />
18             </lightning:layoutItem>
19         </aura:if>
20         <div class="slds-feed" style="max-height: 250px;">
21             <ul class="slds-feed__list">
22                 <aura:iteration items="{!v.boatReviews}" var="boatReview">
23                     <li class="slds-feed__item">
24                         <header class="slds-post__header slds-media">
25                             <div class="slds-media__figure">
26                                 <img alt="Image" src="{!boatReview.CreatedBy.SmallPhotoUrl}" title="" />
27                             </div>
28                             <div class="slds-media__body">
29                                 <div class="slds-grid slds-grid_align-spread slds-has-flexi-truncate">
30                                     <p>
31                                         <a href="javascript:void(0)" onclick="{!c.onUserInfoClick}" data-userid="{!boatReview.CreatedBy.Id}">
32                                         {!boatReview.CreatedBy.Name}
33                                         </a> - {!boatReview.CreatedBy.CompanyName}
34                                     </p>
35                                 </div>
36                                 <p class="slds-text-body_small">
37                                     <lightning:formattedDateTime value="{!boatReview.CreatedDate}" 
38                                         year="numeric" month="short" day="numeric"  
39                                         hour="2-digit" minute="2-digit" hour12="true"/>
40                                 </p>
41                             </div>
42                         </header>
43                         <div class="slds-post__content slds-text-longform">
44                             <div>
45                                 <ui:outputText value="{!boatReview.Name}" />              
46                             </div>
47                             <div>
48                                 <ui:outputRichText class="slds-text-longform"  value="{!boatReview.Comment__c}" />
49                             </div>
50                         </div>
51                         <footer class="slds-post__footer">
52                             <ul class="slds-post__footer-actions-list slds-list_horizontal">
53                                 <li class="slds-col slds-item slds-m-right_medium">
54                                     <c:FiveStarRating aura:id="FiveStarRating" value="{!boatReview.Rating__c}" readonly="true"/>
55                                 </li>
56                            </ul>
57                         </footer>
58                     </li>
59                 </aura:iteration>
60             </ul>
61         </div>
62     </ui:scrollerWrapper>
63 </aura:component>
BoatReviews.cmp

BoatReviewsController.js

 1 ({
 2     doInit : function(component, event, helper) {
 3         helper.onInit(component, event);
 4     },
 5     onUserInfoClick : function(component,event,helper){
 6         var userId = event.currentTarget.getAttribute("data-userid");
 7         var navEvt = $A.get("e.force:navigateToSObject");
 8         navEvt.setParams({
 9             "recordId" : userId,
10         });
11         navEvt.fire()
12 
13     }
14 })
BoatReviewsController.js

BoatReviewsHelper.js

 1 ({
 2     onInit : function(component, event) {
 3             var boat=component.get("v.boat");
 4             var action = component.get("c.getAll");
 5             action.setParams({
 6               "boatId":boat.Id
 7             });
 8             action.setCallback(this, function(response) {
 9                
10                 var state = response.getState();
11                 if (component.isValid() && state === "SUCCESS") {
12                     component.set("v.boatReviews", response.getReturnValue());
13                 }
14                 else {
15                     console.log("Failed with state: " + state);
16                 }
17             });
18             $A.enqueueAction(action);
19     }
20 })
BoatReviewsHelper.js

 BoatDetail.cmp:

 1 <aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
 2     <aura:attribute name="boat" type="Boat__c[]" />
 3     <aura:dependency resource="markup://force:navigateToSObject" type="EVENT"/>
 4     <lightning:card iconName="utility:anchor">
 5           <aura:set attribute="title">
 6             {!v.boat.Contact__r.Name}'s Boat
 7          </aura:set>
 8 
 9         <aura:set attribute="Actions">
10             <aura:if isTrue='{!v.showButton}'>
11                 <lightning:button label="Full Details" onclick="{!c.onFullDetails}" />
12             </aura:if>    
13         </aura:set>
14         
15         <lightning:layout multipleRows="true">
16             <lightning:layoutItem size="6" padding="around-small">
17                 
18                 <div class="slds-p-horizontal--small">
19                     <div class="boatproperty">
20                         <span class="label">Boat Name: </span>
21                         <span>{!v.boat.Name}</span>
22                     </div>
23                     <div class="boatproperty">
24                         <span class="label">Type:</span>
25                         <span>{!v.boat.BoatType__r.Name}</span>
26                     </div>
27                     <div class="boatproperty">
28                         <span class="label">Length:</span>
29                         <span> {!v.boat.Length__c}ft</span>
30                     </div>
31                     <div class="boatproperty">
32                         <span class="label">Est. Price:</span>
33                         <span><lightning:formattedNumber value="{!v.boat.Price__c}" style="currency"
34                              currencyCode="USD" currencyDisplayAs="symbol"/></span>
35                     </div>
36                     <div class="boatproperty">
37                         <span class="label">Description:</span>
38                         <span><ui:outputRichText value="{!v.boat.Description__c}"/></span>
39                     </div>
40                 </div>
41 
42             </lightning:layoutItem>
43 
44             <lightning:layoutItem size="6" padding="around-small">
45                 
46                 <lightning:button variant='neutral' label='Full Details' onclick='{!c.onFullDetails}'/>
47                 <div class="imageview" style="{!'background-image:url(\'' + v.boat.Picture__c + '\'); '}" />
48             </lightning:layoutItem>
49             
50         </lightning:layout>
51         
52     </lightning:card>
53     
54 </aura:component>
BoatDetail.cmp

BoatDetailController.js

 1 ({
 2      onFullDetails: function(component, event, helper) {
 3         var navEvt = $A.get("e.force:navigateToSObject");
 4         navEvt.setParams({
 5           "recordId":  component.get("v.boat.Id")
 6 
 7         });
 8         navEvt.fire();
 9     }
10 })
BoatDetailController.js

BoatDetail.css

 1 .THIS .label {
 2     font-weight: bold;
 3     display: block;
 4 }
 5 .THIS .boatproperty {
 6     margin-bottom: 3px;
 7 }
 8 .THIS .imageview {
 9     background-repeat: no-repeat;
10     background-size: contain;
11     height: 200px;
12     margin: 2px;   
13 }
BoatDetail.css

BoatDetails.cmp:包含了兩個事件的handler,分別是Application Event Boat選擇的事件處理以及 add boat Review的事件處理

 1 <aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
 2     <aura:attribute name="selectedTabId" type="String"/>
 3     <aura:attribute name="boat" type="Boat__c"/>
 4     <aura:attribute name="id" type="Id" />
 5     <aura:attribute name="recordError" type="String"/>
 6     <aura:dependency resource="markup://force:navigateToSObject" type="EVENT"/>
 7     <aura:handler event="c:BoatSelected" action="{!c.onBoatSelected}" />
 8     <aura:handler name="boatReviewAdded" event="c:BoatReviewAdded" action="{!c.onBoatReviewAdded}"/>
 9     <force:recordData aura:id="service"
10                       layoutType="FULL"
11                       recordId="{!v.id}"
12                       fields="Id,Name,Description__c,Price__c,Length__c,Contact__r.Name,
13                                   Contact__r.Email,Contact__r.HomePhone,BoatType__r.Name,Picture__c"
14                       targetError="{!v.recordError}"
15                       targetFields="{!v.boat}"
16                       mode="EDIT"
17                       recordUpdated="{!c.onRecordUpdated}"
18                       />
19     
20     <lightning:tabset variant="scoped" selectedTabId="{!v.selectedTabId}" aura:id="details">
21         <lightning:tab label="Details" id="details" >
22             <aura:if isTrue="{!not(empty(v.id))}">
23              <c:BoatDetail boat="{!v.boat}"/> 
24                 </aura:if>
25         </lightning:tab>
26         <lightning:tab label="Reviews" id="boatreviewtab" >
27            
28             <aura:if isTrue="{!not(empty(v.id))}">
29                 <c:BoatReviews boat="{!v.boat}"  aura:id="BRcmp"/> 
30             </aura:if>
31         </lightning:tab>
32         <lightning:tab label="Add Review" id="addReview" >
33             <aura:if isTrue="{!not(empty(v.id))}">
34                 <c:AddBoatReview boat="{!v.boat}"/> 
35             </aura:if>
36         </lightning:tab>
37     </lightning:tabset>
38     
39     <aura:if isTrue="{!not(empty(v.recordError))}">
40         <div class="recordError">
41             <ui:message title="Error" severity="error" closable="true">
42                 {!v.recordError}
43             </ui:message>
44         </div>
45     </aura:if>
46 </aura:component>
BoatDetails.cmp

BoatDetailsController.js

 1 ({
 2     init: function(component, event, helper) {
 3         component.set("v.enableFullDetails", $A.get("e.force:navigateToSObject"));
 4     },
 5     onBoatSelected : function(component, event, helper) {
 6         var boatSelected=event.getParam("boat");
 7         component.set("v.id",boatSelected.Id);
 8         component.find("service").reloadRecord() ;
 9         
10     },
11     onRecordUpdated : function(component, event, helper){
12         
13     },
14     onBoatReviewAdded : function(component, event, helper) {
15         console.log("Event received");
16         component.find("details").set("v.selectedTabId", 'boatreviewtab');
17         var BRcmp = component.find("BRcmp");
18         console.log(BRcmp);
19         var auraMethodResult = BRcmp.refresh();            
20     }
21 
22 })
BoatDetailsController.js

 BoatHeader.cmp

 1 <aura:component implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
 2         <lightning:layout class="slds-box">
 3         <lightning:layoutItem >
 4             <lightning:icon iconName="custom:custom54" alternativeText="FriendswithBoats"/>
 5         </lightning:layoutItem>
 6         <lightning:layoutItem padding="horizontal-small">
 7             <div class="page-section page-header">        
 8                 <h1 class="slds-page-header__title slds-truncate slds-align-middle" title="FriendswithBoats">Friends with Boats</h1>   
 9             </div>
10         </lightning:layoutItem>
11     </lightning:layout> 
12 </aura:component>
BoatHeader.cmp

Map.cmp

 1 <aura:component implements="flexipage:availableForAllPageTypes" access="global" >
 2     
 3     <aura:attribute access="private" name="leafletMap" type="Object" />
 4 
 5     <aura:attribute name="width"  type="String" default="100%" />
 6     <aura:attribute name="height" type="String" default="200px" />
 7     <aura:attribute name="location" type="SObject"/>
 8     <aura:attribute name="jsLoaded" type="boolean" default="false"/>
 9     <aura:handler event="c:PlotMapMarker" action="{!c.onPlotMapMarker}"/>
10     <ltng:require styles="{!$Resource.Leaflet + '/leaflet.css'}" 
11                   scripts="{!$Resource.Leaflet + '/leaflet-src.js'}"
12                   afterScriptsLoaded="{!c.jsLoaded}" /> 
13     <lightning:card title="Current Boat Location" >
14     <div aura:id="map" style="{!'width: ' + v.width + '; height: ' + v.height}">
15         <div style="width:100%; height:100%" class="slds-align_absolute-center">Please make a selection</div>
16     </div>
17     </lightning:card>
18 </aura:component>
Map.cmp

Map.css

1 .THIS {
2     width: 100%;
3     height: 100%;
4     border: 1px dashed black;
5 }
Map.css

Map.design

1 <design:component label="Map">
2     <design:attribute name="width" label="Width" description="The width of the map as a percentage (100%) or pixels (100px)" />
3     <design:attribute name="height" label="Height" description="The height of the map as a percentage (100%) or pixels (100px)" />
4 </design:component>
Map.design

Map.svg

 1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 2 <svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 3     <!-- Generator: Sketch 39.1 (31720) - http://www.bohemiancoding.com/sketch -->
 4     <title>Slice</title>
 5     <desc>Created with Sketch.</desc>
 6     <defs></defs>
 7     <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
 8         <rect id="Rectangle" fill="#62B7ED" x="0" y="0" width="100" height="100" rx="8"></rect>
 9         <path d="M84.225,26.0768044 L62.925,15.4268044 C61.8895833,14.9830544 60.70625,14.9830544 59.81875,15.4268044 L40.1458333,25.3372211 L20.325,15.4268044 C19.1416667,14.8351377 17.6625,14.8351377 16.6270833,15.5747211 C15.5916667,16.1663877 15,17.3497211 15,18.5330544 L15,71.7830544 C15,73.1143044 15.7395833,74.2976377 16.9229167,74.8893044 L38.2229167,85.5393044 C39.2583333,85.9830544 40.4416667,85.9830544 41.3291667,85.5393044 L61.15,75.6288877 L80.8229167,85.5393044 C81.2666667,85.8351377 81.8583333,85.9830544 82.45,85.9830544 C83.0416667,85.9830544 83.78125,85.8351377 84.3729167,85.3913877 C85.4083333,84.7997211 86,83.6163877 86,82.4330544 L86,29.1830544 C86,27.8518044 85.4083333,26.6684711 84.225,26.0768044 L84.225,26.0768044 Z M78.6041667,32.8809711 L78.6041667,60.9851377 C78.6041667,62.6122211 77.125,63.7955544 75.6458333,63.2038877 C70.1729167,61.1330544 74.6104167,51.9622211 70.6166667,46.9330544 C66.91875,42.3476377 62.1854167,47.0809711 57.6,39.8330544 C53.3104167,32.8809711 59.0791667,27.8518044 64.4041667,25.1893044 C65.14375,24.8934711 65.8833333,24.8934711 66.475,25.1893044 L77.4208333,30.6622211 C78.3083333,31.1059711 78.6041667,31.9934711 78.6041667,32.8809711 L78.6041667,32.8809711 Z M48.8729167,74.0018044 C47.9854167,74.4455544 46.95,74.2976377 46.2104167,73.7059711 C44.73125,72.3747211 43.5479167,70.3038877 43.5479167,68.2330544 C43.5479167,64.6830544 37.63125,65.8663877 37.63125,58.7663877 C37.63125,52.9976377 30.8270833,51.5184711 25.0583333,52.1101377 C23.5791667,52.2580544 22.54375,51.2226377 22.54375,49.7434711 L22.54375,28.1476377 C22.54375,26.3726377 24.31875,25.1893044 25.7979167,26.0768044 L38.51875,32.4372211 C38.6666667,32.4372211 38.8145833,32.5851377 38.8145833,32.5851377 L39.2583333,32.8809711 C44.5833333,35.9872211 43.5479167,38.5018044 41.3291667,42.3476377 C38.8145833,46.6372211 37.7791667,42.3476377 34.2291667,41.1643044 C30.6791667,39.9809711 27.1291667,42.3476377 28.3125,44.7143044 C29.4958333,47.0809711 33.0458333,44.7143044 35.4125,47.0809711 C37.7791667,49.4476377 37.7791667,52.9976377 44.8791667,50.6309711 C51.9791667,48.2643044 53.1625,49.4476377 55.5291667,51.8143044 C57.8958333,54.1809711 59.0791667,58.9143044 55.5291667,62.4643044 C53.4583333,64.5351377 52.5708333,68.9726377 51.6833333,71.9309711 C51.5354167,72.5226377 51.0916667,73.1143044 50.5,73.4101377 L48.8729167,74.0018044 L48.8729167,74.0018044 Z" id="Shape" fill="#FFFFFF"></path>
10     </g>
11 </svg>
Map.svg

MapController.js

 1 ({
 2     jsLoaded: function(component) {
 3         component.set("v.jsLoaded", true);
 4     }  ,
 5     onPlotMapMarker: function(component,event,helper) {
 6         debugger
 7         var id = event.getParam('sObjectId');
 8         var latitude = event.getParam('lat');
 9         var longitude = event.getParam('long');
10         var label = event.getParam('label');
11         var leafletMap = helper.getLeafletMap(component, latitude, longitude);
12         L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
13             attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
14         }).addTo(leafletMap);
15         
16         L.marker([latitude, longitude]).addTo(leafletMap)
17             .bindPopup(label)
18             .openPopup();
19     }
20 })
MapController.js

MapHelper.js

 1 ({
 2     getLeafletMap : function(component, latitude, longitude) {
 3 
 4         var leafletMap = component.get('v.leafletMap');
 5 
 6         if (!leafletMap) {
 7             var mapContainer = component.find('map').getElement();
 8 
 9             leafletMap = L.map(mapContainer, {zoomControl: false, tap: false})
10                 .setView([latitude, longitude], 13);
11             component.set('v.leafletMap', leafletMap);
12             
13         } else {
14         leafletMap.setView([latitude, longitude], 13);
15         }
16         return leafletMap;
17     }
18 })
MapHelper.js

MapRenderer.js

 1 ({
 2     rerender: function (component) {
 3         
 4         var nodes = this.superRerender();
 5         
 6         var location = component.get('v.location');
 7             
 8         if (!location) {
 9            
10         } else {
11             // If the Leaflet library is not yet loaded, we can't draw the map: return
12             if (!window.L) {
13                 return nodes;
14             }
15             
16             // Draw the map if it hasn't been drawn yet
17             if (!component.map) {
18                 var mapElement = component.find("map").getElement();
19                 component.map = L.map(mapElement, {zoomControl: true}).setView([42.356045, -71.085650], 13);
20                 component.map.scrollWheelZoom.disable();
21                 window.L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {attribution: 'Tiles © Esri'}).addTo(component.map);
22             }
23             
24             if (location && location.lat && location.long) {
25                 var latLng = [location.lat, location.long];
26                 if (component.marker) {
27                     component.marker.setLatLng(latLng);
28                 } else {
29                     component.marker = window.L.marker(latLng);
30                     component.marker.addTo(component.map);
31                 }
32                 component.map.setView(latLng);
33             }
34             
35             return nodes;
36         }
37         
38     }
39 })
MapRenderer.js

BoatReviews.cls

1 public class BoatReviews {
2     @AuraEnabled
3     public static list<BoatReview__c> getAll(Id  boatId ) {
4         
5         return [SELECT Id,Name,Comment__c,Rating__c,LastModifiedDate,CreatedDate,CreatedBy.Name,CreatedBy.SmallPhotoUrl,CreatedBy.CompanyName FROM BoatReview__c WHERE Boat__c=:boatId];
6     }
7 
8 }
BoatReviews.cls

BoatSearchResults.cls

 1 public class BoatSearchResults  {
 2     
 3     public list<Boat__c> Boats{get;set;}
 4     
 5     @AuraEnabled
 6     public static List<BoatType__c> getboattypes() {
 7         return [SELECT Name, Id FROM BoatType__c];
 8     }
 9     
10     @AuraEnabled
11     public static List<Boat__c> getBoats(string boatTypeId ) {
12         list<Boat__c> obj = new list<Boat__c>();
13         if(boatTypeId!='') {
14             obj=[SELECT id, BoatType__c, picture__c, name,contact__r.Name, Geolocation__Latitude__s, Geolocation__Longitude__s
15                     FROM Boat__c
16                     WHERE BoatType__c =: boatTypeId];
17         }else {
18             obj=[SELECT id, BoatType__c,picture__c, name,contact__r.Name, Geolocation__Latitude__s, Geolocation__Longitude__s
19                     FROM Boat__c];
20         }
21         return obj;
22     }
23 }
BoatSearchResults

 FriendsWithBoats.app

 1 <aura:application extends="force:slds" >
 2     <c.BoatHeader/>
 3     <lightning:layout >
 4     
 5         <div class="slds-col slds-size_2-of-3">
 6             <c.BoatSearch/>
 7         </div>
 8         <div class="slds-col slds-size_1-of-3">
 9           
10             <c.BoatDetails /> 
11             <c.Map /> 
12         </div>
13     </lightning:layout>
14 </aura:application>
FriendsWithBoats.app

 效果展現:

https://v.youku.com/v_show/id_XNDA5MzYyMDUwMA==.html?spm=a2h3j.8428770.3416059.1

總結:經過本篇能夠大體對Aura架構下的一個簡單的APP開發有一個基本的概念,此功能的代碼實現不惟一,感興趣的也可使用其餘的方式實現。篇中有錯誤的地方歡迎指出,有不懂的歡迎提出。

相關文章
相關標籤/搜索