前面的一些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>
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>
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>
FormSubmit.evt
1 <aura:event type="COMPONENT" description="Event template" > 2 <aura:attribute name="formData" type="object"/> 3 </aura:event>
BoatReviewAdded.evt
1 <aura:event type="COMPONENT" description="Event template" />
接下来按照上图中的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>
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 })
此元素组件实现了当点击了搜索出来的列表的某个子单元之后,便会触发三个事件,从而会根据绑定这些事件的元素组件按照事件传播方式进行分别执行。
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.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>
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>
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 })
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 })
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>
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 })
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>
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 })
左侧的功能已经实现。左侧的功能主要是显示全部的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>
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 })
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>
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 })
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 })
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>
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 })
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 })
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>
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 })
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 }
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>
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 })
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>
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.css
1 .THIS { 2 width: 100%; 3 height: 100%; 4 border: 1px dashed black; 5 }
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.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>
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: '© <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 })
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 })
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 })
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 }
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 }
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>
效果展现:
https://v.youku.com/v_show/id_XNDA5MzYyMDUwMA==.html?spm=a2h3j.8428770.3416059.1
总结:经过本篇能够大体对Aura架构下的一个简单的APP开发有一个基本的概念,此功能的代码实现不惟一,感兴趣的也可使用其余的方式实现。篇中有错误的地方欢迎指出,有不懂的欢迎提出。