+ Start a Discussion
Mars Rover 1489Mars Rover 1489 

Lightning Component - Picklist Field with Ability to write free text

I need to override standard Create and Edit Actions on Account object with Lightning Component. Is there a way or a type of field that we could cover the following requirement? Have a field with some predifined values but letting user if he wants to enter free text. I would actually like something like Subject field on Task object but i cannot find any way to implement it. Any ideas please ?
Example: I have a picklist field for Citizenship that has some values for Countries (England, Germany, France, ...etc...). But i want to let user enter his own value (for example: German). i have tried using the following but without success:
 
<!--05/09/2019 George Galaios: Code for Iteration Picklist: Citizenship -->
                        <lightning:select aura:id="accCitizenship" label="{!$Label.c.ea_citizenship}" value="{!v.accountRecord.ea_Citizenship__c}" disabled="{!and(v.noExtraPermission, v.noPermAndCleansed)}" class="slds-size--1-of-2 slds-p-horizontal_x-small">
                            <aura:iteration items="{!v.ea_Citizenship_Values}" var="item">
                                <option value="{!item}" selected="{!item==v.accountRecord.ea_Citizenship__c}">{!item}</option>
                            </aura:iteration>
                        </lightning:select>                
                        <!-- End of Code for Iteration -->

 
Best Answer chosen by Mars Rover 1489
Mars Rover 1489Mars Rover 1489
Finally i gave solution to my problem by creating a new Lightning Component and including it to the Lightning Component for Account. This component i created has an input field as well as a dropdown list to show the available list items. Onclick, the dropdown gets displayed and onfocusout it gets disappeared. Also, the inputField has an action onchange in order to call Apex Class controller to filter the results while user is Typing!

All Answers

Mars Rover 1489Mars Rover 1489
Finally i gave solution to my problem by creating a new Lightning Component and including it to the Lightning Component for Account. This component i created has an input field as well as a dropdown list to show the available list items. Onclick, the dropdown gets displayed and onfocusout it gets disappeared. Also, the inputField has an action onchange in order to call Apex Class controller to filter the results while user is Typing!
This was selected as the best answer
SkyhSkyh
Mars Rover 1489,

can you please share your code?  
George Galaios 22George Galaios 22
Hi Rov,

sure. Please find code below: 

Component:
<!--27/10/2019 George Galaios: This component is created in order to cover Citizenship fields. Those fields are not restricted picklists, so it is needed to accept either values from Picklists or Free Text! -->
<!--27/10/2019 George Galaios: Define the appopriate Attributes.-->
<aura:component controller="ea_PickListController">
    <aura:attribute name="objectName" type="String" default="Account"/>
    <aura:attribute name="fieldName" type="String" default="ea_Citizenship__c"/>
    <aura:attribute name="selectRecordId" type="String"/>
    <aura:attribute name="selectRecordName" type="String"/>
    <aura:attribute name="Label" type="String" default="{!$Label.c.ea_citizenship}"/>
    <aura:attribute name="picklistValues" type="String[]"/>
    <aura:attribute name="allListValues" type="String[]"/>
    <aura:attribute name="required" type="Boolean" default="false"/>
    <aura:attribute name="iconName" type="String" default="utility:identity"/>
    <aura:attribute name="LoadingText" type="Boolean" default="false"/>
    <aura:attribute name="readonly" type="Boolean" default="false"/>
    <aura:attribute name="helpText" type="String" default="Επιλέξτε μία χώρα από την λιστα είτε πληκτρολογήστε μία τιμή της επιλογής σας"/>
    
    <!--27/10/2019 George Galaios: If focus goes out, call closeDropDown Controller function-->
    <div onfocusout="{!c.closeDropDown}">         
        <div class="slds-combobox_container slds-size--1-of-1 slds-p-horizontal_x-small">
            
            <div class="slds-size--1-of-1 slds-p-horizontal_x-small slds-combobox slds-dropdown-trigger slds-dropdown-trigger_click" aura:id="resultBox" aria-expanded="false" aria-haspopup="listbox" role="combobox">
                <div class="slds-form-element__control" role="none">
                    <aura:if isTrue="{!v.readonly}">
                        
                        <!--27/10/2019 George Galaios: Input Field for Citizenship. When user typing, call searchField Controller function-->                    
                        <lightning:input required="{!v.required}" disabled="{!v.readonly}" aura:id="userinput" label="{!v.Label}" name="searchText" value="{!v.selectRecordName}" fieldLevelHelp="{!v.helpText}" class="leftspace"/>
                        
                        <aura:set attribute = "else">
                            <!--27/10/2019 George Galaios: Input Field for Citizenship. When user typing, call searchField Controller function-->                    
                            <lightning:input required="{!v.required}" disabled="{!v.readonly}" aura:id="userinput" label="{!v.Label}" name="searchText" onchange="{!c.searchField}" onclick="{!c.getPickListValues}" value="{!v.selectRecordName}" isLoading="{!v.LoadingText}" type="search" fieldLevelHelp="{!v.helpText}" class="leftspace"/></aura:set>
                        
                    </aura:if>
                    
                </div>
                
                <!--27/10/2019 George Galaios: Second part display result -->
                <div id="listbox-id-1" class="slds-dropdown slds-dropdown_length-with-icon-7 slds-dropdown_fluid" role="listbox">
                    <ul class="slds-listbox slds-listbox_vertical" role="presentation" >
                        <aura:iteration items="{!v.picklistValues}" var="serecord" indexVar="hdtv">
                            <li role="presentation" class="slds-listbox__item">
                                <!--27/10/2019 George Galaios: Show the list of values that match user's typed criteria. Onmousedown call setSelectedRecord controller function  -->
                                <div data-name="{!serecord}" onmousedown="{!c.setSelectedRecord}"  class="slds-media slds-listbox__option slds-listbox__option_entity slds-listbox__option_has-meta" role="option">
                                    <span class="slds-media__figure">
                                        <span class="slds-icon_container slds-icon-standard-account">
                                            <!--27/10/2019 George Galaios: Show icon on the left of results displayed  -->                                            
                                            <lightning:icon iconName="{!v.iconName}" class="slds-icon slds-icon slds-icon_small slds-icon-text-default" size="x-small"/>
                                        </span>
                                    </span>
                                    <span class="slds-media__body">
                                        <!--27/10/2019 George Galaios: Display Countries Names  -->
                                        <span class="slds-listbox__option-text slds-listbox__option-text_entity">{!serecord}</span>
                                    </span>
                                </div>
                            </li>
                        </aura:iteration>
                        
                        <!--27/10/2019 George Galaios: If loading, show the appropriate message on the screen  -->
                        <aura:if isTrue="{!v.LoadingText}">
                            Loading...
                        </aura:if>
                    </ul>
                </div>
            </div>                                          
        </div>
    </div>           
</aura:component>

Controller
//ggalaios @ 27/10/2019 
//Lightning Component for Citizenships fields of Account
//Component Controller

({
    //When clicking on the component it calls Apex function that fetches the values of the picklist field that is given
    getPickListValues : function(component) {
        //Check if field is in ReadOnly mode and confirm that it does not already have a value
        if ((component.get("v.readonly") == false) && ((component.get("v.selectRecordName") == "" || component.get("v.selectRecordName") == undefined || component.get("v.selectRecordName") == null))) {
            component.set("v.LoadingText", true);
            var action = component.get("c.getPickListValuesIntoList");
            action.setParams({
                //set the appropriate params
                "objectType": component.get("v.objectName"),
                "selectedField": component.get("v.fieldName")
            });
            action.setCallback(this, function(response) {
                var list = response.getReturnValue();
                component.set("v.picklistValues", list);
                component.set("v.allListValues", list);
                component.set("v.LoadingText", false);
                var resultBox = component.find('resultBox');
                $A.util.addClass(resultBox, 'slds-is-open');
                //console.log('return value: ' +response.getReturnValue());
            })
            $A.enqueueAction(action);
        }
    },
    
    //Function called while user typing on input field
    searchField : function(component, event, helper) {
        //Capitalize input
        component.set("v.LoadingText", true);
        var currentText = helper.RemoveAccentsAndCapitalize(event.getSource().get("v.value"));
        //Capitalize Component attribute
        component.set("v.selectRecordName", currentText);
        var resultBox = component.find('resultBox');
        //if length of text typed by user > 0 show resultBox, else not show it and assign again PickListValues Attriute the whole list of values
        if(currentText.length > 0) {
            $A.util.addClass(resultBox, 'slds-is-open');
            //Call Apex Class to return the results while user is typing
            //console.log('Just before calling Apex... current Text is: ' +currentText);
            var action = component.get("c.getSearchList");
            action.setParams({
                "citizenshipList" : component.get("v.allListValues"),
                "value" : currentText
            });
            action.setCallback(this, function(response){
                var STATE = response.getState();
                if(STATE === "SUCCESS") {
                    //console.log('from function is returned...: ' +response.getReturnValue());
                    component.set("v.picklistValues", response.getReturnValue());
                    component.set("v.LoadingText", false);
                    if(component.get("v.picklistValues").length == 0) {
                        $A.util.removeClass(resultBox, 'slds-is-open');
                    }
                }
                else if (state === "ERROR") {
                    var errors = response.getError();
                    if (errors) {
                        if (errors[0] && errors[0].message) {
                            console.log("Error message: " + 
                                        errors[0].message);
                        }
                    } else {
                        console.log("Unknown error");
                    }
                }
            });
            $A.enqueueAction(action);
        }
        else {
            component.set("v.LoadingText", true);
            //console.log('case is currentText is zero length');
            var action = component.get("c.getPickListValuesIntoList");
            action.setParams({
                //set the appropriate params
                "objectType": component.get("v.objectName"),
                "selectedField": component.get("v.fieldName")
            });
            action.setCallback(this, function(response) {
                var list = response.getReturnValue();
                component.set("v.picklistValues", list);
                component.set("v.allListValues", list);
                var resultBox = component.find('resultBox');
                component.set("v.LoadingText", false);
                $A.util.addClass(resultBox, 'slds-is-open');
            })
            $A.enqueueAction(action);
        }
    },
    
    //when user clicks (X) button, set the appropriate Attributes blank.
    resetData : function(component, event, helper) {
        component.set("v.LoadingText", true);
        component.set("v.selectRecordName", "");
        component.find('userinput').set("v.readonly", false);
        component.set("v.LoadingText", false);
    },
    
    //When user selects a record, set it as selected
    setSelectedRecord : function(component, event, helper) {
        var currentText = event.currentTarget.id;
        var resultBox = component.find('resultBox');
        $A.util.removeClass(resultBox, 'slds-is-open');
        component.set("v.selectRecordName", event.currentTarget.dataset.name);
    }, 
    
    //Function when user clicks outside Component to close the dropdown list
    closeDropDown : function(component, event, helper) {
        var resultBox = component.find('resultBox');
        $A.util.removeClass(resultBox, 'slds-is-open');
        component.set("v.LoadingText", false);
    },
    
    
})

Helper:
​​​​​​​
({
	RemoveAccentsAndCapitalize: function(strAccents) {
		var strAccents = strAccents.split('');
		var strAccentsOut = new Array();
		var strAccentsLen = strAccents.length;
        var accents = 'ÀÁÂÃÄÅàáâãäåÒÓÔÕÕÖØòóôõöøÈÉÊËèéêëðÇçÐÌÍÎÏìíîïÙÚÛÜùúûüÑñŠšŸÿýŽžάέήύόύϋΰίϊΐώΆΈΉΎΌΆΎΊΏ';
		var accentsOut = "AAAAAAaaaaaaOOOOOOOooooooEEEEeeeeeCcDIIIIiiiiUUUUuuuuNnSsYyyZzαεηυουϋϋιϊϊωαεηυοαυιω";
		for (var y = 0; y < strAccentsLen; y++) {
			if (accents.indexOf(strAccents[y]) != -1) {
				strAccentsOut[y] = accentsOut.substr(accents.indexOf(strAccents[y]), 1);
			} else
				strAccentsOut[y] = strAccents[y];
		}
		strAccentsOut = strAccentsOut.join('');
		return strAccentsOut.toUpperCase(); //@ggalaios return string also capitalized
	},
})

Style
.THIS .slds-leftpad{
	padding-left: 2rem;
}
.THIS .iconheight{
    top: 65%;
}

.THIS .leftspace input {
    padding-left:2rem;
}

Apex Controller
public class ea_PickListController {
    @AuraEnabled
    //15/09/2019: George Galaios: Method to get Picklist values
    public static List<String> getPickListValuesIntoList(String objectType, String selectedField){
        List<String> pickListValuesList = new List<String>();
        Schema.SObjectType convertToObj = Schema.getGlobalDescribe().get(objectType);
        Schema.DescribeSObjectResult res = convertToObj.getDescribe();
        Schema.DescribeFieldResult fieldResult = res.fields.getMap().get(selectedField).getDescribe();
        List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
        //27/10/2019: ggalaios: Check if SelectedField is different than Citizenships. Because in Citizenships we do not want the Choice "Please Select"
        if ((selectedField != 'ea_Citizenship__c') && (selectedField != 'ea_Citizenship2__c') && (selectedField != 'ea_Profession__c')) {
            pickListValuesList.add('--Παρακαλώ Επιλέξτε--');
        }
        for( Schema.PicklistEntry pickListVal : ple){
            pickListValuesList.add(pickListVal.getLabel());
        }     
        return pickListValuesList;
    }
    
    @AuraEnabled
    //27/10/2019 George Galaios: This method is called while user is typing (searching for Citizenship)
    public static List<String> getSearchList(List<String> citizenshipList, String value) {
        //Initiate the list that is going to be returned
        List<String> searchResultList = new List<String>();
        try {
            //for every item of the list check if what user has typed is contained in the List of Citizenships
            for (Integer i=0; i<citizenshipList.size(); i++) {
                if (citizenshipList[i].contains(value)) {
                    searchResultList.add(citizenshipList[i]);
                }
            }
        } catch (Exception e) {
            system.debug('exception caught: ' +e.getMessage());
        }
        return searchResultList;
    }
}

Apex Conroller Test Class
@isTest
//Test class for ea_PickListController Apex Class
public class ea_PickListControllerTest {
    @isTest Public static void getPickListValuesIntoListTest() {
        ea_PickListController.getPickListValuesIntoList('Account', 'ea_CustomerType__c');
    }
    
    //Test getSearchList method
    @isTest public static void getSearchListTest() {
        List<String> testList = new List<String>();
        testList.add('ΑΓΓΛΙΑ');
        testList.add('ΕΛΛΑΔΑ');
        testList.add('ΗΝΩΜΕΝΕΣ ΠΟΛΙΤΕΙΕΣ ΑΜΕΡΙΚΗΣ');
        ea_PickListController.getSearchList(testList, 'ΕΛΛ');
    }
}


​​​​​​​