目录
应用场景
- 重写对象的新建和编辑页面时,动态拉去系统配置的标准页面布局元素
参数说明
- <c:GetRecordLayout sobjectAPI="Case" recordMap="{!v.recordMap}" layout="{!v.layout}" fieldChange="{!c.fieldChange}" mode="create" recordTypeId="{!v.recordTypeId}" displayDensity="Comfy"/>
- sobjectAPI对象API
- recordMap封装初始值,可设置value(字段值),type(字段类型),formatter(字段格式),standard(是否使用inputField展示字段),helptext(帮助文本,自动拉取), despatchChange(是否监听字段变化),checkError(是否出错,默认false),errorMsg(报错消息),apiName(字段API)
- Layout为记录字段的信息,由子组件返回至主组件中进行校验数据,保存数据等操作
- fieldChange为字段变更事件
- Mode默认为create,表示拉去页面布局的类型(view,edit,create)view暂不支持,有标准的组件可用。
- recordTypeId记录类型id,为空的话,取当前用户的默认记录类型
- displayDensity布局类型(comfy,Compact)
缺点限制
- 查找字段没有新建记录页面
- 字段依赖关系的链接无法显示(或可优化)
- DateTime类型字段不会显示Date ,Time标签(或可优化)
- 必填的选项列表字段初始化时页面会显示字段必填提示(还未找到解决方案)
- 未知缺点
展示页面示例
组件源码
Page
getSessionId.page
<apex:page >
Start_Of_Session_Id{!$Api.Session_ID}End_Of_Session_Id
</apex:page>
Controller
ResponseBody.cls
public class ResponseBody {
@AuraEnabled
public String Status{get;set;}
@AuraEnabled
public String Msg{get;set;}
@AuraEnabled
public Object Entity{get;set;}public ResponseBody(String Status, String Msg, Object Entity){
this.Status = Status;
this.Msg = Msg;
this.Entity = Entity;
}
}
GetRecordLayout.cls
public with sharing class GetRecordLayout
{
/**
* [getRecordLayoutInformation 通过请求rest获取layout布局元素]
* @Author Faith
* @DateTime 2019-05-15T12:01:03+0800
* @Description
* @param condition [请求rest的参数]
* @return [description]
*/
@AuraEnabled
public static ResponseBody getRecordLayoutInformation(String condition)
{
try
{
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setTimeout(120000);
request.setMethod('GET');
request.setHeader('Authorization', 'Bearer '+getUserSessionId());
request.setEndpoint(URL.getSalesforceBaseUrl().toExternalForm()+condition);
HttpResponse response=http.send(request);
return new ResponseBody('SUCCESS','',response.getBody());
}catch(Exception e)
{
return new ResponseBody('ERROR',e.getMessage(),'');
}
}
/**
* [getUserSessionId 获取当前用户的sessionid]
* @Author Faith
* @DateTime 2019-05-15T12:03:18+0800
* @Description
* @return [description]
*/
public static String getUserSessionId()
{
return Page.GetSessionId.getContent().toString().substringBetween('Start_Of_Session_Id', 'End_Of_Session_Id');
}
/**
* [getFieldMap 获取字段信息并封装实体类]
* @Author Faith
* @DateTime 2019-05-15T14:18:51+0800
* @Description
* @param sObjectName [description]
* @return [description]
*/
public static Map<String,FieldEntry> getFieldMap(String sObjectName)
{
Map<String, FieldEntry> fieldEntryMap=new Map<String, FieldEntry>();
if (Schema.getGlobalDescribe().containsKey(sObjectName))
{
Map<String, Schema.SobjectField> sObjectFieldsMap=Schema.getGlobalDescribe().get(sObjectName).getDescribe().fields.getMap();
for(String fieldName:sObjectFieldsMap.keySet())
{
Schema.DescribeFieldResult fieldDescribe=sObjectFieldsMap.get(fieldName).getDescribe();
fieldEntryMap.put(fieldDescribe.getName(),new FieldEntry(fieldDescribe.getName(),fieldDescribe.getMaskType(),fieldDescribe.getInlineHelpText()));
}
}
return fieldEntryMap;
}
/**
* 字段信息封装实体类
*/
public class FieldEntry
{
@AuraEnabled
private String value{get;set;}
@AuraEnabled
private String type{get;set;}
@AuraEnabled
private String formatter{get;set;}
@AuraEnabled
private Boolean standard{get;set;}
@AuraEnabled
private String helptext{get;set;}
@AuraEnabled
private Boolean despatchChange{get;set;}
@AuraEnabled
private Boolean checkError{get;set;}
@AuraEnabled
private String errorMsg{get;set;}
@AuraEnabled
private String apiName{get;set;}
public FieldEntry(String apiName,String type,String helptext)
{
this.value=null;
this.apiName=apiName;
this.errorMsg=null;
this.formatter=null;
this.standard=true;
this.checkError=false;
this.despatchChange=false;
this.type=type;
this.helptext=helptext;
}
}
}
Aura
ElementChangeEvent.evt
<aura:event type="APPLICATION" description="ElementChangeEvent">
<aura:attribute name="element" type="Object" access="public"/>
</aura:event>
GetRecordLayout.cmp
<aura:component implements="flexipage:availableForAllPageTypes" controller="GetRecordLayout">
<!-- SobjectAPI -->
<aura:attribute name="sobjectAPI" type="String" default="Case"/>
<!-- The access mode for the record(create,edit,view) -->
<aura:attribute name="mode" type="String" default="create"/>
<!-- The ID of the record type (RecordType object) for the new record,If not provided, the default record type is used. -->
<aura:attribute name="recordTypeId" type="String"/>
<!-- The layout type for the record. One of these values(Compact,Full) -->
<aura:attribute name="layoutType" type="String" default="Full"/>
<!-- The layout display size for the record. One of these values:Large,Medium,Small -->
<aura:attribute name="formFactor" type="String" default="Large"/>
<!-- layout information -->
<aura:attribute name="layout" type="Object" />
<!-- the record init values(fieldAPI=>values) -->
<aura:attribute name="recordMap" type="Map"/>
<!-- loading -->
<aura:attribute name="showSpinner" type="Boolean" default="true" />
<!-- DISPLAY DENSITY(Comfy,Compact) -->
<aura:attribute name="displayDensity" type="String" default="Comfy"/>
<!-- registerEvent -->
<aura:registerEvent name="fieldChange" type="c:ElementChangeEvent" />
<!-- init method -->
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:if isTrue="{!v.showSpinner}"><lightning:spinner /></aura:if>
<div style="background-color:white;padding:5px;">
<lightning:recordEditForm objectApiName="{!v.sobjectAPI}" recordTypeId="{!v.recordTypeId}">
<aura:iteration items="{!v.layout.sections}" var="section" indexVar="sectionIndex">
<div class="slds-section slds-is-open">
<aura:if isTrue="{!section.useHeading}">
<h3 class="slds-section__title slds-theme_shade">
<span class="slds-truncate slds-p-horizontal_small" title="{!section.heading}">{!section.heading}</span>
</h3>
</aura:if>
<div aria-hidden="false" class="slds-section__content">
<div class="slds-form" role="list">
<aura:iteration items="{!section.layoutRows}" var="row" indexVar="rowIndex">
<div class="slds-form__row">
<aura:iteration items="{!row.layoutItems}" var="item" indexVar="itemIndex">
<div class="slds-form__item" role="listitem">
<div class="{!'slds-form-element slds-form-element_'+if(v.displayDensity=='Comfy','stacked','horizontal')+' slds-is-editing'}">
<aura:if isTrue="{!and(item.layoutComponents.length>1)}">{!item.label}<br/></aura:if>
<aura:iteration items="{!item.layoutComponents}" var="element" indexVar="elementIndex">
<aura:if isTrue="{!and(element.apiName!=null,element.componentType=='Field')}">
<label class="slds-form-element__label">
<aura:if isTrue="{!item.required}"><abbr class="slds-required" title="required">*</abbr></aura:if>
{!if(item.layoutComponents.length==1,item.label,element.label)}
</label>
<aura:if isTrue="{!element.helptext!=null}">
<lightning:helptext content="{!element.helptext}"/>
</aura:if>
<aura:if isTrue="{!element.standard}">
<lightning:inputField fieldName="{!element.apiName}" disabled="{!if(mode=='create',element.editableForNew,element.editableForUpdate)}" variant="label-hidden" class="{!'InputFieldLabelHide'+if(element.checkError,' slds-has-error','')}" value="{!element.value}" οnchange="{!c.changeHandler}" title="{!sectionIndex+'#'+rowIndex+'#'+itemIndex+'#'+elementIndex}"/>
<aura:set attribute="else">
<lightning:input type="{!element.type}" formatter="{!element.formatter}" value="{!element.value}" class="if(element.checkError,' slds-has-error','')}" οnchange="{!c.changeHandler}" title="{!sectionIndex+'#'+rowIndex+'#'+itemIndex+'#'+elementIndex}" variant="label-hidden"/>
</aura:set>
</aura:if>
<aura:if isTrue="{!element.checkError}">
<div class="slds-form-element__help" style="color:#DC143C;">{!element.errorMsg}</div>
</aura:if>
</aura:if>
</aura:iteration>
</div>
</div>
</aura:iteration>
</div>
</aura:iteration>
</div>
</div>
</div>
</aura:iteration>
</lightning:recordEditForm>
</div>
</aura:component>
GetRecordLayout.css
.THIS {}
.THIS .InputFieldLabelHide .slds-form-element__label,.THIS .InputFieldLabelHide lightning-helptext{display:none}
.THIS input::-webkit-outer-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
margin: 0;
}
.THIS input::-webkit-inner-spin-button {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
margin: 0;
}
.THIS input[type=number]::-webkit-inner-spin-button,
.THIS input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
GetRecordLayoutController.js
({
doInit:function(cmp, event, helper)
{
helper.getRecordLayoutInformation(cmp, event, helper);
},
changeHandler:function(cmp,event,helper)
{
var layout=cmp.get("v.layout");
var indexInformation=event.getSource().get("v.title");
var result=indexInformation.split("#");
var sectionIndex=result[0],rowIndex=result[1],itemIndex=result[2],elementIndex=result[3];
var value=event.getSource().get("v.value");
if(layout.sections[sectionIndex].layoutRows[rowIndex].layoutItems[itemIndex].required&&(value==null||value==""))
{
layout.sections[sectionIndex].layoutRows[rowIndex].layoutItems[itemIndex].layoutComponents[elementIndex].checkError=true;
}
else
{
layout.sections[sectionIndex].layoutRows[rowIndex].layoutItems[itemIndex].layoutComponents[elementIndex].checkError=false;
}
if(layout.sections[sectionIndex].layoutRows[rowIndex].layoutItems[itemIndex].layoutComponents[elementIndex].despatchChange)
{
helper.despatchFieldChangeEvent(cmp,result);
}
cmp.set("v.layout", layout);
},
})
GetRecordLayoutHelper.js
({
getRecordLayoutInformation:function(cmp, event, helper)
{
console.log("getRecordLayoutInformation");
var action=cmp.get("c.getRecordLayoutInformation");
var condition="/services/data/v45.0/ui-api/layout/"+cmp.get("v.sobjectAPI")+"?mode="+cmp.get("v.mode");
var recordTypeId=cmp.get("v.recordTypeId");
if(recordTypeId!=null&&recordTypeId!='') condition+="&recordTypeId="+recordTypeId;
action.setParams({"condition":condition});
action.setCallback(this, function (response)
{
if (cmp.isValid()&&response.getState()==="SUCCESS")
{
var ResponseBody = response.getReturnValue();
console.log(ResponseBody);
var Entity = ResponseBody.Entity;
var Msg = ResponseBody.Msg;
var Status = ResponseBody.Status;
if(Status=="SUCCESS")
{
var layout = JSON.parse(Entity);
var recordMap=cmp.get("v.recordMap");
var sections=layout.sections;
for(var i=0; i<sections.length;i++)
{
var layoutRows=sections[i].layoutRows;
for(var j=0;j<layoutRows.length;j++)
{
var layoutItems=layoutRows[j].layoutItems;
for(var n=0;n<layoutItems.length;n++)
{
var layoutComponents=layoutItems[n].layoutComponents;
for(var m=0;m<layoutComponents.length;m++)
{
if(layoutComponents[m].apiName==null||layoutComponents[m].componentType!='Field') continue;
if(recordMap!=null&&recordMap[layoutComponents[m].apiName]!=undefined)
{
layoutComponents[m].value=recordMap[layoutComponents[m].apiName].value;
layoutComponents[m].type=recordMap[layoutComponents[m].apiName].type;
layoutComponents[m].formatter=recordMap[layoutComponents[m].apiName].formatter;
layoutComponents[m].standard=recordMap[layoutComponents[m].apiName].standard;
layoutComponents[m].helptext=recordMap[layoutComponents[m].apiName].helptext;
}
else
{
layoutComponents[m].value=null;
layoutComponents[m].type="inputField";
layoutComponents[m].formatter=null;
layoutComponents[m].standard=true;
layoutComponents[m].helptext=null;
layoutComponents[m].despatchChange=false;
}
layoutComponents[m].checkError=false;
layoutComponents[m].errorMsg="This field is required";
}
layoutItems[n].layoutComponents=layoutComponents;
}
layoutRows[j].layoutItems=layoutItems;
}
sections[i].layoutRows=layoutRows;
}
layout.sections=sections;
console.log(layout);
cmp.set("v.layout", layout);
cmp.set("v.showSpinner", false);
}
else alert(Msg);
}
else alert(response.getErrors());
});
$A.enqueueAction(action);
},
despatchFieldChangeEvent: function(cmp,element){
var compEvent = cmp.getEvent("fieldChange");
compEvent.setParams({"element": element});
compEvent.fire();
},
/*despatchSearchChangeEvent: function(cmp,element){
var compEvent = cmp.getEvent("searchChange");
compEvent.setParams({"element": element});
compEvent.fire();
}*/
})
源码链接
链接: https://pan.baidu.com/s/1GUwPaS44ELFLN6wbmudhmw 提取码: nkhr
CreateRecord.cmp为调用示例