[SalesForce] lightning:inputField Lookup mobile

So I know that on mobile <lightning:inputField> for lookup fields doesn't work. It's right there in the documentation (not clearly, but it's there if you know what to look for).
But if you check, you'll see that the standard Salesforce components do have a dropdown for selecting records in lookup field on a mobile device. (Try creating a new Contact on mobile and selecting an Account).
How do they do it?

Best Answer

Well, it looks like it's possible with force:inputField.
But that requires a lot more effort on my part, client side and server side.

Markup

<aura:attribute name="record" type="MyObject__c" default="{'sobjectType': MyObject__c'}"/>
<aura:attribute name="myValue" type="string"/>
<aura:attribute name="editRecordId" type="string"/>
<aura:handler name="change" value="{!v.record.field1__c}" action="{!c.lookupChanged"}/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

<lightning:recordEditForm objectApiName="MyObject__c" recordId="{!v.editRecordId}">
    <lightning:messages />
    <div class="{! $Browser.isPhone ? '' : 'hide-field'}">
        <div class="slds-form-element__label">Mobile Lookup!</div>
            <force:inputField aura:id="mobileField" value="!v.record.field1__c}"/>
        </div>
    <lightning:inputField class="{! $Browser.isPhone ? 'hide-field' : ''}" aura:id="lookupField" fieldName="field1__c" value="{!v.myValue}" onchange="{!c.handleInputChanged}"/>
    <lightning:button variant="brand" type="submit" name="save" label="Save"/>
</lightning:recordEditForm>  

So what we're doing here is a few things:

  1. We may or may not be editing a record. That is decided by the lightning:recordEditForm> based on whether editRecordId has a value. Tangential, but still important.
  2. We made sure to set a default for the force:inputField to refer to. A common mistake to forget that.
  3. We are listening for changes on the force:input field, so we can dynamically set the value inside the editRecordForm.
  4. We are hiding the irrelevant field based on the browser size. (In the markup we have two fields, but it will always only display one). We are using CSS to hide the fields (display: none) so that they will not disappear from the DOM and cause problems.

Controller

doInit : function(component, event, helper) {
    if ($A.get("$Browser.isPhone")){
    var objId = component.get("v.editRecordId");
        if (objId){
            var action = component.get("c.getObject");
            var fields = " Id, field1__c, field1__r.Name ";
            action.setParams({objId: objId, objectType: "MyObject__c", fields: fields});
            action.setCallback(this, function(response){
                var state = response.getState();
                if (state === "SUCCESS"){
                    var returnedObj = response.getReturnValue();
                    component.set("v.record", returnedObj);
                    //commented lines are not strictly required for minimum functionality.
                    //But you might want them for something else.
                    //var inputValue = [{
                        //type: 'ChildObject__c',
                        //id: returnedObj.Id,
                        //label: returnedObj.field1__r.Name,
                        //record: returnedObj.field1__r
                    //}];
                    //var forceInput = component.find("mobileField").get("v.body")[0];
                    //forceInput.set("v.values", inputValue);
                }
                else{
                    console.error(response.getError()[0]["message"]);
                }
            });
            $A.enqueueAction(action);
        }
    }
},
lookupChanged: function(component, event, helper){
    if ($A.get("$Browser.isPhone")){
        component.set("v.myValue", component.get("v.record.field1__c"));
    }
}  

So we have two functions there:
1. doInit which retrieves the values required if we are editing the form (Apex code to follow). Notice here that in the callback function we are doing a lot of work to make the value of the field from the database visible to the user (in the case of edit).
2. lookupChanged which sets the value of the field inside the lightning:recordEditForm so that the submit button will work (this is why we are using CSS to hide that inputField, so that our code can set its value).

Apex controller

@AuraEnabled
public static SObject getObject(String objId, String objectType, String fields){
    if (String.isBlank(objId) || !(objId instanceOf Id)){
        throw new AuraHandledException('no id');
    }
    String query = 'SELECT ';
    query += fields;
    query += ' FROM ' + objectType;
    query += ' WHERE Id = :objId ';
    try{
        List<SObject> objects = Database.query(query);
        if (objects != null && objects.size() > 0){
            return objects[0];
        }
        else{
            throw new AuraHandledException('No record retrieved');
        }
    }
    catch(Exception e){
        throw new AuraHandledException(e.getMessage());
    }
}

EDIT:
After additional poking and prodding, it turns out that some of the code in the Javascript controller might be redundant. I commented it out, because in some cases you might need/want it.
Think carefully if you need it though, component.find is probably a very expensive function call, much more so than component.get.

Related Topic