在18年下半年期间,在一个通信项目开发期间用到了不少公用组件,其中部分有自己亲身参与编码的一些成果,这里总结一下,希望能为以后积累丰富经验,如果有不合理的地方也欢迎各位评论指正。
模糊搜索单选组件,包括:
Aura:
AutoMatchPickList
SearchInput
CommonEvent.evt
AutoMatchListEvt.evt
Apex:
AutoMatchPickListCtl.cls
AutoMatchPickList.cmp
<!--
-
- 这是具有相似查询功能的搜索框组件
-
- @return {string} value - 通过comp.get('v.value')获得最终选择的值
-->
<aura:component controller="AutoMatchPickListCtl" access="global">
<!-- 需要查找的对象 -->
<aura:attribute name="objectType" type="String" default="" required="true"/>
<!-- 需要模糊匹配的字段名称 -->
<aura:attribute name="fieldList" type="String" default="" required="true"/>
<!-- 需要显示在下拉框中作为标签的字段 -->
<aura:attribute name="labelField" type="String" default="" required="true"/>
<!-- 查询过滤条件 -->
<aura:attribute name="filterCondition" type="String" default=""/>
<aura:attribute name="isMatch" type="Boolean" default="true"/>
<aura:attribute name="label" type="String" default=""/>
<aura:attribute name="disabled" type="Boolean" default="false"/>
<aura:attribute name="placeholder" type="String" default=""/>
<aura:attribute name="required" type="Boolean" default="false"/>
<aura:attribute name="options" type="List" default="[]"/>
<aura:attribute name="inputValue" type="String" default=""/>
<aura:attribute name="class" type="String" default=""/>
<!--通过该属性可以获得最终选择的值,-->
<aura:attribute name="value" type="String" default=""/>
<!--通过该回掉函数也可以获得最终选择的值-->
<aura:attribute name="onSelect" type="Aura.action" default="{!c.emptyAction}"/>
<!--input行内样式 create by eric -->
<aura:attribute name="inputClass" type="String" default=""/>
<!--手动实现表单验证相关的方法-->
<aura:method name="checkValidity"/>
<aura:method name="reportValidity"/>
<aura:attribute name="scopefilter" type="String" default="" />
<aura:handler action="{!c.init}" name="init" value="{!this}" />
<!-- 动态传入条件时,重新执行 init -->
<aura:handler action="{!c.init}" name="change" value="{!v.filterCondition}" />
<c:SearchInput class="{!v.class}" aura:id="searchInput" required="{!v.required}" disabled="{!v.disabled}" label="{!v.label}" value="{!v.value}" options="{!v.options}" onSearch="{!c.handleSearch}" onSelect="{!v.onSelect}" placeholder="{!v.placeholder}" inputValue="{!v.inputValue}" isMatch="{!v.isMatch}" inputClass="{!v.inputClass}"/>
</aura:component>
AutoMatchPickListController.js
({
handleSearch:function(comp,event,helper)
{
var data=event.getParam('data');
var searchValue=data.value;
//调用服务器接口
var action = comp.get("c.returnPicklistItemMap");
action.setParams({
searchValue:searchValue,
objectType:comp.get('v.objectType'),
fieldlist:comp.get('v.fieldList'),
filterCondition:comp.get('v.filterCondition'),
labelField:comp.get('v.labelField'),
scopeCondtion:comp.get('v.scopefilter')
});
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
// Alert the user with the value returned
// from the server
var returnValue=response.getReturnValue();
var options=JSON.parse(returnValue);
comp.set('v.options',options);
}
else if (state === "INCOMPLETE") {
}
else if (state === "ERROR") {
}
});
$A.enqueueAction(action);
},
init:function(comp,event,helper)
{
//调用服务器接口
var action = comp.get("c.returnPicklistItemMap");
action.setParams({
searchValue:'',
objectType:comp.get('v.objectType'),
fieldlist:comp.get('v.fieldList'),
filterCondition:comp.get('v.filterCondition'),
labelField:comp.get('v.labelField')
});
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
// Alert the user with the value returned
// from the server
var returnValue=response.getReturnValue();
var options=JSON.parse(returnValue);
comp.set('v.options',options);
}
});
$A.enqueueAction(action);
},
//空的action,作为onSelect的默认值,最优?
emptyAction:function(comp,event,helper)
{
// console.log('On input--->'+comp.get('v.inputValue'));
// console.log('on options--->'+JSON.stringify(comp.get('v.options')));
},
checkValidity: function(comp, event, helper) {
var isValidity = comp.find('searchInput').checkValidity();
if(isValidity){
var isRequired = comp.get('v.required');
var inputValue = comp.get('v.value');
if(isRequired &&
(inputValue == null || inputValue == undefined || inputValue == '')){
isValidity = false;
}
}
return isValidity;
// return comp.find('searchInput').checkValidity();
},
reportValidity: function(comp, event, helper) {
comp.find('searchInput').reportValidity();
}
})
AutoMatchPickListCtl.cls
public without sharing class AutoMatchPickListCtl {
/*接口入参:
String searchValue,
String objectType,
List<String> fieldlist
filterCondition:Sample[{"FieldName": "LastName","Condition": "=","Value": "Ray"}]*/
@AuraEnabled
public static String returnPicklistItemMap(String searchValue, String objectType, String fieldlist, String filterCondition, String labelField,String scopeCondtion) {
List<SelectItem> returnPickList = new List<SelectItem>();
String searchquery = '';
System.debug(LoggingLevel.INFO, '*** searchValue: ' + searchValue);
// Search query by specific
if (String.isNotBlank(searchValue)) {
searchquery='FIND {1} IN ALL FIELDS RETURNING {2} ({3}{4})';
searchValue = '\'*' + searchValue + '*\'';
searchquery = searchquery.replace('{1}', searchValue);
searchquery = searchquery.replace('{2}', objectType);
searchquery = searchquery.replace('{3}', fieldlist);
searchquery = searchquery.replace('{4}', generateCondition(filterCondition,scopeCondtion));
System.debug(LoggingLevel.INFO, '*** searchquery: ' + searchquery);
List<List<SObject>>searchList=search.query(searchquery);
if (searchList.size() > 0) {
List<SObject> oblist = searchList[0];
for (SObject sobj : oblist) {
SelectItem pitem = new SelectItem();
pitem.label = (String)sobj.get(labelField);
pitem.value = (String)sobj.get('Id');
if (String.isNotBlank(pitem.label)) {
returnPickList.add(pitem);
}
}
}
}
// Setting Defalult select result
if (String.isBlank(searchValue)) {
searchquery='SELECT {1} FROM {2} {3}';
searchquery = searchquery.replace('{1}', fieldlist);
searchquery = searchquery.replace('{2}', objectType);
searchquery = searchquery.replace('{3}', generateCondition(filterCondition,scopeCondtion));
searchquery = searchquery + (' limit 100');
System.debug(LoggingLevel.INFO, '*** searchquery: ' + searchquery);
List<SObject> sobjectlist = Database.query(searchquery);
if (sobjectlist.size() > 0) {
for (SObject sobj : sobjectlist) {
SelectItem pitem = new SelectItem();
pitem.label = (String)sobj.get(labelField);
pitem.value = (String)sobj.get('Id');
if (String.isNotBlank(pitem.label)) {
returnPickList.add(pitem);
}
}
}
}
return JSON.serialize(returnPickList);
}
public static String generateCondition(String filterCondition, String scopeCondtion) {
System.debug(LoggingLevel.INFO, '*** filterCondition: ' + filterCondition);
System.debug(LoggingLevel.INFO, '*** scopeCondtion: ' + scopeCondtion);
if (String.isNotBlank(filterCondition)) {
List<Condtion> condtions = (List<Condtion>)JSON.deserialize(filterCondition, List<Condtion>.class);
if (String.isNotBlank(scopeCondtion)) {
List<Condtion> condtions2 = (List<Condtion>)JSON.deserialize(scopeCondtion, List<Condtion>.class);
condtions.addAll(condtions2);
}
String returnFitler = ' WHERE ';
Integer i=1;
for (Condtion con : condtions) {
if (i>1) {
returnFitler += ' And';
}
if (con.Condtion != 'IN' && con.Value!='true') {
returnFitler += (' ' + con.FieldName +' ' + con.Condtion + ' \''+ con.Value +'\'' + ' ');
}else if(con.Value=='true'){
returnFitler += (' ' + con.FieldName +' ' + con.Condtion + Boolean.valueOf(con.Value) + ' ');
}else {
returnFitler += (' ' + con.FieldName +' ' + con.Condtion + ' '+ con.Value.replace('"', '\'') +' ' + ' ');
}
i++;
}
return returnFitler;
}
return '';
}
public class Condtion {
public String FieldName {get; set;}
public String Condtion {get; set;}
public String Value {get;set;}
}
public class SelectItem {
@AuraEnabled public String label;
@AuraEnabled public String value;
}
}
SearchInput.cmp
<!--
-
- 公共的搜索框组件
-
- @param {string} label
-
- @param {boolean} disabled
-
- @param {string} placeholder
-
- @param {array} options - 搜索到的选项列表,
- 格式1:[{label:'中国',value:'zh'},{label:'法国',value:'fr'}]
- 格式2:['中国','法国'],此时label和value的值一样
-
- @param {function} onSearch - 输入搜索时触发
-
- @param {function} onSelect - 选中值时触发
-
- @return {string} value - 通过comp.get('v.value')获得最终选择值
-->
<aura:component description="SearchInput">
<aura:attribute name="label" type="String" default=""/>
<aura:attribute name="disabled" type="Boolean" default="false"/>
<aura:attribute name="placeholder" type="String" default=""/>
<aura:attribute name="options" type="List" default="[]"/>
<aura:attribute name="required" type="Boolean" default="false"/>
<aura:attribute name="class" type="String" default=""/>
<!--input的值-->
<aura:attribute name="inputValue" type="String" default=""/>
<!--最终选择的值,和input的值不一样,返回的是value不是label-->
<aura:attribute name="value" type="String" default=""/>
<!--下拉列表是否可见-->
<aura:attribute name="selectVisible" type="Boolean" default="false"/>
<!--是否正在查询-->
<aura:attribute name="isSearching" type="Boolean" default="false"/>
<aura:attribute name="isMatch" type="Boolean" default="true"/>
<!--input行内样式 -->
<aura:attribute name="inputClass" type="String" default=""/>
<aura:registerEvent name="onSearch" type="c:CommonEvent"/>
<aura:registerEvent name="onSelect" type="c:CommonEvent"/>
<aura:registerEvent name="relatedAffect" type="c:AutoMatchListEvt"/>
<aura:handler name="init" value="{!this}" action="{!c.init}"/>
<aura:handler name="change" value="{!v.options}" action="{!c.optionsUpdated}"/>
<!--手动实现表单验证相关的方法-->
<aura:method name="checkValidity"/>
<aura:method name="reportValidity"/>
<div class="{!v.class}">
<form class="search-input__form" autocomplete="false" onsubmit="{!c.preventDefault}">
<lightning:input aura:id="input" required="{!v.required}" type="text" label="{!v.label}" disabled="{!v.disabled}"
isLoading="{!v.isSearching}" class="{!v.inputClass}"
placeholder="{!v.placeholder}" value="{!v.inputValue}" onfocus="{!c.handleFocus}"
onchange="{!c.changeInputValue}" onblur="{!c.handleBlur}"/>
<lightning:icon iconName="utility:search" size="xx-small"/>
</form>
<aura:if isTrue="{!v.options.length > 0}">
<ul onmousedown="{!c.selectOption}" style="{!'display:'+(v.selectVisible?'block':'none')}">
<aura:iteration items="{!v.options}" var="option">
<!--将onclick事件换成onmousedown事件,以消除input的onblur触发时,layout变化,导致li的onclick无法正常侦听的问题-->
<li data-value="{! option.value!=undefined?option.value:option}" role="option" aria-selected="true">
{! option.label!=undefined?option.label:option}
</li>
</aura:iteration>
</ul>
</aura:if>
</div>
</aura:component>
SearchInput.css
.THIS {
position: relative;
}
.THIS ul
{
max-height: 10rem;
overflow-y: auto;
border: 1px solid rgb(217, 219, 221);
background-color: #fff;
cursor: pointer;
width: 100%;
position: absolute;
left: 0;
z-index: 99;/*因为后台页面的header的z-index为100,为了避免滚动时叠在UI上,现在将z-index的值改成99*/
}
.THIS .label-input{
display: inline-block;
margin-left: 9px;
}
.THIS .label-box{
margin-bottom: 5px;
}
.THIS li
{
line-height: 2rem;
text-align: center;
}
.THIS li:hover
{
background-color: #eeeeee;
}
/*当disable时,手动移除输入框的清除按钮*/
.THIS input[disabled] ~ div
{
display: none;
}
/*假的search按钮的样式*/
.THIS lightning-icon
{
position: absolute;
left: 0.75rem;
top: 1.8rem;
}
.THIS lightning-icon svg
{
fill: rgb(171, 173, 176);
}
.THIS lightning-input input
{
padding-left: 2rem;
}
SearchInputController.js
({
//尝试根据options和value确定inputValue
init:function(comp,event,helper)
{
console.log('初始value',comp.get('v.value'));
helper.getInputValue(comp);
},
optionsUpdated:function(comp,event,helper)
{
comp.set('v.isSearching', false);
helper.getInputValue(comp);
},
selectOption:function(comp,event,helper)
{
var li=event.target;
//确定是li标签
if(li.tagName==='LI')
{
var label=li.textContent;
var value=li.getAttribute('data-value');
//更新inputValue
comp.set('v.inputValue',label);
//更新value
comp.set('v.value',value);
comp.set('v.selectVisible',false);
helper.fireEvent(comp,'onSelect','v.value');
var evt = $A.get("e.c:AutoMatchListEvt");
evt.fire();
}
//点击的scrollbar
if(li.tagName==='UL')
{
helper.clickScrollbar=true;
}
},
handleFocus:function(comp,event,helper)
{
comp.set('v.selectVisible',true);
},
changeInputValue:function(comp,event,helper)
{
//只要触发onChange,最终选择的值,重置为空
comp.set('v.value','');
comp.set('v.isSearching', true);
comp.set('v.selectVisible',true);
//onSelect回传v.value的值
helper.fireEvent(comp,'onSelect','v.value');
//onSearch回传v.inputValue的频率降低,将id挂靠到comp上,也可以挂靠到helper上(ie上不要使用箭头函数!)
var delay = 400;
//第一次立即执行
if (comp.id === undefined) {
helper.fireEvent(comp,'onSearch','v.inputValue');
comp.id = setTimeout(function(){
comp.id = undefined;
}, delay);
} else {
clearTimeout(comp.id);
comp.id = setTimeout(function(){
helper.fireEvent(comp,'onSearch','v.inputValue');
//为下一次立即执行做准备
comp.id = undefined;
}, delay);
}
},
handleBlur:function(comp,event,helper)
{
//如果是点击的scrollbar触发的input的blur事件,不认可
if(helper.clickScrollbar===true){
helper.clickScrollbar=false;
return;
}
//input失去焦点时,有可能是在选择下拉列表项,现在li侦听的是mousedown,与blur事件不冲突
comp.set('v.selectVisible',false);//accesss设置成private后,这段代码不起作用?
comp.reportValidity();
if (comp.get("v.inputValue") == '' || comp.get("v.inputValue") == null) {
var evt = $A.get("e.c:AutoMatchListEvt");
evt.fire();
}
},
checkValidity:function(comp, event, helper) {
var b=true;
var selected=true;
if(comp.get('v.required'))
{
selected=false;
var value=comp.get('v.value');
var inputValue=comp.get('v.inputValue');
var options=comp.get('v.options');
//有可选数据,disable状态下,数据有时候并不全
if(options)
{
//确保inputValue和value匹配,是从可选数据里选出来的
for(var i=0,len=options.length;i<len;i++)
{
var option=options[i];
var _value=option.value===undefined?option:option.value;
var _inputValue=option.label===undefined?option:option.label;
//不用===做判断,
if(value==_value&&inputValue==_inputValue)
{
selected=true;
break;
}
}
}
//再做一次判断
if(!selected)
{
if(value!=='')
{
selected=true;
}
}
b=selected;
}
comp.set('v.isMatch', selected);
return b;
},
reportValidity: function(comp, event, helper) {
var input=comp.find('input');
if(comp.checkValidity())
{
input.setCustomValidity('');
}
else
{
var disabled=comp.get('v.disabled');
if(!disabled)//fixed ie problem: show customValidity even disabled
{
input.setCustomValidity('您输入的值不正确,请从下拉列表中选择一个值。');
}
}
input.reportValidity();
},
preventDefault : function(component, event){
event.preventDefault();
}
})
SearchInputHelper.js
({
fireEvent:function(comp,eventType,valueKey)
{
//获得值
var value=comp.get(valueKey);
//获得事件
var event=comp.getEvent(eventType);
//值设置为事件的参数
event.setParams({
type:eventType,
data:{
value:value
}
});
//发射
event.fire();
},
/*
* 获取初始inputValue
*/
getInputValue:function(comp)
{
var options=comp.get('v.options');
if(Array.isArray(options))
{
var value=comp.get('v.value');
var inputValue=comp.get('v.inputValue');
//value有效
if(this.isValidString(value))
{
for(var i=0,len=options.length;i<len;i++)
{
var option=options[i];
var _value=option.value===undefined?option:option.value;
var _label=option.label===undefined?option:option.label;
if(value===_value)
{
if(inputValue!==_label)
{
console.log('searchinput设置inputvalue初始值');
comp.set('v.inputValue',_label);
break;
}
}
}
}
}
},
isValidString:function(str)
{
return str!==undefined&&str!==null&&str!=='';
}
})
CommonEvent.evt
<aura:event type="APPLICATION" description="Event template" >
<aura:attribute name="type" type="String"/>
<aura:attribute name="message" type="String"/>
<aura:attribute name="data" type="Object"/>
</aura:event>
AutoMatchListEvt.evt
<aura:event type="APPLICATION" description="Event template" >
<aura:attribute name="Type" type="String" default="Country"/>
<aura:attribute name="ParentID" type="String" default=""/>
</aura:event>
引用组件示例:
<aura:attribute name="searchName" type="String" default=""/>
<aura:attribute name="returnResult" type="String" default=""/>
<c:AutoMatchPickList objectType="Account"
labelField="Name"
filterCondition=''
fieldList="Name"
value="{! v.returnResult}"
placeholder="Please input"
label="Search Account"
required="true"
aura:id="account-search"
inputValue="{! v.searchName}" />