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


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

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

通過步驟安裝相關的app exchange即可安裝相關的表結構以及初始化數據,詳細可以看這個superbadge的細節描述。安裝以后主要有3個表,Boat Type、Boat、BoatReview。相關表結構關系如下:

 Boat Type在這個demo中用來存儲 船的類型,當然這個數據也可以維護在custom setting中;

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

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

 接下來說一下想要實現的UI,這個superbadge主要想實現以下的功能:

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

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

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

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開發有一個基本的概念,此功能的代碼實現不唯一,感興趣的也可以使用其他的方式實現。篇中有錯誤的地方歡迎指出,有不懂的歡迎提出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM