Lightning框架示例 - 动态建立Lightning组件

动态建立Lightning组件

组件化前端开发是Lightning框架的优点之一。在进行Lightning应用开发时,我们可以将组件进行嵌套、引用,从而实现模块的封装和重用,提高开发效率。

组件的嵌套和引用最常用的方法是在Lightning组件中直接使用组件标签,比如:

<aura:component controller="ExampleController">
    <c:ExampleChildComponent />
</aura:component>

在这段代码中,正在进行开发的Lightning组件中静态地引用了“ExampleChildComponent”组件。

另一种方法就是在控制器或辅助函数的Javascript代码中动态建立组件,主要使用“$A.createComponent()”函数(官方文档)。

本文将通过一个实例来阐述如何动态建立组件。

示例目标

本示例的目标是实现一个类似于Salesforce标准列表视图的应用,可以显示一个电话为“1234567890”的“客户”对象的列表,并可以更改它们的名字。

示例将包含以下几个部分:

  • Lightning应用
  • 列表组件,用于显示“客户”对象的列表
  • 更新组件,在运行时动态建立,包括一个文本框和提交按钮,接收用户的输入并将输入的值更新到“客户”对象的“名字”字段中
  • Lightning事件,用于在组件中传递数据

建立Lightning组件的Apex控制器

建立一个Apex类,取名为“AccountListViewController”,作为即将建立的Lightning组件的控制器。在这个Apex类中我们将编写“客户”对象相关的函数,包括了查找“客户”对象和更新“客户”对象。

为了演示简单起见,我们略去异常处理以及单元测试等代码。

代码如下:

public class AccountListViewController {
    @AuraEnabled
    public static String loadAccounts() {
        List<Account> accountList = [SELECT Id, Name, AccountNumber, Phone, Website FROM Account WHERE Phone = '1234567890'];
        
        return JSON.serialize(accountList);
    }
    
    @AuraEnabled
    public static String updateAccount(String jsonString) {
        Account acc = (Account)JSON.deserialize(jsonString, Account.class);
        
        update acc;
        
        return 'Success';
    }
}

建立更新事件

为了在两个组件中传递数据(列表组件、更新组件),需要建立一个Lightning事件,取名为“accountListViewEvt”,其中包含了“客户”对象的ID和名字两个属性。

代码如下:

<aura:event type="COMPONENT">
    <aura:attribute name="accountId" type="String" />
    <aura:attribute name="accountName" type="String" />
</aura:event>

建立更新组件

建立一个新的Lightning组件,命名为“AccountNameUpdateCmp”,用于接收用户的输入,其中包含:

  • 两个属性:“客户”的ID和名字
  • 输入框,用于让用户输入“客户”名字
  • 提交按钮,用于调用更新的函数
  • 事件注册,用于调用刚才建立的更新事件,传递数据

组件外观代码如下:

<aura:component >
    <aura:attribute name="accountId" type="String" access="public" />
    <aura:attribute name="accountName" type="String" access="public" />

    <aura:registerEvent name="accountListViewEvt" type="c:accountListViewEvt" />

    <div class="slds-form slds-form_horizontal">

        <div class="slds-form-element">
            <label class="slds-form-element__label">名字</label>
            <div class="slds-form-element__control">
                <lightning:input value="{!v.accountName}" />
            </div>
        </div>

        <div class="slds-form-element">
            <div class="slds-form-element__control">
                <lightning:button variant="brand" label="Submit" onclick="{!c.submit}" />
            </div>
        </div>
    </div>
</aura:component>

组件控制器代码如下:

({
    submit : function(component, event, helper) {
        var cmpEvent = component.getEvent("accountListViewEvt");

        cmpEvent.setParams({
            'accountId': component.get('v.accountId'),
            'accountName': component.get('v.accountName'),
        });

        cmpEvent.fire();
    },
})

从上面可以看出:

  • 此组件没有相关联的Apex控制器,只负责接收用户输入的值,作为“客户”对象的名字
  • 当用户点击提交按钮后,在控制器中使用“component.getEvent()”函数启用更新事件,并用“fire()”函数将用户的输入发送出去。

接下来要建立的列表组件将动态创建此组件,并在此组件执行结束后接收用户的输入(通过更新事件发送的值)。

建立列表组件

建立一个Lightning组件,取名为“AccountListViewCmp”,并在其中建立:

  • 一个列表类型的属性,用于存储要显示的“客户”对象
  • 一个表格,用于显示“客户”对象的内容
  • 一个初始化函数,用于读取“客户”对象
  • 一个创建更新组件的函数,动态建立更新组件
  • 一个事件处理函数,用于在更新组件提交后接收用户的输入,并通过Apex控制器更新“客户”对象的名字

组件外观的代码如下:

<aura:component controller="AccountListViewController">
    <!-- 将需要动态建立的组件作为依赖注册在此 -->
    <aura:dependency resource="c:AccountNameUpdateCmp" />

    <!-- Aura.Component[] 类型的属性用于保存动态建立的组件 -->
    <aura:attribute name="updateWidget" type="Aura.Component[]" access="public" />
    
    <aura:attribute name="accountList" type="Object[]" access="public" />
         
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

    <!-- 在更新事件启动之后,接收事件传递的值,并更新“客户”对象 -->
    <aura:handler name="accountListViewEvt" event="c:accountListViewEvt" action="{!c.handleUpdate}"/>

    <!-- 这个部分默认是隐藏的,其中包括了变量“updateWidget”,所以更新组件建立后将被显示在此 -->
    <div>
        <section role="dialog" tabindex="-1" aria-modal="true" class="slds-modal" aura:id="modalBody">
            <div class="slds-modal__container">
                <div class="slds-modal__content slds-p-around_medium">
                    {!v.updateWidget}
                </div>
            </div>
        </section>
        <div class="slds-backdrop" aura:id="modalBackdrop"></div>
    </div>

    <!-- 列表的外观,用于显示“客户”对象 -->
    <table class="slds-table slds-table_bordered">
        <thead>
            <tr class="slds-text-title">
                <th scope="col">
                    <div class="slds-truncate" title="客户名">客户名</div>
                </th>
                <th scope="col">
                    <div class="slds-truncate" title="电话">电话</div>
                </th>
            </tr>
        </thead>
        <tbody>
            <aura:iteration items="{!v.accountList}" var="acc">
                <tr>
                    <td>
                        <div class="slds-truncate">
                            <a class="slds-th__action" href="javascript:void(0);" role="button"
                                onclick="{!c.startUpdate}" data-account-id="{!acc.Id}" data-account-name="{!acc.Name}">
                                {!acc.Name}
                            </a>
                        </div>
                    </td>
                    <td>
                        <div class="slds-truncate">
                            {!acc.Phone}
                        </div>
                    </td>
                </tr>
            </aura:iteration>   
        </tbody>
    </table>
    
</aura:component>

组件的控制器代码如下:

({
    doInit : function(component, event, helper) {
        helper.loadAccountList(component, helper);
    },
    
    startUpdate : function(component, event, helper) {
        var accountId = event.currentTarget.dataset.accountId;
        var accountName = event.currentTarget.dataset.accountName;
        
        // 动态建立更新组件,并将“客户”的ID和当前的“名字”的值传递到更新组件中
        $A.createComponent(
            "c:AccountNameUpdateCmp",
            {
                "accountId": accountId,
                "accountName": accountName
            },
            function(newComponent, status, errorMessage){
                if (status === "SUCCESS") {
                    component.set('v.updateWidget', newComponent);

                    var modalBody = component.find('modalBody');
                    var modalBackdrop = component.find('modalBackdrop');
                    $A.util.addClass(modalBody,'slds-fade-in-open');
                    $A.util.addClass(modalBackdrop,'slds-backdrop--open');
                }
            }
        );
    },
    
    handleUpdate : function(component, event, helper) {
        // 在更新事件结束后,得到其中包含的值,即“客户”对象的ID和用户输入作为对象新名字的值
        var updateInfo = {
            Id: event.getParam('accountId'),
            Name: event.getParam('accountName')
        };
        
        // 调用Apex控制器的代码进行更新
        var action = component.get('c.updateAccount');
        action.setParams({
            jsonString: JSON.stringify(updateInfo)
        });
        action.setCallback(this, function(response) {
            var result = response.getReturnValue();
            if (result == 'Success') {
                helper.loadAccountList(component, helper);
            }
        });
        $A.enqueueAction(action);  
        
        var modalBody = component.find('modalBody');
        var modalBackdrop = component.find('modalBackdrop');
        $A.util.removeClass(modalBody,'slds-fade-in-open');
        $A.util.removeClass(modalBackdrop,'slds-backdrop--open');
    },
})

辅助函数代码如下:

({
    loadAccountList : function(component, helper) {
        var action = component.get('c.loadAccounts');
        action.setCallback(this, function(response) {
            var result = response.getReturnValue();
            var accountList = JSON.parse(result);
            component.set('v.accountList', accountList);
        });
        $A.enqueueAction(action);  
    }, 
})

建立Lightning应用

建立一个Lightning应用,取名为“AccountListViewApp”,包含了刚才的列表组件。代码如下:

<aura:application extends="force:slds">
    <c:AccountListViewCmp />
</aura:application>

小结

动态建立组件可以实现组件的灵活复用。

比如上面示例中的更新组件,我们可以再增加几个属性,扩展为通用的文本更新组件。它没有相关联的Apex控制器,没有很多逻辑,只负责接收和发送文本框中的数据(相关的逻辑由调用此组件的代码来处理)。

当然,动态建立的组件本身也可以包括Apex控制器和相关的逻辑,但是这样会增加和服务器端的通讯次数。为了保证运行效率,我们最好让它们只负责客户端的操作。

Salesforce对于动态建立的组件有垃圾回收机制,这只限于组件被创建后赋值给了“Aura.Component[]”类型的属性。当此组件不再被使用时,会被自动清除。

对于更新“客户”对象的“名字”功能,上面的示例有些过度设计了。但是在实际应用中,可以对和该示例类似的结构进行扩展,用于更复杂的场景,比如对于外部数据的CRUD操作(CRUD操作需要通过网络服务使用,Salesforce的默认功能可能无法使用)。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容