Salesforce Lightning开发学习重写新建/更新按钮

Posted luqinghua

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Salesforce Lightning开发学习重写新建/更新按钮相关的知识,希望对你有一定的参考价值。

重写新建/更新按钮的原因是因为项目需要用户在新建数据时从接口对数据进行校验,保证数据的有效性,同时获取接口返回的部分数据完成信息填充,而Sales force的trigger仅支持@future方法异步调用接口,不能实时完成数据校验

那么重写新建/更新按钮要面临的几个核心问题:

1、lightning暂时不支持lookup字段,如果重写可能要自定义组件

2、lightning新建按钮,怎么重写在项目下新建时自动填充的父对象

点击对象管理器,新建对象项目(Test_Project),部门(Test_Department)

以下是部门(Test_Department)的字段表

标签 API 数据类型
部门名称 Name 文本(80)  
项目 Project_Dep__c 查找(项目)  
角色 Role__c 选项列表

项目经理

项目顾问

项目开发

项目测试

邮箱 Email__c 电子邮件  
报表权限 Report_Access__c 复选框  
描述 Remarks__c 文本区(255)  

 先创建一个lightning组件Test_NewDepartment

<aura:component implements="lightning:actionOverride,
                 flexipage:availableForRecordHome,
                 force:hasRecordId,
                 flexipage:availableForAllPageTypes
" access="global" description="Test_NewDepartment"> </aura:component>

组件继承的几个接口说明下

lightning:actionOverride:继承该接口才能覆盖标准按钮

技术分享图片

force:hasRecordId:继承该接口才能通过“v.recordId"获取当前页面的记录ID

然后来处理第一个问题,lightning暂时不支持lookup字段的问题

经过了解,lightning提供了一个<lightning:recordEditForm>组件,通过<lightning:inputField>可以操作查找字段.

<aura:component implements="lightning:actionOverride,
                 flexipage:availableForRecordHome,
                 force:hasRecordId,
                 flexipage:availableForAllPageTypes"
access="global" description="Test_NewDepartment">
  <!-- 部门 -->
<aura:attribute name="simpleDepartmentRecord" type="Test_Department__c" default="{‘SobjectType‘:‘Test_Department__c‘}"/>   <!-- 错误消息-->
<aura:attribute name="recordError" type="String"/>
  <!-- 标记按钮能否点击-->
<aura:attribute name="flag" type="Boolean" default="true"/> <div aura:id="editDialog" role="dialog" tabindex="-1" aria-labelledby="header43" class="slds-modal slds-fade-in-open"> <div class="slds-modal__container"> <div class="slds-modal__header"> <h2 class="slds-text-heading--medium">{!(v.recordId == null?‘新增‘:‘更新‘) + ‘部门‘}</h2> </div> <lightning:messages /> <lightning:recordEditForm objectApiName="Test_Department__c"> <div class="slds-modal__content slds-p-around--medium slds-wrap" > <lightning:input aura:id="departmentId" label="用户名" name="userName" placeholder="请输入完整的用户名" required="true" value="{!v.simpleDepartmentRecord.Name}" /> <lightning:inputField class="customRequired" aura:id="roleId" fieldName="Role__c" value="{!v.simpleDepartmentRecord.Role__c}"/> <lightning:inputField class="customRequired" fieldName="Email__c" value="{!v.simpleDepartmentRecord.Email__c}"/> <lightning:inputField class="customRequired" aura:id="projectLookupId" fieldName="Project_Dep__c" value="{!v.simpleDepartmentRecord.Project_Dep__c}"/> <lightning:inputField class="customRequired" aura:id="reportId" fieldName="Report_Access__c" value="{!v.simpleDepartmentRecord.Report_Access__c}"/> <lightning:inputField fieldName="Remarks__c" value="{!v.simpleDepartmentRecord.Remarks__c}"/> </div> <div class="slds-modal__footer"> <lightning:button variant="neutral" label="Cancel" onclick="{!c.cancelDialog}" /> <lightning:button variant="brand" label="Submit" onclick="{!c.saveRecord}" disabled="{!v.flag}"/> </div> </lightning:recordEditForm> </div> </div>
  <!-- 弹窗打开的时候,用一个遮罩层将页面变暗-->
  <div aura:id="overlay" class="slds-backdrop slds-backdrop--open"></div>
</aura:component>

此时预览下页面查看效果

技术分享图片

通过<lightning:recordEditForm>组件,就能比较方便的重写新建/更新按钮,但此时会出现一个问题

当点击更新按钮的时候,会发现使用<lightning:input>的用户名没有办法进行更新,此时查阅相关文档

<Lightning:recordEditForm> 发现这样一句话:

The lightning:inputField component is used inside the lightning:recordEditForm to create editable fields. 
The lightning:outputField component and other display components such as lightning:formattedName can be used to
display read-only information in your form.

简单说,除了使用<lightning:inputField>的字段是可编辑的,lightning:input,lightning:formattedName等其他标签都是只读的

于是这里就遇到一个很严重的问题,查阅文档知道lightning:inputField字段不支持onblur属性,它支持onchange属性,而需求是在输入用户名后通过接口实时对输入的数据进行校验并获取返回的用户ID.

直接操作看起来不太可行,于是想到了迂回解决的办法,把更新也当作新建处理.

在点击更新的时候,将页面记录ID传到后台进行初始化,把查询出来的部门信息反向填充到表单中,这样就能绕开<lightning:recordEditForm>更新时lightning:input无法编辑,而lightning:inputField又不支持onblur属性的问题.

PS:
此处补充一个<lightning:recordEditForm>组件的小问题,<lightning:recordEditForm>在提交按钮 <lightning:button variant="brand" label="Submit" onclick="{!c.saveRecord}" type="submit"/> 会有一个默认的提交行为,所以不小心就会出现提交两次的问题,此时在JSController提交方法中设置

// 取消默认的提交行为
event.preventDefault();
就能取消组件的默认提交行为,以下是文档的描述
To customize the behavior of your form when it loads or when data is submitted, use the onload and onsubmit attributes
to specify event handlers. If you capture the submit event and submit the form programmatically,
use event.preventDefault() to cancel the default behavior of the event. This prevents a duplicate form submission.

接下来处理第二个问题:在父对象项目的相关列表中创建部门对象时,如何自动填充父对象的问题.

很遗憾, Salesforce没有提供类似v.recordId这样的属性可以直接获取父级id,甚至在查阅解决方案的时候发现有人提了Idea

技术分享图片

后来有个想法就是说不管怎么设计,相关列表创建子对象记录一定绕不开的就是通过url传递父对象的ID

所以在组件的Helper.js中写一个解析URL的方法

//  获取URL参数
getUrlParameter : function(component,event,name) {
    name = name.replace(/[[]]/g, "\\$&"); 
    var url = window.location.href; 
    var regex = new RegExp("[?&]" + name + "(=1.([^&#]*)|&|#|$)"); 
    var results = regex.exec(url); 
    if (!results) return null; 
    if (!results[2]) return ‘‘; 
    return decodeURIComponent(results[2].replace(/+/g, " "));
}

在组件的Controller.js中打印父级ID

doInit : function(component, event, helper){
    // 获取可能的父级id
    var value = helper.getUrlParameter(component,event,‘inContextOfRef‘);
    var context = JSON.parse(window.atob(value));
    var parentId = context.attributes.recordId;
    console.log(‘*** parentId:‘ + parentId);
},

技术分享图片

判断如果parentId存在,则将Id传递到后台,查询父对象的信息并返回数据进行填充

// 从父级对象创建部门的时候给对应的字段预填充
@AuraEnabled
public static String initFunction(String parentId){
    System.debug(‘*** parentId:‘ + parentId);

    Test_Department__c permiss = new Test_Department__c();
    // 考虑权部门以后除了项目还有其他的主表,这里要检验parentId属于那个对象
    String prefix = Test_Project__c.sobjecttype.getDescribe().getKeyPrefix();
    if (parentId.substring(0, 3) == prefix) {
        permiss.Project_Dep__c = parentId;    
    }    
    System.debug(‘*** 部门:‘ + permiss);
    return JSON.serialize(permiss);
}

 填充效果如下

 技术分享图片

 

以下附完整代码

  1 Lightning组件代码
  2 <aura:component implements="lightning:actionOverride,flexipage:availableForRecordHome,force:hasRecordId,flexipage:availableForAllPageTypes" 
  3                 access="global" 
  4                 description="Test_NewDepartment"
  5                 controller="Test_NewDepartmentCTl">
  6     <aura:attribute name="simpleDepartmentRecord" type="Test_Department__c" default="{‘SobjectType‘:‘Test_Department__c‘}"/>
  7     <aura:attribute name="recordError" type="String"/>
  8 
  9     <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
 10 
 11     <div aura:id="editDialog" role="dialog" tabindex="-1" aria-labelledby="header43" class="slds-modal slds-fade-in-open">
 12         <div class="slds-modal__container">
 13             <div class="slds-modal__header">
 14                 <h2 class="slds-text-heading--medium">{!(v.recordId == null?‘新增‘:‘更新‘) + ‘部门‘}</h2>
 15             </div>
 16             <lightning:messages />
 17             <lightning:recordEditForm objectApiName="Test_Department__c">
 18                 <div class="slds-modal__content slds-p-around--medium slds-wrap" >
 19                     <lightning:input    aura:id="permisssId" 
 20                                         label="用户名" 
 21                                         name="userName" 
 22                                         placeholder="请输入完整的用户名"
 23                                         required="true"        
 24                                         onblur="{!c.checkPackageCode}"
 25                                         value="{!v.simpleDepartmentRecord.Name}" />
 26                     <lightning:inputField   class="customRequired" 
 27                                             aura:id="roleId" 
 28                                             fieldName="Role__c" 
 29                                             value="{!v.simpleDepartmentRecord.Role__c}"/>
 30                     <lightning:inputField   class="customRequired" 
 31                                             fieldName="Email__c" 
 32                                             value="{!v.simpleDepartmentRecord.Email__c}"/>
 33                     <lightning:inputField   class="customRequired" 
 34                                             aura:id="projectLookupId" 
 35                                             fieldName="Project_Dep__c" 
 36                                             value="{!v.simpleDepartmentRecord.Project_Dep__c}"/>
 37                     <lightning:inputField   class="customRequired" 
 38                                             aura:id="reportId" 
 39                                             fieldName="Report_Access__c" 
 40                                             onchange="{!c.changeReport}"
 41                                             value="{!v.simpleDepartmentRecord.Report_Access__c}"/>
 42                     <lightning:inputField   fieldName="Remarks__c" 
 43                                             value="{!v.simpleDepartmentRecord.Remarks__c}"/>
 44                 </div>
 45 
 46                 <div class="slds-modal__footer">                    
 47                     <lightning:button variant="neutral" label="Cancel" onclick="{!c.cancelDialog}" />
 48                     <lightning:button variant="brand"   label="Submit" onclick="{!c.saveRecord}" />
 49                 </div>
 50             </lightning:recordEditForm>
 51         </div>
 52     </div>
 53     <div aura:id="overlay" class="slds-backdrop slds-backdrop--open"></div>
 54 </aura:component>
 55 
 56 Lightning Controller代码
 57 ({
 58     // 新建/更新初始化
 59     // 1、直接点击新建按钮,不填充任何数据
 60     // 2、相关列表创建部门,需要自动填充parentId信息
 61     // 3、更新部门信息
 62     doInit : function(component, event, helper){
 63         var recId = component.get("v.recordId");        
 64         // 获取可能的父级id
 65         var value = helper.getUrlParameter(component,event,‘inContextOfRef‘);
 66         var context = JSON.parse(window.atob(value));
 67         var parentId = context.attributes.recordId;
 68         console.log(‘*** parentId:‘ + parentId);
 69 
 70         // 新建时,如果parentId 不为空,传递到后台get数据
 71         // 更新时传递recId到后台,将数据组转后传回来
 72         if ((!recId && parentId != undefined) || (recId != undefined || recId != null)) {
 73             console.log(‘init‘);
 74             helper.initHelper(component,recId,parentId);
 75         }
 76     },
 77     // 校验用户名数据
 78     checkPackageCode:function(component, event, helper){
 79         // 校验package code
 80         helper.validateuserName(component);
 81     },
 82     //保存数据
 83     saveRecord : function(component, event, helper) {        
 84         var Name = component.get(‘v.simpleDepartmentRecord.Name‘);
 85         if (Name == null || Name == ‘‘) {
 86             helper.showToast(‘提示‘,‘用户名不能为空‘,‘info‘);
 87             return;
 88         }
 89         var Role = component.get(‘v.simpleDepartmentRecord.Role__c‘);
 90         if (Role == null || Role == ‘‘) {
 91             helper.showToast(‘提示‘,‘部门角色不能为空‘,‘info‘);
 92             return;
 93         }
 94         var Email = component.get(‘v.simpleDepartmentRecord.Email__c‘);
 95         if (Email == null || Email == ‘‘) {
 96             helper.showToast(‘提示‘,‘用户邮箱不能为空‘,‘info‘);
 97             return;
 98         }
 99         var Project = component.get(‘v.simpleDepartmentRecord.Project_Dep__c‘);
100         if (Project == null || Project == ‘‘) {
101             helper.showToast(‘提示‘,‘项目不能为空‘,‘info‘);
102             return;
103         }
104         // 取消默认的提交行为
105         event.preventDefault();
106         helper.saveRecord(component);
107     },
108     // 取消弹窗
109     cancelDialog: function(component, event, helper) {
110         var recId = component.get("v.recordId");
111         if (!recId) {
112             var homeEvt = $A.get("e.force:navigateToObjectHome");
113             homeEvt.setParams({
114                 "scope": "Test_Department__c"
115             });
116             homeEvt.fire();
117         } else {
118             helper.navigateTo(component, recId);
119         }
120     },
121     // 复选框的值设定
122     changeReport:function(component, event, helper){
123         var Report_Access = component.get(‘v.simpleDepartmentRecord.Report_Access__c‘);
124         component.set(‘v.simpleDepartmentRecord.Report_Access__c‘, !Report_Access);
125     },
126 })
127 
128 Lightning Helper源码
129 ({
130     // 父级Id不为空的情况下,将id传递到后台,校验数据,然后返回一个新的Test_Department__c进行组装
131     initHelper:function(component,recordId,parentId){
132         var action = component.get("c.initFunction");
133         action.setParams({
134             "parentId": parentId,
135             "recordId":recordId
136         });
137         action.setCallback(this, function(response){
138             var state = response.getState();
139             if (state === ‘SUCCESS‘) {
140                 var result = response.getReturnValue();
141                 var dcObj = JSON.parse(result);
142                 component.set(‘v.simpleDepartmentRecord‘, dcObj);
143             }
144         });
145         $A.enqueueAction(action);
146     },
147     // 校验数据
148     validateuserName : function(component) {
149         var username = component.get(‘v.simpleDepartmentRecord.Name‘);
150         console.log(‘username:‘ + username);
151 
152         var action = component.get("c.validateName");
153         action.setParams({
154             "username": username,
155         });
156         action.setCallback(this, function(response){
157             var state = response.getState();
158             if (state === "SUCCESS") {
159                 var dcObj = JSON.parse(response.getReturnValue());
160                 if (dcObj.errorCode == ‘error‘) {
161                     this.showToast(dcObj.errorTitle,dcObj.errorMsg,dcObj.errorStatu);
162                 }else{
163                     // 将校验数据传递的值进行回写
164                 }
165             }else{                
166                 this.showToast(‘错误‘,‘出现未知错误,请联系管理员!‘,‘error‘);
167                 console.log(‘error:‘ + response.getErrors());
168             }
169         });
170         $A.enqueueAction(action);
171     },
172      // 保存数据
173     saveRecord:function(component){        
174         var action = component.get("c.saveSimpleRecord");
175         action.setParams({
176             "packageRecord": JSON.stringify(component.get("v.simpleDepartmentRecord")),
177         });
178         action.setCallback(this, function(response){
179             var state = response.getState();
180             if (state === "SUCCESS") {                
181                 var dcObj = JSON.parse(response.getReturnValue());
182                 if (dcObj.errorCode == ‘error‘) {
183                     this.showToast(dcObj.errorTitle,dcObj.errorMsg,dcObj.errorStatu);
184                 }else{
185                     this.showToast(dcObj.errorTitle,dcObj.errorMsg,dcObj.errorStatu);
186                     window.location.reload(); 
187                     window.location.href = "/"+ dcObj.dcpId;
188                 }
189             }else{                
190                 this.showToast(‘错误‘,‘出现未知错误,请联系管理员!‘,‘error‘);
191                 console.log(‘error:‘ + response.getErrors());
192             }
193         });
194         $A.enqueueAction(action);
195     },
196     navigateTo: function(component, recId) {
197         // 页面跳转到新记录
198         var navEvt = $A.get("e.force:navigateToSObject");
199         navEvt.setParams({
200             "recordId": recId
201         });
202         navEvt.fire();
203     },
204     // type = ‘error‘, ‘warning‘, ‘success‘, or ‘info‘.default is ‘other‘
205     showToast : function(title, message, type){
206         var toastEvent = $A.get("e.force:showToast");
207         toastEvent.setParams({
208             "title": title,
209             "message": message,
210             "type" : type
211         });
212         toastEvent.fire();
213     },
214     //  获取URL参数
215     getUrlParameter : function(component,event,name) {
216         name = name.replace(/[[]]/g, "\\$&"); 
217         var url = window.location.href; 
218         var regex = new RegExp("[?&]" + name + "(=1.([^&#]*)|&|#|$)"); 
219         var results = regex.exec(url); 
220         if (!results) return null; 
221         if (!results[2]) return ‘‘; 
222         return decodeURIComponent(results[2].replace(/+/g, " "));
223     }
224 })
225 
226 最后的Apex类源码
227 /*************************************************************************************************
228 * Name: Test_NewDepartmentCtl
229 * Object: Test_Department__c
230 * Purpose: Override create/edit department, validate username by service callout
231 * Author: Ricardo Lu
232 * Create Date: 2019-01-19
233 * Modify History:
234 *         2019-01-19        Create this class 
235 *************************************************************************************************/
236 public with sharing class Test_NewDepartmentCTl {
237     // 从父级对象创建部门的时候给对应的字段预填充
238    @AuraEnabled
239    public static String initFunction(String parentId,String recordId){
240         System.debug(‘*** parentId:‘ + parentId + ‘,recordId:‘ + recordId);
241 
242         Test_Department__c department = new Test_Department__c();
243         // 更新
244         if (recordId != null) {
245             department = [SELECT Name,Role__c,Email__c,Project_Dep__c,Report_Access__c,Remarks__c FROM 
                  Test_Department__c WHERE Id =:recordId]; 246 }else if (parentId != null) { 247 // 考虑部门以后除了项目还有其他的主表,这里要检验parentId属于那个对象 248 String prefix = Test_Project__c.sobjecttype.getDescribe().getKeyPrefix(); 249 if (parentId.substring(0, 3) == prefix) { 250 department.Project_Dep__c = parentId; 251 } 252 } 253 System.debug(‘*** 部门:‘ + department); 254 return JSON.serialize(department); 255 } 256 257 @AuraEnabled 258 public static String validateName(String username){ 259 ReturnWrapper wrapper = new ReturnWrapper(); 260 if (username == null || username == ‘‘) { 261 wrapper.errorCode = ‘error‘; 262 wrapper.errorMsg = ‘请输入用户名‘; 263 wrapper.errorTitle = ‘提示‘; 264 wrapper.errorStatu = ‘warning‘; 265 return JSON.serialize(wrapper); 266 } 267 268 try { 269 // 对username进行需要的接口校验 270 } catch(Exception ex) { 271 System.debug(‘*** line:‘ + ex.getLineNumber()); 272 System.debug(‘*** messgae:‘ + ex.getMessage()); 273 wrapper.errorCode = ‘error‘; 274 wrapper.errorTitle = ‘错误‘; 275 wrapper.errorStatu = ‘error‘; 276 wrapper.errorMsg = ‘校验错误,请联系管理员!‘; 277 return JSON.serialize(wrapper); 278 } 279 wrapper.errorCode = ‘success‘; 280 wrapper.errorTitle = ‘成功‘; 281 wrapper.errorStatu = ‘success‘; 282 wrapper.errorMsg = ‘‘; 283 return JSON.serialize(wrapper); 284 } 285 @AuraEnabled 286 public static String saveSimpleRecord(String packageRecord){ 287 ReturnWrapper wrapper = new ReturnWrapper(); 288 289 Map<String, Object> objectField = new Map<String, Object>(); 290 try{ 291 292 objectField = (Map<String, Object>) JSON.deserializeUntyped(packageRecord); 293 294 // 部门Id 295 String packageId = null; 296 if(objectField.get(‘Id‘) != null){ 297 packageId = (String)objectField.get(‘Id‘); 298 } 299 300 // 部门用户名 301 String packageName = (String)objectField.get(‘Name‘); 302 303 // 用户名不能为空 304 if(packageName == null || packageName.trim().length() == 0){ 305 wrapper.errorCode = ‘error‘; 306 wrapper.errorMsg = ‘用户名称不能为空!‘; 307 wrapper.errorTitle = ‘提示‘; 308 wrapper.errorStatu = ‘warning‘; 309 return JSON.serialize(wrapper); 310 } 311 312 // 部门角色 313 String packageRole = (String)objectField.get(‘Role__c‘); 314 // Email__c 315 String packageEmail = String.valueOf(objectField.get(‘Email__c‘)); 316 // Project_Dep__c 317 Object packageProject = objectField.get(‘Project_Dep__c‘); 318 String projectId = String.valueOf(packageProject); 319 projectId = projectId.replace(‘(‘,‘‘).replace(‘)‘,‘‘); 320 321 // Report_Access__c:是否允许报表 322 Boolean packageReportAccess = false; 323 if (objectField.get(‘Report_Access__c‘) != null) { 324 packageReportAccess = (Boolean)objectField.get(‘Report_Access__c‘); 325 } 326 // Remarks__c 327 String packageRemarks = (String)objectField.get(‘Remarks__c‘); 328 329 // 新建 or update 部门 330 Test_Department__c dcp = new Test_Department__c(); 331 dcp.Id = packageId; 332 dcp.Name = packageName; 333 dcp.Role__c = packageRole; 334 dcp.Project_Dep__c = projectId; 335 dcp.Email__c = packageEmail; 336 dcp.Report_Access__c = packageReportAccess; 337 dcp.Remarks__c = packageRemarks; 338 upsert dcp; 339 340 wrapper.errorCode = ‘success‘; 341 wrapper.errorMsg = ‘恭喜,部门保存成功!‘; 342 wrapper.errorTitle = ‘成功‘; 343 wrapper.errorStatu = ‘success‘; 344 wrapper.dcpId = dcp.Id; 345 346 return JSON.serialize(wrapper); 347 } catch(Exception ex) { 348 System.debug(‘error:‘ + ex.getLineNumber()); 349 System.debug(‘error:‘ + ex.getMessage()); 350 wrapper.errorCode = ‘error‘; 351 wrapper.errorMsg = ‘保存错误,请联系管理员!‘; 352 wrapper.errorTitle = ‘错误‘; 353 wrapper.errorStatu = ‘error‘; 354 return JSON.serialize(wrapper); 355 } 356 } 357 358 public class ReturnWrapper{ 359 public String errorCode{get;set;} 360 public String errorTitle{get;set;} 361 public String errorStatu{get;set;} 362 public String errorMsg {get;set;} 363 public String dcpId{get;set;} 364 } 365 }

 




以上是关于Salesforce Lightning开发学习重写新建/更新按钮的主要内容,如果未能解决你的问题,请参考以下文章

salesforce零基础学习(九十五)lightning out

salesforce lightning零基础学习 Aura Js 浅谈一: Component篇

salesforce lightning零基础学习Lightning Data Service(LDS)

学习Salesforce | Platform Developer Ⅰ 平台初级开发认证考试指南及备考资源

salesforce lightning零基础学习(十三) 自定义Lookup组件(Single & Multiple)

Salesforce LWC学习(十九) 针对 lightning-input-field的label值重写