通用Aura和LWC lookup 组件, 逻辑存在很多可以优化的地方后期会进行优化,可以简单拿来使用(参考网上代码 侵权删)
Aura
Lookup(Parent)
Lookup cmp
<aura:component description="Lookup. Lightning component for lookup fields.
Can be used standalone or with other lightning component"
controller="LookupController">
<aura:attribute name="objectAPIName"
type="String"
required="true"
description="It is Object API name to search."/>
<aura:attribute name="placeholder"
type="String"
default="Search..."
description="Placeholder text."/>
<aura:attribute name="fieldLabel"
type="String"
default=""
description="input search Label."/>
<aura:attribute name="displayField"
type="String"
default="Name"
description="指定显示在文本框的字段,默认显示Name的值"/>
<aura:attribute name="fieldName"
type="String"
default=""
description="组件的名字,当需要监听组件的选择事件时,用来判断是那个组件发起的"/>
<aura:attribute name="filter"
type="String[]"
default="[]"
description="Array of filter condition for SOSL/SOQL query.Example: AccountId='001200000047KEdAAM', name like 'abc%' "/>
<aura:attribute name="chosenRecordId"
type="String"
description="Used to store the selected record id."/>
<aura:attribute name="chosenRecordLabel"
type="String"
description="This is used to show the selected record Name."/>
<aura:attribute name="chosenRecordLabelId"
type="String"
description=""/>
<aura:attribute name="iconCategoryName"
type="String"
default="standard:account"
description="This is used to show icon"/>
<aura:attribute name="subHeadingFieldsAPI"
type="String[]"
description="Field API for the fields to be shown under the record Name.
Must be comma separated. Example: Email,Phone"/>
<aura:attribute name="matchingRecords"
type="Object[]"
access="private"
description="List of records returned from server side call."/>
<aura:attribute name="DisableOnfocusEvent"
type="String"
default="false"
description="To disable onfocus component event, once disabled, there is not lastview records when focus it"/>
<aura:attribute name="isTriggerLookupChooseEvent"
type="String"
default="true"
description="Is trigger LookupChooseEvent function flag."/>
<aura:attribute name="isRequired"
type="Boolean"
default="false"
description="Is Required ."/>
<aura:handler name="lookupChoose"
event="c:LookupChooseEvent"
action="{!c.handleLookupChooseEvent}"
description="Event handler to get the selected record Id and Name from LookupItem component."/>
<aura:html tag="style">
.customloolup .slds-form-element__help { display:none;}
</aura:html>
<div class="customloolup slds-form-element__control layer-index">
<div class="slds-combobox_container layer-index"> <!-- slds-has-inline-listbox-->
<div aura:id="lookupdiv"
class="slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click slds-combobox-lookup layer-index"
aria-expanded="false" aria-haspopup="listbox"
role="combobox">
<div class="slds-combobox__form-element layer-index">
<!-- using search type as input field-->
<lightning:input type="search"
aura:id="searchinput"
label="{!v.fieldLabel}"
variant="{!v.fieldLabel=='' && !v.isRequired?'label-hidden':''}"
name="{!v.fieldName}"
value="{!v.chosenRecordLabel}"
onfocus="{!c.searchRecords}"
onchange="{!c.searchRecords}"
isLoading="false"
placeholder="{!v.placeholder}"
onblur="{!c.hideList}"
autocomplete="off"
required="{!v.isRequired}"
/>
</div>
<div id="listbox-unique-id" role="listbox">
<ul class="slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid layer-index"
role="presentation">
<!-- LookupItem component for display record list -->
<aura:iteration var="rec" items="{!v.matchingRecords}">
<c:LookupItem record="{!rec}"
fieldName="{!v.fieldName}"
displayField="{!v.displayField}"
subHeadingFieldsAPI="{!v.subHeadingFieldsAPI}"
isTriggerLookupChooseEvent="{!v.isTriggerLookupChooseEvent}"
objectAPIName = "{!v.objectAPIName}"
iconCategoryName="{!v.iconCategoryName}"/>
</aura:iteration>
</ul>
</div>
</div>
</div>
</div>
</aura:component>
Lookup controller js
({
//Function to handle the LookupChooseEvent. Sets the chosen record's Id and Name
handleLookupChooseEvent: function (component, event, helper) {
component.set("v.chosenRecordId", event.getParam("recordId"));
component.set("v.chosenRecordLabel", event.getParam("recordLabel"));
component.set("v.chosenRecordLabelId", event.getParam("recordId"));
helper.toggleLookupList(component,
false,
'slds-combobox-lookup',
'slds-is-open');
if (component.get("v.isRequired")) {
$A.util.removeClass(component.find("searchinput"), "slds-has-error");
}
},
//Function for finding the records as for given search input text
searchRecords: function (component, event, helper) {
console.log('lookup cmp start searchRecords');
var searchText = component.find("searchinput").get("v.value");
if (searchText) {
helper.searchSOSLHelper(component, searchText, helper);
} else {
component.set("v.chosenRecordId", null);
component.set("v.chosenRecordLabelId", null);
helper.searchSOQLHelper(component);
}
},
//function to hide the list on onblur event.
hideList: function (component, event, helper) {
if (component.get("v.isRequired") && !component.get("v.chosenRecordId")) {
$A.util.addClass(component.find("searchinput"), "slds-has-error");
}
//Using timeout and $A.getCallback() to avoid conflict between LookupChooseEvent and onblur
window.setTimeout(
$A.getCallback(function () {
if (component.isValid()) {
helper.toggleLookupList(component,
false,
'slds-combobox-lookup',
'slds-is-open'
);
}
}), 200
);
}
})
Lookup helper js
({
//Function to toggle the record list drop-down
toggleLookupList: function (component, ariaexpanded, classadd, classremove) {
component.find("lookupdiv").set("v.aria-expanded", true);
$A.util.addClass(component.find("lookupdiv"), classadd);
$A.util.removeClass(component.find("lookupdiv"), classremove);
},
//function to call SOSL apex method.
searchSOSLHelper: function (component, searchText, helper) {
//validate the input length. Must be greater then 1.
//This check also manages the SOSL exception. Search text must be greater then 1.
if (searchText && searchText.length >= 2) {
//show the loading icon for search input field
component.find("searchinput").set("v.isLoading", true);
//product2
//server side callout. returns the list of record in JSON string
var action = component.get("c.search");
var extrafields = component.get("v.subHeadingFieldsAPI");
action.setParams({
"objectAPIName": component.get("v.objectAPIName"),
"searchText": searchText,
"whereClause": component.get("v.filter"),
"extrafields": extrafields,
});
action.setCallback(this, function (a) {
var state = a.getState();
if (component.isValid() && state === "SUCCESS") {
// console.log('===>result'+a.getReturnValue());
// return;
//parsing JSON return to Object[]
var result = [].concat.apply([], JSON.parse(a.getReturnValue()));
// if (result) {
// result = helper.soslResultEnhancement(result, searchText, extrafields);
// }
component.set("v.matchingRecords", result);
//Visible the list if record list has values
if (a.getReturnValue() && a.getReturnValue().length > 0) {
this.toggleLookupList(component,
true,
'slds-is-open',
'slds-combobox-lookup');
//hide the loading icon for search input field
component.find("searchinput").set("v.isLoading", false);
} else {
this.toggleLookupList(component, false,
'slds-combobox-lookup',
'slds-is-open');
}
} else if (state === "ERROR") {
console.log('error in searchRecords');
}
});
$A.enqueueAction(action);
} else {
//hide the drop down list if input length less then 3
this.toggleLookupList(component,
false,
'slds-combobox-lookup',
'slds-is-open'
);
}
},
//function to call SOQL apex method.
searchSOQLHelper: function (component) {
component.find("searchinput").set("v.isLoading", true);
var action = component.get("c.getRecentlyViewed");
var chosenRecordId = component.get("v.chosenRecordId");
action.setParams({
"objectAPIName": component.get("v.objectAPIName"),
"objectAPIName2": component.get("v.objectAPIName2"),
"whereClause": component.get("v.filter"),
"extrafields": component.get("v.subHeadingFieldsAPI")
});
// Configure response handler
action.setCallback(this, function (response) {
var state = response.getState();
if (component.isValid() && state === "SUCCESS") {
if (response.getReturnValue()) {
component.set("v.matchingRecords", response.getReturnValue());
if (response.getReturnValue().length > 0) {
this.toggleLookupList(component,
true,
'slds-is-open',
'slds-combobox-lookup');
}
component.find("searchinput").set("v.isLoading", false);
}
} else {
console.log('Error in loadRecentlyViewed: ' + state);
}
});
$A.enqueueAction(action);
},
//SOSL模糊搜索有时候结果不理想,返回时对结果进行增强
soslResultEnhancement: function (result, searchText, extrafields) {
var data = [];
searchText = searchText.replace(/\*/g, '');
var check = function (arrs, obj, searchText) {
for (var i = 0; i < arrs.length; i++) {
if (obj[arrs[i]] && obj[arrs[i]].indexOf(searchText) > -1)
return true;
}
return false;
};
result.forEach(function (item) {
if (item.Name.indexOf(searchText) > -1 ) {
data.push(item);
return false;
} else if(check(extrafields, item, searchText)) {
data.push(item);
}
});
return data;
}
})
Lookup(Event)
Lookup event
<aura:event type="COMPONENT" description="LookupChooseEvent">
<aura:attribute name="recordId" type="String"
required="true"
description="Used to send selected record Id"/>
<aura:attribute name="recordLabel"
type="String"
description="Used to send selected record Name"
required="true"/>
<aura:attribute name="record"
type="Object"
description="Used to send selected Object"
required="false"/>
<aura:attribute name="fieldName"
type="Object"
description="Used to send selected fieldName"
required="false"/>
<aura:attribute name="isTriggerFunction"
type="String"
description="After select record ,Send current event is trigger function flag."
required="true"/>
</aura:event>
Lookup Item(Child)
Lookup Item cmp
<aura:component description="LookupItem.
Component used for dispaly list elements for records. it is used in Lookup component">
<!-- Component attributes-->
<aura:attribute name="record"
type="Object"
description="Holds the single record instance"
required="true"/>
<aura:attribute name="subHeadingFieldsAPI"
type="String[]"
description="Holds the field API names to show as meta entity in list"/>
<aura:attribute name="subHeadingFieldValues"
type="String"
description="Used to construct the meta entity value. Works as subheading in record option"/>
<aura:attribute name="iconCategoryName"
type="String"
description="Lightning icon category and icon name to show with each record element"/>
<aura:attribute name="isTriggerLookupChooseEvent"
type="String"
default="true"
description="Is trigger LookupChooseEvent function flag."/>
<aura:attribute name="objectAPIName"
type="String"
required="false"
description="It is Object API name to search."/>
<aura:attribute name="displayField"
type="String"
default="Name"
description="指定显示在文本框的字段,默认显示Name的值"/>
<aura:attribute name="fieldName"
type="String"
default=""
description="组件的名字,当需要监听组件的选择事件时,用来判断是那个组件发起的"/>
<!-- Component event registers-->
<aura:registerEvent name="lookupChoose"
type="c:LookupChooseEvent"
description="Event used to send the selected record Id and Name to Lookup component"/>
<!-- Component event handlers-->
<aura:handler name="init"
value="{!this}"
action="{!c.loadValues}"
description="standard init event to prepare the sub heading mete entity value"/>
<!-- Component markup-->
<li role="presentation" class="slds-listbox__item" onclick="{!c.choose}" title="{!v.record.Name+'-'+v.subHeadingFieldValues}">
<span class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta"
role="option">
<!-- lightning icon -->
<span class="slds-media__figure">
<lightning:icon iconName="{!v.iconCategoryName}"
size="small"
alternativeText="{!v.record.Name}"/>
</span>
<!-- option Name-->
<span class="slds-media__body">
<span class="slds-listbox__option-text slds-listbox__option-text_entity">
{!v.record.Name}
</span>
<!-- option sub heading. Also known as meta entity as per SLDS combobox component-->
<span class="slds-listbox__option-meta slds-listbox__option-meta_entity">
{!v.subHeadingFieldValues}
</span>
</span>
</span>
</li>
</aura:component>
Lookup item controller js
({
loadValues: function (component) {
var record = component.get("v.record");
var subheading = '';
for (var i = 0; i < component.get("v.subHeadingFieldsAPI").length; i++) {
if (record[component.get("v.subHeadingFieldsAPI")[i]]) {
subheading = subheading + record[component.get("v.subHeadingFieldsAPI")[i]] + ' • ';
}
}
subheading = subheading.substring(0, subheading.lastIndexOf('•'));
component.set("v.subHeadingFieldValues", subheading);
},
choose: function (component, event) {
var displayField = component.get("v.displayField"),
chooseEvent = component.getEvent("lookupChoose");
chooseEvent.setParams({
"recordId": component.get("v.record").Id,
"recordLabel": component.get("v.record")[displayField],
"record": component.get("v.record"),
"fieldName": component.get("v.fieldName"),
"isTriggerFunction": component.get("v.isTriggerLookupChooseEvent")
});
chooseEvent.fire();
}
})
LWC
Lookup lwc
Lookup html
<template>
<div class="slds-form-element">
<label if:true={label} class="slds-form-element__label" for="combobox">
<abbr if:true={required} title="required" class="slds-required">*</abbr>
{label}
</label>
<div class="slds-form-element__control">
<div class={getContainerClass}>
<div
class={getDropdownClass}
aria-expanded={isExpanded}
aria-haspopup="listbox"
role="combobox"
>
<!-- Search input start -->
<div class={getComboboxClass} role="none">
<template if:false={isMultiEntry}>
<lightning-icon
icon-name={getSelectIconName}
size="small"
alternative-text="Selected item icon"
class={getSelectIconClass}
>
</lightning-icon>
</template>
<!-- Text input -->
<input
type="text"
class={getInputClass}
aria-autocomplete="list"
aria-controls="listbox"
autocomplete="off"
role="textbox"
id="combobox"
placeholder={placeholder}
value={getInputValue}
title={getInputTitle}
readonly={isInputReadonly}
onfocus={handleFocus}
onblur={handleBlur}
oninput={handleInput}
/>
<!-- Search icon -->
<lightning-icon
icon-name="utility:search"
size="x-small"
alternative-text="Search icon"
class={getSearchIconClass}
></lightning-icon>
<!-- Clear selection button icon for single entry lookups -->
<template if:false={isMultiEntry}>
<button
title="Remove selected option"
type="button"
onclick={handleClearSelection}
class={getClearSelectionButtonClass}
>
<lightning-icon
icon-name="utility:close"
size="x-small"
alternative-text="Remove selected option"
class="slds-button__icon"
></lightning-icon>
</button>
</template>
</div>
<!-- Search input end -->
<!-- Result list box start -->
<div id="listbox" role="listbox" onclick={handleComboboxClick}>
<ul class={getListboxClass} role="presentation">
<!-- Spinner to display when waiting for results of search -->
<div if:true={loading}>
<lightning-spinner
alternative-text="Loading"
size="small"
></lightning-spinner>
</div>
<!-- Display if results are present -->
<template
for:each={searchResults}
for:item="result"
if:true={isExpanded}
>
<li
key={result.id}
role="presentation"
class="slds-listbox__item"
>
<span
class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta"
role="option"
onclick={handleResultClick}
data-recordid={result.id}
>
<span class="slds-media__figure">
<lightning-icon
icon-name={result.icon}
size="small"
alternative-text="Result item icon"
></lightning-icon>
</span>
<span class="slds-media__body">
<span
class="slds-listbox__option-text slds-listbox__option-text_entity"
>
<lightning-formatted-rich-text
value={result.titleFormatted}
disable-linkify
>
</lightning-formatted-rich-text>
</span>
<span
class="slds-listbox__option-meta slds-listbox__option-meta_entity"
>
<lightning-formatted-rich-text
value={result.subtitleFormatted}
disable-linkify
>
</lightning-formatted-rich-text>
</span>
</span>
</span>
</li>
</template>
<!-- Display that there are no results -->
<template if:false={isExpanded}>
<li role="presentation" class="slds-listbox__item">
<span
class="slds-media slds-listbox__option_entity"
role="option"
>
<span if:false={loading} class="slds-media__body">
No results.
</span>
<span if:true={loading} class="slds-media__body">
Loading...
</span>
</span>
</li>
</template>
</ul>
</div>
<!-- Result list box end -->
</div>
</div>
<!-- Multi-selection start -->
<template if:true={isMultiEntry}>
<div id="selection" role="listbox" aria-orientation="horizontal">
<ul
class="slds-listbox slds-listbox_inline slds-var-p-top_xxx-small"
role="group"
aria-label="Selected Options:"
>
<template for:each={curSelection} for:item="item">
<li key={item.id} role="presentation" class="slds-listbox__item">
<lightning-pill
label={item.title}
title={item.title}
onremove={handleRemoveSelectedItem}
name={item.id}
>
<lightning-icon icon-name={item.icon}></lightning-icon>
</lightning-pill>
</li>
</template>
</ul>
</div>
</template>
<!-- Multi-selection end -->
<!-- Errors start -->
<template for:each={errors} for:item="error">
<label
key={error.id}
role="alert"
class="slds-form-element__label slds-var-m-top_xx-small form-error"
>{error.message}</label
>
</template>
<!-- Errors end -->
</div>
</div>
</template>
lookup js
import { LightningElement, api } from "lwc";
const MINIMAL_SEARCH_TERM_LENGTH = 2; // Min number of chars required to search
const SEARCH_DELAY = 300; // Wait 300 ms after user stops typing then, peform search
export default class Lookup extends LightningElement {
@api label;
@api required;
@api placeholder = "";
@api isMultiEntry = false;
@api errors = [];
@api scrollAfterNItems;
@api obj = "";
searchTerm = "";
searchResults = [];
hasFocus = false;
loading = false;
isDirty = false;
cleanSearchTerm;
blurTimeout;
searchThrottlingTimeout;
curSelection = [];
// EXPOSED FUNCTIONS
@api
set selection(initialSelection) {
this.curSelection = Array.isArray(initialSelection)
? initialSelection
: [initialSelection];
}
get selection() {
return this.curSelection;
}
// @api
// setApiName(api){
// this.api = api;
// console.log('setApiName----->'+this.api);
// }
@api
setSearchResults(results) {
// Reset the spinner
this.loading = false;
// Clone results before modifying them to avoid Locker restriction
const resultsLocal = JSON.parse(JSON.stringify(results));
// Format results
this.searchResults = resultsLocal.map(result => {
// Clone and complete search result if icon is missing
if (this.searchTerm.length > 0) {
const regex = new RegExp(`(${this.searchTerm})`, "gi");
result.titleFormatted = result.title
? result.title.replace(regex, "<strong>$1</strong>")
: result.title;
result.subtitleFormatted = result.subtitle
? result.subtitle.replace(regex, "<strong>$1</strong>")
: result.subtitle;
}
if (typeof result.icon === "undefined") {
const { id, sObjectType, title, subtitle } = result;
return {
id,
sObjectType,
icon: "standard:default",
title,
subtitle
};
}
return result;
});
}
@api
getSelection() {
return this.curSelection;
}
// INTERNAL FUNCTIONS
updateSearchTerm(newSearchTerm) {
this.searchTerm = newSearchTerm;
console.log('api-1111-->'+this.obj);
// Compare clean new search term with current one and abort if identical
const newCleanSearchTerm = newSearchTerm
.trim()
.replace(/\*/g, "")
.toLowerCase();
if (this.cleanSearchTerm === newCleanSearchTerm) {
return;
}
// Save clean search term
this.cleanSearchTerm = newCleanSearchTerm;
// Ignore search terms that are too small
if (newCleanSearchTerm.length < MINIMAL_SEARCH_TERM_LENGTH) {
this.searchResults = [];
return;
}
// Apply search throttling (prevents search if user is still typing)
if (this.searchThrottlingTimeout) {
clearTimeout(this.searchThrottlingTimeout);
}
// eslint-disable-next-line @lwc/lwc/no-async-operation
this.searchThrottlingTimeout = setTimeout(() => {
// Send search event if search term is long enough
if (this.cleanSearchTerm.length >= MINIMAL_SEARCH_TERM_LENGTH) {
// Display spinner until results are returned
this.loading = true;
const searchEvent = new CustomEvent("search", {
detail: {
searchTerm: this.cleanSearchTerm,
selectedIds: this.curSelection.map(element => element.id),
apiName: this.obj
}
});
this.dispatchEvent(searchEvent);
}
this.searchThrottlingTimeout = null;
}, SEARCH_DELAY);
}
isSelectionAllowed() {
if (this.isMultiEntry) {
return true;
}
return !this.hasSelection();
}
hasResults() {
return this.searchResults.length > 0;
}
hasSelection() {
return this.curSelection.length > 0;
}
// EVENT HANDLING
handleInput(event) {
// Prevent action if selection is not allowed
if (!this.isSelectionAllowed()) {
return;
}
this.updateSearchTerm(event.target.value);
}
handleResultClick(event) {
const recordId = event.currentTarget.dataset.recordid;
// Save selection
let selectedItem = this.searchResults.filter(
result => result.id === recordId
);
if (selectedItem.length === 0) {
return;
}
selectedItem = selectedItem[0];
const newSelection = [...this.curSelection];
newSelection.push(selectedItem);
this.curSelection = newSelection;
this.isDirty = true;
// Reset search
this.searchTerm = "";
this.searchResults = [];
// Notify parent components that selection has changed
this.dispatchEvent(new CustomEvent("selectionchange"));
}
handleComboboxClick() {
// Hide combobox immediatly
if (this.blurTimeout) {
window.clearTimeout(this.blurTimeout);
}
this.hasFocus = false;
}
handleFocus() {
// Prevent action if selection is not allowed
if (!this.isSelectionAllowed()) {
return;
}
this.hasFocus = true;
}
handleBlur() {
// Prevent action if selection is not allowed
if (!this.isSelectionAllowed()) {
return;
}
// Delay hiding combobox so that we can capture selected result
// eslint-disable-next-line @lwc/lwc/no-async-operation
this.blurTimeout = window.setTimeout(() => {
this.hasFocus = false;
this.blurTimeout = null;
}, 300);
}
handleRemoveSelectedItem(event) {
const recordId = event.currentTarget.name;
this.curSelection = this.curSelection.filter(item => item.id !== recordId);
this.isDirty = true;
// Notify parent components that selection has changed
this.dispatchEvent(new CustomEvent("selectionchange"));
}
handleClearSelection() {
this.curSelection = [];
this.isDirty = true;
// Notify parent components that selection has changed
this.dispatchEvent(new CustomEvent("selectionchange"));
}
// STYLE EXPRESSIONS
get getContainerClass() {
let css = "slds-combobox_container slds-has-inline-listbox ";
if (this.hasFocus && this.hasResults()) {
css += "slds-has-input-focus ";
}
if (this.errors.length > 0) {
css += "has-custom-error";
}
return css;
}
get getDropdownClass() {
let css =
"slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click ";
if (
this.hasFocus &&
this.cleanSearchTerm &&
this.cleanSearchTerm.length >= MINIMAL_SEARCH_TERM_LENGTH
) {
css += "slds-is-open";
}
return css;
}
get getInputClass() {
let css = "slds-input slds-combobox__input has-custom-height ";
if (
this.errors.length > 0 ||
(this.isDirty && this.required && !this.hasSelection())
) {
css += "has-custom-error ";
}
if (!this.isMultiEntry) {
css +=
"slds-combobox__input-value " +
(this.hasSelection() ? "has-custom-border" : "");
}
return css;
}
get getComboboxClass() {
let css = "slds-combobox__form-element slds-input-has-icon ";
if (this.isMultiEntry) {
css += "slds-input-has-icon_right";
} else {
css += this.hasSelection()
? "slds-input-has-icon_left-right"
: "slds-input-has-icon_right";
}
return css;
}
get getSearchIconClass() {
let css = "slds-input__icon slds-input__icon_right ";
if (!this.isMultiEntry) {
css += this.hasSelection() ? "slds-hide" : "";
}
return css;
}
get getClearSelectionButtonClass() {
return (
"slds-button slds-button_icon slds-input__icon slds-input__icon_right " +
(this.hasSelection() ? "" : "slds-hide")
);
}
get getSelectIconName() {
return this.hasSelection() ? this.curSelection[0].icon : "standard:default";
}
get getSelectIconClass() {
return (
"slds-combobox__input-entity-icon " +
(this.hasSelection() ? "" : "slds-hide")
);
}
get getInputValue() {
if (this.isMultiEntry) {
return this.searchTerm;
}
return this.hasSelection() ? this.curSelection[0].title : this.searchTerm;
}
get getInputTitle() {
if (this.isMultiEntry) {
return "";
}
return this.hasSelection() ? this.curSelection[0].title : "";
}
get getListboxClass() {
return (
"slds-listbox slds-listbox_vertical slds-dropdown slds-dropdown_fluid " +
(this.scrollAfterNItems
? "slds-dropdown_length-with-icon-" + this.scrollAfterNItems
: "")
);
}
get isInputReadonly() {
if (this.isMultiEntry) {
return false;
}
return this.hasSelection();
}
get isExpanded() {
return this.hasResults();
}
}
Apex
public with sharing class LookupController {
/* Method to query records using SOSL*/
@AuraEnabled
public static String search(
String objectAPIName,
String searchText,
String searchFields,
List<String> whereClause,
List<String> extrafields
) {
System.debug('start to search record for aura ');
return JSON.serializePretty(searchResult(objectAPIName,searchText,searchFields,whereClause,extrafields));
}
public static List<List<SObject>> searchResult(
String objectAPIName,
String searchText,
String searchFields,
List<String> whereClause,
List<String> extrafields
){
System.debug('start to search record ');
objectAPIName = String.escapeSingleQuotes(objectAPIName);
searchText = String.escapeSingleQuotes(searchText);
searchText = searchText.replace('-', '\\-');
String searchQuery =
'FIND {' +
searchText +
'} IN ALL FIELDS RETURNING ' +
objectAPIName +
'(Id,Name';
if (!extrafields.isEmpty()) {
for (String item : extrafields) {
if (!String.isEmpty(item))
searchQuery = searchQuery + ',' + item;
}
}
if (!whereClause.isEmpty()) {
searchQuery = searchQuery + ' WHERE ';
searchQuery = searchQuery + String.join(whereClause, ' AND ');
}
searchQuery = searchQuery + ' LIMIT 10 ) ';
return search.query(searchQuery);
}
/* Method to query records using SOQL*/
@AuraEnabled
public static List<SObject> getRecentlyViewed(
String objectAPIName,
List<String> whereClause,
List<String> extrafields
) {
String searchQuery = 'SELECT Id, Name';
if (!extrafields.isEmpty()) {
searchQuery = searchQuery + ',' + String.join(extrafields, ',');
}
searchQuery = searchQuery + ' FROM ' + objectAPIName + ' ';
if (!whereClause.isEmpty()) {
searchQuery = searchQuery + ' WHERE ';
searchQuery = searchQuery + String.join(whereClause, ' AND ');
}
searchQuery = searchQuery + ' ORDER BY CreatedDate DESC LIMIT 10';
system.debug(searchQuery);
List<SObject> objectList = new List<SObject>();
objectList = Database.query(searchQuery);
return objectList;
}
@AuraEnabled(Cacheable=false)
public static List<LookupSearchResult> searchResult(String searchTerm,String apiName) {
System.debug('searchTerm---->'+searchTerm);
System.debug('apiName---->'+apiName);
// Prepare query paramters
searchTerm += '*';
// Execute search query
//List<SObject> result = LookupController
List<List<SObject>> searchResults = searchResult(apiName, searchTerm, null, new List<String>(),new List<String>());
System.debug('searchResults for lwc'+searchResults);
List<SObject> result2 = getRecentlyViewed('User',new List<String>(),new List<String>());
System.debug('result id'+result2[0].get('Id'));
System.debug('result name'+result2[0].get('Name'));
// Prepare results
List<LookupSearchResult> results = new List<LookupSearchResult>();
// Extract Accounts & convert them into LookupSearchResult
String objIcon = 'standard:' + apiName.toLowerCase();
for (SObject obj : searchResults[0]) {
results.add(
new LookupSearchResult(
String.valueOf(obj.get('Id')),
apiName,
objIcon,
String.valueOf(obj.get('Name')),
''
)
);
}
// Optionnaly sort all results on title
results.sort();
return results;
}
}
Use Aura lookup
Aura cmp
<aura:component implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId" access="global">
<aura:attribute name="chooseId" type="String" />
<lightning:card title="" >
<div class="slds-content-body demo-only demo-only--sizing slds-grid slds-wrap " style="overflow-y: auto; margin: 1rem">
<aura:if isTrue="{!v.isSpinner}">
<lightning:spinner alternativeText="Loading" variant="brand" size="large" />
</aura:if>
<div class="slds-size_2-of-2">
<div style="margin:6px auto;">
<c:Lookup aura:id="ahm__Master_Id__c"
objectAPIName="Account"
fieldLabel="替换Account"
isRequired="false"
fieldName="AccountSource"
subHeadingFieldsAPI="AccountNumber"
chosenRecordId="{!v.chooseId}"
chosenRecordLabel=""
DisableOnfocusEvent="true"
filter=""
placeholder="请选择"
isTriggerLookupChooseEvent="true" />
</div>
</div>
</lightning:card>
</aura:component>
Use LWC lookup
Lwc html
<template>
<lightning-card title="">
<div class="slds-form-element lookup-form" style="overflow-y: auto; margin: 1rem">
<c-cc_lwc_lookup
errors={lookupErrors}
onsearch={handleLookupSearch}
onselectionchange={handleSelectionChange}
label="Search User"
placeholder="Search Users..."
is-multi-entry={isMultiEntry}
obj="User"
>
</c-cc_lwc_lookup>
</div>
</lightning-card>
</template>
Lwc js
import { LightningElement } from 'lwc';
import searchResult from "@salesforce/apex/LookupController.searchResult";
export default class DemoForLwc extends LightningElement {
//lookup properties
lookupErrors = []; //errors related to the lookup component
isMultiEntry = false;
initialSelection = [];
handleLookupSearch(event) {
//this.template.querySelector("c-cc_lwc_lookup").setApiName('User');
console.log('event '+JSON.stringify(event));
console.log('event detail'+JSON.stringify(event.detail));
searchResult(event.detail)
.then(results => {
console.log('SearchResults');
console.log(results);
this.template.querySelector("c-cc_lwc_lookup").setSearchResults(results);
})
.catch(error => {
this.lookupErrors = [error];
});
}
handleSelectionChange() {
this.lookupErrors = [];
}
}