1. 程式人生 > 實用技巧 >【Salesforce】Lightning 元件基礎知識

【Salesforce】Lightning 元件基礎知識

Lightning框架簡介

Lightning框架是Salesforce提供的一套基於使用者介面的開發框架,對於開發單頁面應用(Single Page Application)有很大的幫助。它和Visualforce可以共存,但開發的方法並不相同。

Lightning有單獨的前端架構,基於名叫aura的框架,主要包括:

  • 元件:由XML語言開發的使用者介面,元件內部可以包含其他元件和標準的HTML元素,Lightning框架本身也提供了若干標準組件
  • 應用:Lightning應用是一種特殊的元件,它是整個程式的入口。一個Lightning應用包含若干Lightning元件

每個元件或應用還包含了:

  • 前端控制器:包含了JavaScript函式,用於和元件的元素互動
  • 輔助函式:可以看作是前端控制器的擴充套件,用於儲存JavaScript輔助函式
  • CSS:儲存了針對於某個元件的CSS

Lightning框架中通過前端控制器和後端進行資料通訊,在前端控制器中提供了直接呼叫Apex程式碼(後端控制器)的功能。

Lightning元件

本文通過一個簡單的“Hello World”例子介紹如何建立和編輯Lightning元件、應用,以及元件間的通訊。

新建Lightning元件

在Developer Console中,點選“File”選單,指向“New”,點選“Lightning Component”,輸入名字“helloworld”,點選“Submit”按鈕,即可新建一個Lightning元件。

每個Lightning元件不光包含了元件本身,還包含了其他的檔案。當新建“helloworld”元件後,在Developer Console的右側有一個列表,其中包含了和元件相關的各種檔案,比如控制器、輔助函式、頁面樣式、文件等。點選任意一項,即可新建相應的檔案。

定義Lightning元件外觀的檔名以“.cmp”結尾,這裡就是“helloworld.cmp”。

在編輯區域中,系統已經預設生成了一段程式碼:

<aura:component >
</aura:component>

每一個Lightning元件都是包含在“aura:component”標籤中。

在“aura:component”標籤中寫入一段HTML程式碼:

<aura:component >
    <p> Hello world! </p>
</aura:component>

當元件執行之後,在螢幕中就會輸出文字。

新建Lightning應用

Lightning元件無法單獨執行,它必須被包含在一個Lightning應用中。使用者只有通過Lightning應用才能執行元件的功能。

Lightning應用可以看作是一種特殊的元件。在Developer Console中,點選“File”選單,指向“New”,點選“Lightning Application”,輸入名字“helloworld_APP”,點選“Submit”按鈕,即可新建一個Lightning應用。

Lightning元件可以包含其他元件,Lightning應用可以包含元件,但是Lightning應用不能包含應用。

定義Lightning應用外觀的檔名以“.app”結尾,這裡就是“helloworld_APP.app”。

在新建的Lightning應用中,系統也生成了預設的程式碼:

<aura:application >
</aura:application>

每一個Lightning應用都是包含在“aura:application”標籤中。

在應用中呼叫剛才建立的元件:

<aura:application >
    <c:helloworld />
</aura:application>

在視窗右側的列表上方有“Preview”按鈕,點選即可執行Lightning應用。

執行Lightning應用,即可看到螢幕上顯示了“Hello world!”的字樣,說明執行成功了,元件的內容也顯示在了應用中。

為元件增加CSS樣式

在“helloworld”元件中,會顯示預設的“Hello world!”文字。如果想修改頁面中的顯示,可以在Developer Console右側列表中點選“STYLE”,系統會自動建立“helloworld.css”檔案。開發者可以在此檔案中修改CSS樣式。

要注意的是,不同於普通的CSS檔案,在整個檔案中,每一個樣式必須帶有“.THIS”,它的作用是保證新建的樣式只對當前的元件有效。

向“helloworld.css”檔案中新增一段CSS程式碼:

p.THIS {
    font-size: 48px;
    color: blue;
}

儲存後再次執行應用,可以看到顯示的文字樣式已經變化了。

為元件新增屬性

現在的“helloworld”元件只能顯示一段靜態文字。如果需要增加其他動態功能,比如自定義顯示文字的內容,則必須要用到元件的“屬性”。

每個元件可以包含若干“屬性”。元件的屬性可以看作是包含在元件內的變數,它們可以是任何型別。當元件載入後,元件的屬性值會被初始化,元件的控制器也可以更改屬性的值。元件的屬性可以被繫結在元件內部的元素中,從而實現動態功能。

元件的屬性要定義在“aura:attribute”標籤中。在元件的元素中,如果想繫結某個屬性,需要用“{!v.屬性名}”的語法來實現。

比如在“helloworld”元件中增加一個“message”屬性,並輸出到“p”標籤中:

<aura:component >
    <aura:attribute name="message" type="String" default="test user" />
	<p> Hello world! - {!v.message} </p>
</aura:component>

再次執行Lightning應用,可以看到顯示的文字從“Hello world!”變為了“Hello world! - test user”。

由於屬性和元件的元素相互繫結,如果在應用執行時更改屬性“message”的值,那麼顯示的文字也會相應的發生變化。

資料提供者

在元件中,如果想繫結一個屬性,需要用“{!v.屬性名}”的語法。其中的“v”被稱為資料提供者(Value Provider)。如果想在元件中顯示稍微複雜的表示式而非單獨的屬性值,同樣可以用“{! }”表示式。

比如在Lightning框架中提供了一個標準顯示文字的元件“ui:outputText”,設定其“value”屬性即可顯示相應的文字。在“helloworld”中可以將程式碼變為:

<aura:component >
    <aura:attribute name="message" type="String" default="test user" />
    <p> <ui:outputText value="{! 'Hello world! - ' + v.message}" /> </p>
</aura:component>

執行應用後輸出的文字和之前一樣。在這段程式碼中,“{! }”表示式的裡面不光只有屬性“message”,還在其之前增加了固定的字串。

屬性的型別

屬性可以是任何型別,除了基本的字串、數字等,還可以是集合型別、sObject物件型別。

比如:

<!--使用標準sObject物件作為屬性型別-->
<aura:attribute name="account" type="Account" />
<!--使用標準sObject物件作為屬性型別,並初始化某些屬性-->
<aura:attribute name="account" 
                type="Account" 
                default="{ 'Name': 'Salesforce',
                            'Type': 'Prospect'}"/>
<!--使用自定義sObject物件作為屬性型別-->
<aura:attribute name="address" type="Address__c" />
<!--使用自定義sObject物件作為屬性型別,並初始化某些屬性-->
<aura:attribute name="address" 
                type="Address__c" 
                default="{ 'Name': 'ExampleAddress',
                            'Street_name__c': 'Example Street Name'}"/>
<!--使用列表作為屬性型別-->
<aura:attribute name="contactList" type="List" />
<!--使用列表作為屬性型別,並初始化列表-->
<aura:attribute name="textList" 
                type="List" 
                default="['text 1',
                            'text 2',
                            'text 3']" />

<!--在元件中使用sObject物件的欄位-->
<aura:outputText value="{!v.account.Name}" />

迴圈讀取集合型別屬性的值

當一個屬性是集合型別時,比如字串的列表,在元件中可以使用標準組件“aura:iteration”遍歷其每一個元素。

比如:

<aura:attribute name="textList" 
                type="List" 
                default="['text 1',
                            'text 2',
                            'text 3']" />

<!--在元件迴圈顯示字串-->
<aura:iteration items="{! v.textList }" var="singleText">
    <p> {! singleText } </p>
</aura:iteration>

在上面的程式碼中,使用了標準元素“aura:iteration”。我們將字串列表屬性“textList”繫結到迴圈列表屬性“items”中,並定義“singleText”為每一個迴圈中列表中變數的名字,類似於“for(String singleText : textList)”。在迴圈元件的內部,使用“{! }”表示式顯示每一個迴圈元素的內容,注意這裡不需要使用“v”了。

為元件新增功能

假設在“helloworld”中,需要增加一個按鈕,點選之後屬性“message”要發生變化。

用標準組件“ui:button”可以新增按鈕,要想實現點選按鈕之後更改屬性的功能,就必須使用前端控制器。

在Developer Console的右側列表中,點選“CONTROLLER”,系統會自動建立一個前端控制器檔案“helloworldController.js”。它是一個JavaScript檔案,開發者可以在其中新增JS函式實現功能。

在Developer Console的右側列表中,點選“HELPER”,系統會自動建立一個輔助函式檔案“helloworldHelper.js”。它是一個JavaScript檔案,開發者可以在其中新增JS函式,這些函式可以從控制器檔案中呼叫。

另外要注意的是,在控制器檔案中,如果定義了若干函式,它們之間不能互相呼叫。所以必須將某些公共的功能挪到輔助函式檔案中,再使用“helper.函式名”來呼叫功能。

在控制器檔案中增加一個“handleClick”函式,更改元件中“message”屬性的值:

handleClick : function(component, event, helper) {
    component.set('v.message', 'Updated Message!');
}

在元件的外觀中增加一個按鈕,點選之後執行“handleClick”函式:

<ui:button label="Change text" press="{!c.handleClick}"/>

其中“label”是要在按鈕上顯示的文字,“press”是一個事件,當點選按鈕後,呼叫“press”裡定義的函式。

注意,這裡使用了“{!c.函式名}”的方式來呼叫JS控制器中的函式,其中的“c”便是代表了“Controller”。

執行應用,當點選了按鈕之後,螢幕上顯示的文字便從“Hello world! - test user”變成了“Hello world! - Updated Message!”。

控制器函式詳解

每一個控制器的函式都預設帶有三個引數:

  • component:代表了當前的元件
  • event:代表了觸發的事件
  • helper:代表了輔助函式的檔案,如果建立了“HELPER”檔案,並定義了某些函式,則使用“helper.函式名()”的語法即可呼叫“HELPER”檔案中的函式

用“component.set('v.屬性名', 要設定的值)”的方式可以直接設定元件中屬性的值,這是最常用的一種設定方法。

同樣的,也可以用“component.get('v.屬性名')”來得到元件中屬性的值。

比如:

exampleFunction : function(component, event, helper) {
    // 得到message屬性的值
    var messageValue = component.get('v.message');

    // 設定message屬性的值
    component.set('v.message', 'value to set');

    // 呼叫helper檔案中的某函式
    var resultFromHelper = helper.exampleHelperFunction();
}

如果想得到觸發某函式的元件元素的內容,則需要使用event引數。

比如在元件中有一個按鈕,點選會觸發控制器中的“handleClick()”函式。在“handleClick()”函式中,使用“event.getSource()”即可得到按鈕元素。

元件中的設定:

<ui:button label="button text" press="{!c.handleClick}" />

控制器中:

handleClick : function(component, event, helper) {
    // 得到元件中的按鈕元素
    var buttonClicked = event.getSource();

    // 得到元件中按鈕元素的“label”屬性
    var buttonValue = buttonClicked.get('v.label');
    // buttonValue的值是“button text”
}

使用這種方式,可以直接得到元件中元素的各種屬性等。

元件和Apex通訊

在“helloworld”元件中,如果想要通過點選按鈕,從資料庫中讀取一個名叫“GenePoint”的Account物件,並將其名字和電話號碼顯示在頁面中,則不光需要前端的功能,也需要和Apex類進行通訊,從資料庫中查詢並得到資料。

要實現這個功能,需要完成以下幾個方面:

  • 準備Apex類和函式,能查詢並返回物件的內容
  • 在元件中定義屬性,型別為sObject物件,並將元件中的某些元素繫結到該物件的欄位中
  • 將元件與Apex類聯絡起來
  • 在元件中呼叫Apex函式,接收Apex函式的執行結果,並更新元件中的屬性

準備Apex類和函式

如果要使一個Apex函式可以被Lightning元件呼叫,則必須滿足兩點:

  1. 該函式的定義包含“@AuraEnabled”註解
  2. 該函式是靜態型別

現在建立相應的Apex類和函式:

public class LightningAccountController {
	@AuraEnabled
    public static Account getAccount(String name) {
        List<Account> accountList = [SELECT Id, Name, Phone 
                                     FROM Account
                                     WHERE Name LIKE :name
                                    ];
        if(accountList.size() > 0) {
            return accountList[0];
        } else {
            return null;
        }
    }
}

在元件中定義屬性

在“helloworld”元件中,定義一個型別為Account的屬性:

<aura:attribute name="account" type="Account" />

<ui:button label="Get Account" press="{!c.handleClick}" />
    	
<p>
    <ui:outputText value="{!v.account.Name}" />
</p>

<p>
    <ui:outputText value="{!v.account.Phone}" />
</p>

將元件與Apex類聯絡起來

元件與Apex類聯絡的方式是在“aura:component”標籤中設定“controller”屬性為Apex類的名字:

<aura:component controller="LightningAccountController">

在元件中呼叫Apex函式,接收Apex函式的執行結果,並更新元件中的屬性

在元件中呼叫Apex函式,需要通過控制器檔案。

在“helloworldController.js”檔案中修改“handleClick()”函式為:

handleClick : function(component, event, helper) {
    // 1. 宣告Apex類中的函式名
    var action = component.get("c.getAccount");

    // 2. 設定Apex函式的引數
    // 通常引數的值可以從元件中得到,比如使用component.get('v.userInput')
    action.setParams({
        "name": 'GenePoint'
    });

    // 3. 設定Apex函式執行完成後的功能
    action.setCallback(this, function(response) {
        // 得到Apex的結果狀態
        var state = response.getState();

        if (state === "SUCCESS") {
            // 得到Apex的結果,結果可以是基本型別,也可以是sObject或集合型別等
            var result = response.getReturnValue();

            component.set('v.account', result);
        } else {
            // 錯誤處理
            // Do nothing
        }
    });

    // 4. 開始呼叫Apex函式
    $A.enqueueAction(action);
}

程式碼解釋:

  1. 呼叫Apex函式需要四步,當然,如果Apex函式中沒有引數,則第二步可以省略。
  2. Apex函式的呼叫是非同步執行的,所以在上面的程式碼中,當執行了Apex函式之後,如果還有其他的程式碼,其他的程式碼有可能比“action.setCallback()”函式裡的程式碼先執行,所以不能用“action.setCallback()”裡的變數去決定“$A.enqueueAction(action);”語句之後的程式碼。
  3. 在Apex函式執行結束後,需要檢測結果的狀態,並且使用“getReturnValue()”函式來得到返回的結果。返回的結果無需型別轉換,可以直接賦值給元件中的屬性。
  4. $A是系統提供的一個全域性變數,包含了一些重要的功能和服務。

至此,執行應用的話,點選按鈕“Get Account”,螢幕上會給出查詢到的Account物件的結果。

事件(Event)和控制代碼(Handler)

在以上的例子中,所有的元件外觀和邏輯(除了Apex部分)都是在一個元件中。在開發的過程中,這樣做或許比較方便,但是有一個缺點,就是前端控制器中的邏輯只能被這一個元件使用。

如果有一個公用的方法,每個元件都可以使用,那麼該方法就會變得可重用,提高了程式碼的效率。

Lightning中的事件(Event)和控制代碼(Handler)就實現了這種功能。

事件和控制代碼有以下幾個特性:

  1. 事件需要單獨定義,獨立於任何元件。
  2. 每個元件都可以註冊事件,從而取得事件的使用權。
  3. 在元件中可以設定控制代碼,控制代碼中可以設定具體某個事件,從而宣告此元件對於某個事件會進行處理。
  4. 在註冊了事件的元件(A)使用事件時,系統會自動尋找包含該事件控制代碼的元件(B),從而自動呼叫B中的函式對事件進行處理。在這個過程中,元件A和B是相互獨立的,並不需要知道對方具體的功能,而事件通過“廣播”被自動進行了正確的處理。

事件自動含有“type”屬性,可以有兩種值,“APPLICATION”和“COMPONENT”,表明了該事件被應用還是元件使用。

還是以上面的“查詢Account物件名字、電話並顯示在螢幕上”的情況為例,重寫元件,並通過事件和控制代碼將功能完成。

分為以下幾個步驟:

  1. 新建事件
  2. 建立事件觸發元件
  3. 建立事件處理元件
  4. 在Lightning應用中包含事件處理元件

新建事件

在Developer Console中通過“File”選單的“New”子選單新建“Lightning Event”,命名為“FindAccount”。新建完成後,可以看到出現了“FindAccount.evt”的檔案。將其中的程式碼修改為:

<aura:event type="COMPONENT" description="Event template" >
    <aura:attribute name="accountName" type="String" />
</aura:event>

重要的是將預設的“type”屬性值改為“COMPONENT”,讓此事件對元件有效。

事件中包含了一個屬性“accountName”,用於接收要查詢的Account物件的名字。

建立事件觸發元件

要執行一個事件,必須要有事件的觸發元件和事件的處理元件。前者使用“aura:registerEvent”觸發事件,後者使用“aura:handler”處理事件。二者也可以被定義在同一個元件中。

在Developer Console中新建元件,命名為“FindAccountEventRegister”。新建完成後,修改程式碼如下:

<aura:component >
    <aura:registerEvent name="findAccountEvent" type="c:FindAccount"/>

    <ui:button label="Get Account" press="{!c.handleClick}" />
</aura:component>

可以看到,元件中註冊了事件,並且只有一個按鈕,用來點選並觸發事件。

在其控制器檔案中寫入“handleClick()”函式:

handleClick : function(component, event, helper) {
    var cmpEvent = component.getEvent('findAccountEvent');
    
    cmpEvent.setParams({
        "accountName": 'GenePoint'
    });
    
    cmpEvent.fire();
}

這段程式碼主要就是使用“component.getEvent()”函式得到元件中註冊的事件,再給事件中定義的屬性賦值,最後通過“fire()”函式觸發事件。

建立事件處理元件

新建元件,命名為“FindAccountEventHandler”。新建完成後,修改程式碼如下:

<aura:component controller="LightningAccountController">
	<aura:attribute name="account" type="Account" />
	
    <aura:handler name="findAccountEvent" event="c:FindAccount" action="{!c.handleEvent}"/>

    <c:FindAccountEventRegister />
    
    <p>
        <ui:outputText value="{!v.account.Name}" />
    </p>
    
    <p>
        <ui:outputText value="{!v.account.Phone}" />
    </p>
</aura:component>

程式碼解釋:

  1. 元件中定義了一個型別為Account的屬性,並在“p”標籤中顯示該屬性的欄位值。
  2. 元件連線了之前的例子中建立好的Apex類,從而可以呼叫其中的函式從資料庫查詢Account物件。
  3. 元件中使用“aura:handler”定義了事件的處理方式,幷包含了“FindAccountEventRegister”元件。這裡有個地方很重要:“aura:handler”的“name”屬性值和“FindAccountEventRegister”元件中“aura:registerEvent”的“name”屬性值是一樣的(findAccountEvent),這就保證了在事件觸發時,事件與兩個元件之間都有關聯。
  4. 元件中的“aura:handler”裡定義了“action”屬性,其作用是當接收到事件觸發的訊息時,呼叫控制器中相應的函式來處理事件。

在控制器檔案中加入如下程式碼:

handleEvent : function(component, event, helper) {
    var accountName = event.getParam('accountName');

    // 呼叫Apex類的函式來查詢Account物件並在元件中顯示結果
    var action = component.get("c.getAccount");
    action.setParams({
        "name": accountName
    });
    action.setCallback(this, function(response) {
        var state = response.getState();

        if (state === "SUCCESS") {
            var result = response.getReturnValue();

            component.set('v.account', result);
        } else {
            // Do nothing
        }
    });

    $A.enqueueAction(action);
}

這裡的重點是通過“event.getParam()”函式來得到事件中屬性的值。通過這個函式,在事件觸發元件(FindAccountEventRegister)中設定的事件的屬性值就被傳遞到了事件處理元件(FindAccountEventHandler),並進行下一步的處理。

在Lightning應用中包含事件處理元件

在Lightning應用中包含事件處理元件(FindAccountEventHandler)。執行該應用,點選按鈕,即可看到查詢的Account物件的結果和資訊。至此,事件和控制代碼的基本功能就完成了。

從這個例子中可以看出,事件可以將邏輯和輸入分離,使得每個元件包含的功能儘可能少,增加重用性,提高開發效率。

小結

通過上面的例子,我們主要闡述了Lightning元件的基本實現方法,並通過事件和控制代碼來實現了元件之間的通訊。

Lightning框架的前端部分主要基於aura框架,如果對其他前端框架(Vue,React)已經有了瞭解,上手Lightning會非常容易。