0

I am building a lightning component to create/edit custom objects. I've been able to do this with force:inputFields in the past but unfortunately ran into a component error that the new recommended lightning inputs/inputFields solves.

The parent lightning component of this component contains:

<aura:attribute name="objs" type="Admin_Panel__c[]" default="[]"/>

                    <tbody>    
                        <aura:iteration items="{!v.objs}" var="item" indexVar="index">
                            <c:LightningDriverRow obj="{!item}" rowIndex="{!index}"/>
                        </aura:iteration>
                    </tbody>

Where the LightningDriverRow component serves as a form in a table row to edit each object and the code in the component is along the lines of:

<aura:attribute name="obj" type="Admin_Panel__c" default="{'sobjectType':'Admin_Panel__c'}"/>

    <tr>
        <td>
            <lightning:recordEditForm objectApiName="Admin_Panel__c">
                <lightning:input value="{!v.obj.Departure_Time__c}" aura:id="input" fieldName="Departure_Time__c" type="time" label="Time" name="time" variant="label-hidden"/>
            </lightning:recordEditForm>
        </td>
        <td>
            <lightning:recordEditForm objectApiName="Admin_Panel__c">
                <lightning:input value="{!v.obj.Arrival_Time__c}" aura:id="input" fieldName="Arrival_Time__c" type="time" label="Time" name="time" variant="label-hidden"/>
            </lightning:recordEditForm>
        </td>
        <td>
            <lightning:recordEditForm objectApiName="Admin_Panel__c">
                <lightning:inputField value="{!v.obj.From_Location__c}" aura:id="input" fieldName="From_Location__c" variant="label-hidden"/>
            </lightning:recordEditForm>
        </td>
    </tr>

This solution has worked great for creating records and does technically work to edit records but the problem I'm running into is that when I pass in my custom object I receive from the back end to edit the time fields display the millisecond value that's stored in the time field like so: Example of time error on row component I've spent some time surfing the web trying to find someone that has run into a similar issue but I couldn't find anything so making a post here is my last resort. Most of what I found mentioned adding type="time", label="Time", and name="time" but this didn't solve my issue.

I'm sure I could resort to some kind of ui input field of some sort but I would like to get more comfortable with the new lightning elements with the force and ui inputs being deprecated and often times buggy. I also find it odd that there's no time field support for lightning:inputFields just date and datetime support. Anyone's input that has run into this issue or has some useful advice for moving forward would be greatly appreciated. I am able to use the dropdown to select a new time in the correct format, it's just when passing in objects initially that the millisecond value displays.

EDIT: Arrival_Time__c and Departure_Time__c are Time values on the back end.

~Holden

1
  • 1
    What is the datatype for Arrival_Time__c and Departure_Time__c? – Nagendra Singh Jan 14 at 1:47
1

So I managed to come up with a hacky JavaScript solution that solved the issue. Not exactly an ideal solution but it works for the time being.

In my LightningDriverRow component I added an init handler to run this code that formats milliseconds into a string HH:mm a format to display in the UI:

    doInit : function(component, event, helper) {
        if (component.get("v.isEdit")) {
            component.getEvent('isEditEvt').fire();
            
            const DTms = component.get("v.obj.Departure_Time__c");            
            const DTdate = new Date();
            DTdate.setHours(0,0,0,DTms);
            const DTtime = $A.localizationService.formatTime(DTdate); //DTtime Format: HH:mm:ss a
            const DTmeridiem = DTtime.split(' ')[1]; //AM or PM string
            const [DThours, DTminutes, DTseconds] = DTtime.split(' ')[0].split(':');
            const formattedDTtime = DThours+':'+DTminutes+' '+DTmeridiem; //formattedDTtime Format: HH:mm a
            console.log(formattedDTtime);
            component.set("v.obj.Departure_Time__c", formattedDTtime);
            
            const ATms = component.get("v.obj.Arrival_Time__c");
            const ATdate = new Date();
            ATdate.setHours(0,0,0,ATms);
            const ATtime = $A.localizationService.formatTime(ATdate); //ATtime Format: HH:mm:ss a
            const ATmeridiem = ATtime.split(' ')[1]; //AM or PM string
            const [AThours, ATminutes, ATseconds] = ATtime.split(' ')[0].split(':');
            const formattedATtime = AThours+':'+ATminutes+' '+ATmeridiem; //formattedATtime Format: HH:mm a
            component.set("v.obj.Arrival_Time__c", formattedATtime);
        }
    },

I found I can create a date instance and pass the millisecond value into the .setHours() method followed by passing that date into the .formatTime() localizationService method to get the values I need to create the proper time format.

Although, when the user doesn't edit the time fields the HH:mm a string format is stored on the object so to handle saving the time value for the back end we need to then check if the time value is still in this format then convert it to a HH:mm:ss.SSS format where hours can be up to 23.

So in the parent component helper method to save the objects, before I send the objects to the back end I have this loop to account for the time values and lookup relationship values:

        for (var indexVar = 0; indexVar < objs.length; indexVar++) {
            //Clear additional data in look to only set Id and avoid "Cannot specify both an external ID reference and SalesForce Id" Error
            //This must be done for all lookups/reference Fields
            if (objs[indexVar].Departure_Time__c.includes('M')) {
                objs[indexVar].Departure_Time__c = helper.formatTimeString(objs[indexVar].Departure_Time__c);
            }
            if (objs[indexVar].Arrival_Time__c.includes('M')) {
                objs[indexVar].Arrival_Time__c = helper.formatTimeString(objs[indexVar].Arrival_Time__c);
            }
            objs[indexVar].Equipment__c = component.get('v.equipmentId');
            objs[indexVar].Section_Header__c = component.get('v.obj.Section_Header__c');
            objs[indexVar].Active__c = component.get('v.equipmentName')+'-'+objs[indexVar].Section_Header__c+'-'+indexVar;
            objs[indexVar].Driver__r = null;
            objs[indexVar].From_Location__r = null;
            objs[indexVar].Destination__r = null;
        }

The formatTimeString helper: (I didn't need to account for seconds in my case)

    formatTimeString : function(timeString) {
        if (timeString.split(' ')[1] === 'AM') {
            let [hours, minutes] = timeString.split(' ')[0].split(':'); 
            if (hours == '12') {
                hours = '0';
            }

            if (hours.length === 1) {
                return '0'+hours+':'+minutes+':00.000';
            } else {
                return hours+':'+minutes+':00.000';
            }
        } else if (timeString.split(' ')[1] === 'PM') {
            let [hours, minutes] = timeString.split(' ')[0].split(':')
            if (hours != '12') {
                hours += 12;
            }
            
            return hours+':'+minutes+':00.000';
        }
    },

Some other issues came up that had to do with 'cloning' a row (setting and pushing a new row object to the values of the one being cloned) that I was able to handle by calculating the milliseconds of the HH:mm a time string and setting the new objects time values to a millisecond value as when it gets passed into the LightningDriverRow component the milliseconds get formatted.

In my case the helper method looks like this for those who are interested: (The timeString parameter is expecting the same format that the formatTimeString helper returns)

    timeToMilliseconds : function(timeString) {
        let milliseconds = 0;
        const [hours, minutes] = timeString.split(':');

        milliseconds += hours * 3600000;
        milliseconds += minutes * 60000;
        return milliseconds;
    },

~Holden

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.