5

I need to perform a simple business task.

I need to alter date property before sending it to server to minimize server response time.

I need to use LEX (Lightning Experience).

I have declared my Date property

<aura:attribute name="month" type="Date"/>

but I have noticed that it is stored as a string like "2016-09-08T12:01:11.832Z".

I need to process this string (in my case, I need to convert to start of the month) and I would like to save it in the property.

My Lightning application code looks like following

<aura:application>
<aura:attribute name="month" type="Date"/>
<aura:handler name="change" value="{!v.month}" action="{!c.monthChange}"/>
</aura:application>

I have intentionally omitted all unrelated code. My javascript controller looks like following

({
monthChange: function( component, event, helper ) {
    debugger;
    if ( window.console ) {
        console.log(component.get("v.month"));
    }
    var s = component.get("v.month");
    var date = new Date(s);
    if ( date.getDate() != 1) {
        var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
//Something has gone wrong. rerender threw an error in 'markup://ui:inputDate' [TypeError: value.split is not a function]
//Failing descriptor: {markup://ui:inputDate}.
//Please try again.
        //component.set("v.month", firstDay );    

        //FATAL_ERROR System.TypeException: Invalid date: 8/1/2016
        //component.set("v.month", firstDay.toLocaleDateString());

        // RangeError: Maximum call stack size exceeded]
        //component.set("v.month", firstDay.toISOString().split('T')[0]);
        // 
        // 
        // Recommended usage is var dateFormat = $A.get("$Locale.dateFormat");
        // but the return value "MMM d, yyyy" doesn't correspond to expected format 'YYYY-MM-DD'
        // 
        // Also $A.localizationService.formatDateTime(date, 'YYYY-MM-DD'); doesn't take the date from parameter
        // but instead return always current date "2016-09-08"
        // 
        // https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/components_l10n.htm

        component.set("v.month", date.getFullYear() + '-' + date.getMonth() + '-01' );    


    }

}
})

My very first attempt was to save Date value inside of Date property like following:

({
monthChange: function( component, event, helper ) {

    var s = component.get("v.month");
    var date = new Date(s);
    var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
    component.set("v.month", firstDay );    

}
})

I received this very misleading error:

Something has gone wrong. rerender threw an error in 'markup://ui:inputDate' [TypeError: value.split is not a function]
Failing descriptor: {markup://ui:inputDate}.
Please try again.

As far as I could guess from this misleading error it says that LEX expect a DATE property value to BE A STRING and it TRIES to SPLIT it.

Ok... let it be. So I tried to change the code to

({
monthChange: function( component, event, helper ) {

    var s = component.get("v.month");
    var date = new Date(s);
    if ( date.getDate() != 1) {
        var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
        component.set("v.month", firstDay.toLocaleDateString());
    }
}
})

This time I received error in apex controller like

FATAL_ERROR System.TypeException: Invalid date: 8/1/2016

I think I should add that I have additional lines like those

<aura:application controller="LexCont">
<aura:handler name="init" value="{!this}" action="{!c.onInit}"/>
<aura:attribute name="projects" type="Project__c[]"/>
<aura:attribute name="project" type="Id"/>

in my application and in my javascript controller

onInit : function(component, event, helper) {
    helper.getProjects( component );


    var d = new Date(2010, 2, 3);
    alert( $A.localizationService.formatDate(d, 'YYYY-MM-DD') );

    alert( $A.localizationService.formatDate(d, 'yyyy-MM-dd') );

    alert( $A.localizationService.formatDate(d, 'yyyy-mm-dd') );
}

and inside of helper

getProjects: function( component ) {
    var action = component.get( "c.getProjects" );

    action.setCallback(this, function(response) {
        var state = response.getState();
        if (component.isValid() && state === "SUCCESS") {
            var projects = response.getReturnValue();
            component.set("v.projects", projects );
            if ( projects.length > 0 ) {
                component.set("v.project", projects[0].Id );                    
            }
        }
    });
    $A.enqueueAction(action);
}

and inside my Apex Controller

public class LEXCont {
    @AuraEnabled
    public static ProjectModel getProjectModel( Id pId, String selectedMonth ){
        return Services.getProjectModel(pId, selectedMonth);
    }
}

My next attempt was

({
monthChange: function( component, event, helper ) {
var s = component.get("v.month");
var date = new Date(s);
if ( date.getDate() != 1) {
    var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
    component.set("v.month", firstDay.toISOString().split('T')[0]);
}
}
})

Method firstDay.toISOString() returns date in the same format it was when I read the value of property if I skip the time part. However, I received this time error

RangeError: Maximum call stack size exceeded

Since this method returns date in UTC\GMT format, and the date gets shifted by 1 day in the past each time it is being processed.

My next attempt was following. I started googling and searching for standard methods related to date processing in Lightning. I have read here that recommended usage is

var dateFormat = $A.get("$Locale.dateFormat");
$A.localizationService.formatDateTime(date, dateFormat ); 

However, when I tried to use this approach, the first discrepancy I noticed was that $A.get("$Locale.dateFormat") returns different format than was the date formatted when I retrieved it. It returns "MMM d, yyyy", but the date is expected to be in format 'YYYY-MM-DD' as far as I understand.

Also I noticed that $A.localizationService.formatDateTime(date, 'YYYY-MM-DD'); returns always the current date, not the date passed in parameters. So this attempt

({
monthChange: function( component, event, helper ) {
var s = component.get("v.month");
var date = new Date(s);
if ( date.getDate() != 1) {
    var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
    // Recommended usage is var dateFormat = $A.get("$Locale.dateFormat");
    // but the return value "MMM d, yyyy" doesn't correspond to expected format 'YYYY-MM-DD'
    // 
    // Also $A.localizationService.formatDateTime(date, 'YYYY-MM-DD'); doesn't take the date from parameter
    // but instead return always current date "2016-09-08"
    // 
    // https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/components_l10n.htm

    component.set("v.month", $A.localizationService.formatDateTime(date, 'YYYY-MM-DD') );    
}
}
})

was not working as expecting, showing always 9/8/2016 in datepicker instead of the start of the month.

So I decided to try to do manual formatting like this

({
monthChange: function( component, event, helper ) {
var s = component.get("v.month");
var date = new Date(s);
if ( date.getDate() != 1) {
    var firstDay = new Date(date.getFullYear(), date.getMonth(), 1);
    component.set("v.month", date.getFullYear() + '-' + date.getMonth() + '-01' );    
}
}
})

and this was my last hope and last resort and it worked, but I have ugly code.

So, my first question is the following.

Can we achieve this business requirement by more elegant code using standard features or recommended by Salesforce? (also I wonder why Lightning is still so glitchy that nothing works from the documentation examples - but this would sound like rhetoric question)

My second question is the following.

Why behavior of method $A.localizationService.formatDate(new Date(2010, 2, 3), 'YYYY-MM-DD') differs upon the execution context?

When I run this code from JavaScript developer console

var d = new Date(2010, 2, 3);
alert( $A.localizationService.formatDate(d, 'YYYY-MM-DD') );
alert( $A.localizationService.formatDate(d, 'yyyy-MM-dd') ); 

I see

2010-03-03
2010-03-03

but when I put in javascript controller and open the app it says

2016-09-08
2016-09-08

Why, why the behavior is different???? Does anyone know that? Can anybody explain this behavior?


Update. I was suggested to change in apex controller and services class property type from String to Date and use

component.set("v.month", firstDay.toLocaleDateString());

It seems to be working (no error happen) but selected value on LEX Calendar doesn't correspond to the value selected

selected value on LEX Calendar doesn't correspond to the value selected,

so I ended up using code which gives me desired behavior

gives me desired behavior

component.set("v.selectedMonth", date.getFullYear() + '-' + (date.getMonth()+1) + '-01' ); 

for now, since getMonth() is zero-based function.

Your Answer

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

Browse other questions tagged or ask your own question.