[SalesForce] How to validate multiple properties in lightning components (event livecycle / setter)

I have a question about the architecture and event lifecycle of lightning components: how can I validate more than one depending attributes?

Think about the following example: I have a DateRangePicker component containing a startDate and endDate property. Obviously the startDate should reference a Date which is earlier in time as the endDate. I tried the following:

DateRangePicker.cmp:

<aura:component>

    <!-- attributes -->
    <aura:attribute name="startDate" type="Date" />
    <aura:attribute name="endDate" type="Date" />

    <!-- event handler -->
    <aura:handler name="change" value="{!v.startDate}" action="{!c.onDateChange}" />
    <aura:handler name="change" value="{!v.endDate}" action="{!c.onDateChange}" />

</aura:component>

Whenever either the startDate or endDate properties are changing (through user input, server validation, whatever action appears) I am going to validate that startDate is before endDate:

DateRangePickerController.js:

({
onDateChange: function(component, event, helper) {
    var startDate = component.get("v.startDate");
    var endDate = component.get("v.endDate");

    //validate startDate is before endDate, otherwise change order
    if($A.localizationService.isBefore(endDate, startDate))
    {
        //changing the order of the two properties

        component.set("v.startDate", endDate);
        component.set("v.endDate", startDate);
    }


)}

The problem about this small example is that setting the new values is triggering a change event immediately for each set-operation and calling the same function again.

Another problem is that when you set the dates programmatically like (pseudo code):

    init:function() {
        component.set("v.startDate", new Date());
        component.set("v.endDate", getTomorrow(new Date()));
    }

the value change event is also triggered immediately for the startDate (before the endDate has been set) and again for endDate – so it is hard to determine in the function call if there is another change or not.

I am missing (or not found) a concept about collecting all property changes to validate them at once in a later point in the lifecycle like invalidating/flagging properties and validate them all at a single point. And resolve the conflict about storing the current value to make accessible from the outside and also be able to react on change events when the value did change from outside.

Or could I achieve a similar behavior by using a different technique (private attributes, setter methods, what ever concept is useful)?

Any ideas about architecture are highly appreciated.

Thanks
Chris

Best Answer

So far I found out a good way is the usage of <aura:method> (https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/ref_tag_method.htm?search_text=aura:method

https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_cmp_methods.htm?search_text=aura:method)

Rather bind directly on both change events individual I wrote one method which accepts two parameters for both values:

<aura:method name="setDateRange" action="{!c.onSetDateRange}" access="PUBLIC"
    description="Initializes component with the start and end date of the date range">
    <aura:attribute name="startDate" type="Date" />
    <aura:attribute name="endDate" type="Date" />
</aura:method>

where inside my controller I do the validation logic like

onSetDateRange: function(component, event, helper) {
    //get the passed in date values
    var params = event.getParam("arguments");
    var newStartDate = params.startDate;
    var newEndDate = params.endDate;

    //validate newStartDate is before newEndDate, otherwise change order
    if($A.localizationService.isBefore(newStartDate, newEndDate))
    {
        //change order and store the new values in the component 
        component.set("v.startDate", newEndDate);
        component.set("v.endDate", newStartDate);
    }
    else
    {
        //directly store new values
        component.set("v.startDate", newStartDate);
        component.set("v.endDate", newEndDate);
    }

},

and storing the values in private like attribute way:

<aura:attribute name="startDate" type="Date" access="PRIVATE" />
<aura:attribute name="endDate" type="Date" access="PRIVATE" />

So no more change handler on the attributes.

The downside is that I can only call the method from JavaScript and not directly set values through expressions in the component markup.

Hope that helps Chris

Related Topic