Aura Lookup and LWC Lookup

通用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=='' &amp;&amp; !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 = [];
    }


}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值