1

I am creating a lightning component to use as a custom New Event creation form. After hitting the Save button I run through some custom validation in the Helper to make sure all of the SObject fields are completed correctly. If there is an error, I display a Toast Message.

In testing, I found that once the Toast Message is displayed, users can no longer add or edit fields. They can type in the UI fields, but the values are not added to the SObject attribute.

Wondering if this expected behavior, a bug, or if I am doing something wrong in the code?

Here is the relevant code:

Component

    <aura:component controller="EventController" implements="force:appHostable,flexipage:availableForAllPageTypes" access="global">
<!--Attributes -->
<aura:attribute name="allDay" type="Boolean" default="False"/>
<aura:attribute name="recurring" type="Boolean" default="False"/>
<aura:attribute name="repeatType" type="string"/>
<aura:attribute name="runningUser" type="string"/>
<aura:attribute name="week" type="Boolean" default=""/>
<aura:attribute name="DofM" type="Boolean" default=""/>
<aura:attribute name="Month" type="Boolean" default=""/>
<aura:attribute name="startType" type="String" default="startdatetime"/>
<aura:attribute name="startDate" type="Date"/>
<aura:attribute name="canceled" type="Boolean" default=""/>
<aura:attribute name="phone" type="Boolean" default=""/>
<aura:attribute name="events" type="SObject[]"/>
<aura:attribute name="newEvent" type="SObject" default="{ 'sobjectType': 'Event', 'RecurrenceTimeZoneSidKey': 'America/New_York'}"/>
<aura:attribute name="eventRelations" type="SObject[]"/>
<aura:attribute name="newEventRelation" type="SObject" default="{ 'sobjectType': 'EventRelation'}"/>
<aura:attribute name="users" type="User[]"/>
<aura:attribute name="assignedTo" type="string"/>
<aura:attribute name="invitee" type="string"/>
<aura:attribute name="what" type="string"/>
<aura:attribute name="eventId" type="Id"/>

<!--Handlers -->
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<aura:handler name="change" value="{!v.newEvent.StartDateTime}" action="{!c.startDate}"/> 
<aura:handler name="change" value="{!v.newEvent.RecurrenceEndDateOnly}" action="{!c.onEndDate}"/> 


<div class="container">    
<!--Assigned To Form Element -->
<ui:inputSelect aura:id="opt" label="Assigned To" multiple="false" change="{!c.onSelectChange}" value="{!v.newEvent.OwnerId}" required="true">
    <aura:iteration items="{!v.users}" var="user">
           <ui:inputSelectOption text="{!user.Id}" label="{!user.Name}" aura:id="users"/>
    </aura:iteration>
</ui:inputSelect>

<!--Subject Form Element -->    
<ui:inputText aura:id="subject" label="Subject" required="true" value="{!v.newEvent.Subject}"/>

<!--All Day Event Form Element -->
<ui:inputCheckbox aura:id="AllDay" label="All Day Event" change="{!c.onCheck}" value="{!v.newEvent.IsAllDayEvent}"/>

<!--Renders Date or DateTime UI Input based on All Day Event Check Box -->    
    <aura:if isTrue="{!v.allDay}">

<!--Start/End Date Form Elements -->
<ui:inputDate aura:id="startdate" label="Start Date" value="{!v.newEvent.StartDateTime}" displayDatePicker="true" required="true"/>
<ui:inputDate aura:id="enddate" label="End Date" value="{!v.newEvent.EndDateTime}" displayDatePicker="true" required="true"/>
        <aura:set attribute="else">

<!--Start/End DateTime Form Elements -->
<ui:inputDateTime aura:id="startdatetime" label="Start Date" class="field" value="{!v.newEvent.StartDateTime}" displayDatePicker="true" required="true"/>
<ui:inputDateTime aura:id="enddatetime" label="End Date" class="field" value="{!v.newEvent.EndDateTime}" displayDatePicker="true" required="true"/>    
    </aura:set>
    </aura:if>

<!--Matter/Prospect Lookup Form Element-->

<c:mpLook what="{!v.newEvent.WhatId}"/>


<!--Invitee Form Element -->
<ui:inputSelect aura:id="opt2" label="Select Invitees" multiple="true" change="{!c.onSelectChange2}">
    <aura:iteration items="{!v.users}" var="user">
           <ui:inputSelectOption text="{!user.Id}" label="{!user.Name}"/>
    </aura:iteration>
</ui:inputSelect>




<!--Event Type Form Element-->
<ui:inputSelect aura:id="eventType" label="Event Type" multiple="False" required="true" value="{!v.newEvent.Event_Type__c}" change="{!c.onConsult}">
    <ui:inputSelectOption text="" label="None" value="true"/>
    <ui:inputSelectOption text="Client Event - In Office" label="Client Event - In Office"/>
    <ui:inputSelectOption text="Potential Client" label="Potential Client"/>
    <ui:inputSelectOption text="Court" label="Court"/>
    <ui:inputSelectOption text="Trial" label="Trial"/>
    <ui:inputSelectOption text="Cancelled" label="Cancelled"/>
    <ui:inputSelectOption text="Travel Time" label="Travel Time"/>
    <ui:inputSelectOption text="Work Time" label="Work Time"/>
    <ui:inputSelectOption text="Firm Administration Event" label="Firm Administration Event"/>
    <ui:inputSelectOption text="Out of Office Client Meeting" label="Out of Office Client Meeting"/>
    <ui:inputSelectOption text="Large Conference Room" label="Large Conference Room"/>
    <ui:inputSelectOption text="Small Conference Room" label="Small Conference Room"/>
    <ui:inputSelectOption text="Professional Development Event" label="Professional Development Event"/>
    <ui:inputSelectOption text="Business Development Event" label="Business Development Event"/>
    <ui:inputSelectOption text="Collection, Fee Arb, Ethics" label="Collection, Fee Arb, Ethics"/>
    <ui:inputSelectOption text="Personal" label="Personal"/>
    <ui:inputSelectOption text="Vacation" label="Vacation"/>
    <ui:inputSelectOption text="Lunch" label="Lunch"/>
</ui:inputSelect>    


<!--Recurring Checkbox Form Element -->

<ui:inputCheckbox aura:id="recurring" label="Creating Recurring Series of Events" change="{!c.onCheck2}" value="{!v.newEvent.IsRecurrence}"/>

<!--Repeat Type Form Element -->
    <aura:if isTrue="{!v.recurring}">
        <br></br>
    <label>Repeat Every</label>
<ui:inputSelect aura:id="repeatType" class="repeatType" label="" multiple="False" change="{!c.onCheck3}" value="{!v.newEvent.RecurrenceType}">
    <ui:inputSelectOption text="RecursDaily" label="Day"/>
    <ui:inputSelectOption text="RecursWeekly" label="Week" value="true"/>
    <ui:inputSelectOption text="RecursMonthly" label="Month"/>
    <ui:inputSelectOption text="RecursYearly" label="Year"/>
</ui:inputSelect>

<!--RecurrenceInterval Form Element -->
<ui:inputText aura:id="interval" class="hide" value="{!v.newEvent.RecurrenceInterval}"/>


<!--Day of Week Repeat Form Element -->
        <aura:if isTrue="{!v.week}">
<ui:inputSelect aura:id="week" label="on" multiple="False" updateOn="change" value="{!v.newEvent.RecurrenceDayOfWeekMask}">
    <ui:inputSelectOption text="1" label="Sunday"/>
    <ui:inputSelectOption text="2" label="Monday"/>
    <ui:inputSelectOption text="4" label="Tuesday"/>
    <ui:inputSelectOption text="8" label="Wednesday"/>
    <ui:inputSelectOption text="16" label="Thursday"/>
    <ui:inputSelectOption text="32" label="Friday"/>
    <ui:inputSelectOption text="64" label="Saturday"/>
    <ui:inputSelectOption text="" label=""/>
</ui:inputSelect>
        <aura:set attribute="else">
            <div></div>
        </aura:set>
    </aura:if>
<!--Day of Month Repeat Form Element -->
        <aura:if isTrue="{!v.DofM}">
<ui:inputSelect aura:id="DofM" label="on the" multiple="False" updateOn="change" value="{!v.newEvent.RecurrenceDayOfMonth}">
    <ui:inputSelectOption text="1" label="1st"/>
    <ui:inputSelectOption text="2" label="2nd"/>
    <ui:inputSelectOption text="3" label="3rd"/>
    <ui:inputSelectOption text="4" label="4th"/>
    <ui:inputSelectOption text="5" label="5th"/>
    <ui:inputSelectOption text="6" label="6th"/>
    <ui:inputSelectOption text="7" label="7th"/>
    <ui:inputSelectOption text="8" label="8th"/>
    <ui:inputSelectOption text="9" label="9th"/>
    <ui:inputSelectOption text="10" label="10th"/>
    <ui:inputSelectOption text="11" label="11th"/>
    <ui:inputSelectOption text="12" label="12th"/>
    <ui:inputSelectOption text="13" label="13th"/>
    <ui:inputSelectOption text="14" label="14th"/>
    <ui:inputSelectOption text="15" label="15th"/>
    <ui:inputSelectOption text="16" label="16th"/>
    <ui:inputSelectOption text="17" label="17th"/>
    <ui:inputSelectOption text="18" label="18th"/>
    <ui:inputSelectOption text="19" label="19th"/>
    <ui:inputSelectOption text="20" label="20th"/>
    <ui:inputSelectOption text="21" label="21st"/>
    <ui:inputSelectOption text="22" label="22nd"/>
    <ui:inputSelectOption text="23" label="23rd"/>
    <ui:inputSelectOption text="24" label="24th"/>
    <ui:inputSelectOption text="25" label="25th"/>
    <ui:inputSelectOption text="26" label="26th"/>
    <ui:inputSelectOption text="27" label="27th"/>
    <ui:inputSelectOption text="28" label="28th"/>
    <ui:inputSelectOption text="29" label="29th"/>
    <ui:inputSelectOption text="30" label="30th"/>
    <ui:inputSelectOption text="31" label="31st"/>
    <ui:inputSelectOption text="" label=""/>
</ui:inputSelect>
            <aura:set attribute="else">
            <div></div>
        </aura:set>
    </aura:if>
<!--Month Repeat Form Element --> 
            <aura:if isTrue="{!v.Month}">
<ui:inputSelect aura:id="month" label="of" multiple="False" updateOn="change" value="{!v.newEvent.RecurrenceMonthOfYear}">
    <ui:inputSelectOption text="January" label="January"/>
    <ui:inputSelectOption text="February" label="February"/>
    <ui:inputSelectOption text="March" label="March"/>
    <ui:inputSelectOption text="April" label="April"/>
    <ui:inputSelectOption text="May" label="May"/>
    <ui:inputSelectOption text="June" label="June"/>
    <ui:inputSelectOption text="July" label="July"/>
    <ui:inputSelectOption text="August" label="August"/>
    <ui:inputSelectOption text="September" label="September"/>
    <ui:inputSelectOption text="October" label="October"/>
    <ui:inputSelectOption text="November" label="November"/>
    <ui:inputSelectOption text="December" label="December"/>
    <ui:inputSelectOption text="" label=""/>
</ui:inputSelect>
                <aura:set attribute="else">
            <div></div>
        </aura:set>
    </aura:if>

        <!--Recurs Start/End Dates Form Element -->
<ui:inputDate aura:id="recurrenceStartDateTime" class="recurrence hide" displayDatePicker="true" value="{!v.newEvent.RecurrenceStartDateTime}"/>    
<ui:inputDate aura:id="recurrenceEndDateOnly" label="End Date" displayDatePicker="true" required="true" value="{!v.newEvent.RecurrenceEndDateOnly}"/>
        <br></br>
      <aura:set attribute="else">
            <div></div>
        </aura:set>
    </aura:if>


<!--Save Event Button-->

<ui:button class="save" label="Save" press="{!c.createEvent}"/>

    </div>

</aura:component>

Controller

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


        var newEvent = component.get("v.newEvent");
        var newEventRelation = component.get("v.newEventRelation");
        helper.validate(component, newEvent, helper);
        //helper.createEvent(component, newEvent);


},



    doInit : function(component, event, helper) {

        var action = component.get("c.getRunningUser");
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                component.set("v.runningUser", response.getReturnValue());
                console.log(component.get("v.runningUser"));
                helper.getMyUsers(component);
            }
        });
        $A.enqueueAction(action);

    },


    startDate : function(component, event, helper) {

        var selected = component.get("v.newEvent.StartDateTime");
        console.log(selected);
        var startMin = selected.substring(0,11);
        var hour = selected.substring(11,13);
        var min = selected.substring(14,16);
        var endMin = selected.substring(16);

        console.log(startMin, hour, min, endMin);

            var hourNum = parseInt(hour);

            var hourNew = hourNum + 1;
            var h = hourNew.toString();
        if(h.length<2){
            h = "0" + h;
        }

            var end = startMin + h + ":" + min + endMin;


        console.log(end);
        var allDay = component.find("AllDay").get("v.value");
        console.log(allDay);
        if(allDay === true){
        var target = component.find("enddate");
        target.set("v.value", selected);
            }
        else{
            var target3 = component.find("enddatetime");
        target3.set("v.value", end);
        }
        var checkCmp = component.find("recurring");
        var recur =checkCmp.get("v.value");

        if (recur === true){


         var target2 = component.find("recurrenceStartDateTime");
         target2.set("v.value", selected); 
        }

    },

    onSelectChange : function(component, event, helper) {

        var selected = component.find("opt").get("v.value");
        component.set("v.assignedTo", selected);
        console.log(selected);
    },

    onSelectChange2 : function(component, event, helper) {

        var selected = component.find("opt2").get("v.value");
        var sel = selected.split(";");
        component.set("v.invitee", selected);
        console.log(selected, selected.length, sel, sel.length);
    },



    onCheck: function(cmp, evt) {
         var checkCmp = cmp.find("AllDay");
         cmp.set("v.allDay", ""+checkCmp.get("v.value"));
        if (checkCmp.get("v.value")==='startdatetime') {
            cmp.set("v.startdType", "startdatetime");
        }
        else {
            cmp.set("v.startType", "starttime");
        }

     },

    onCheck2: function(cmp, evt) {
        var checkCmp = cmp.find("recurring");
         var selected = cmp.get("v.newEvent.StartDateTime");

        if (selected != null){

         cmp.set("v.recurring", ""+checkCmp.get("v.value"));
         cmp.set("v.week", "True");

         var target2 = cmp.find("recurrenceStartDateTime");
         target2.set("v.value", selected); 

         var interval = cmp.find("interval");
         interval.set("v.value", 1);
        }
        else{
            checkCmp.set("v.value", "false");
            var toastEvent = $A.get("e.force:showToast");
    toastEvent.setParams({
        "title": "Error!",
        "message": "Please add a Start Date to this Event before creating a recurrence.",
        "duration": "5000"
    });
    toastEvent.fire();
        }
         },

    onCheck3: function(component, evt, helper) {
         helper.onCheck3(component, evt, helper);
     },

    onEndDate: function(component, event, helper) {
        helper.onEndDate(component, event, helper);
    },




})

Helper

   ({
    createEvent: function(component, event) {
    //Save the event and update the view
    this.upsertEvent(component, event, function(a) {
        var events = component.get("v.events");
        events.push(a.getReturnValue());
        component.set("v.events", events);

    });
        },
            showToast : function(component, event) {
            var toastEvent = $A.get("e.force:showToast");
            toastEvent.setParams({
                "title": "Success!",
                "message": "The record has been created.",
                "duration": "5000"
            });
            toastEvent.fire();
            this.refresh(component, event);
        },

        upsertEvent : function(component, event, callback) {

      var action = component.get("c.saveEvent");
      action.setParams({ 
          'eventObj': event
      });
      if (callback) {
          action.setCallback(this, callback);
      }
        action.setCallback(this, function(response) {
            var responseVal = response.getReturnValue();
            if(component.isValid() && response.getState() === "SUCCESS") {

                var eId = responseVal.Id;
                component.set("v.eventId", eId);
                var newERel = component.get("v.newEventRelation");
                var invitee = component.get("v.invitee");
                console.log(invitee);
                if(invitee != null){
                this.createEventRelation(component, newERel, eId, invitee);
                }
                else{
                    this.showToast(component, event);
                }
            }
            else{
                var toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams({
            "title": "Oops!",
            "message": "It looks like there was a problem. Please try again.",
            "duration": "3000"
        });
        toastEvent.fire();
            }
            });
      $A.enqueueAction(action);
    },

    createEventRelation: function(component, event, eId, invitee) {
    //Save the event and update the view
    this.upsertEventRelation(component, event, eId, invitee, function(a) {
        var events = component.get("v.eventRelations");
        events.push(a.getReturnValue());
        component.set("v.eventRelations", events);

    });
},
upsertEventRelation : function(component, event, eId, invitee, callback) {
  var action = component.get("c.saveEventRelation");
  action.setParams({ 
      'eventRel': event,
      'eId' : eId,
      'invitee': invitee
  });
  if (callback) {
      action.setCallback(this, callback);
  }
    action.setCallback(this, function(response) {
        if(component.isValid() && response.getState() === "SUCCESS") {
             this.showToast(component, event);
        }
        else{
            var toastEvent = $A.get("e.force:showToast");
    toastEvent.setParams({
        "title": "Oops!",
        "message": "It looks like there was a problem. Please try again.",
        "duration": "3000"
    });
    toastEvent.fire();
    helper.refresh(component, event);
        }
    });
  $A.enqueueAction(action);
},


getMyUsers : function(cmp) {
        // Load all user data
        var action = cmp.get("c.getUsers");
        action.setCallback(this, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                cmp.set("v.users", response.getReturnValue());
                cmp.find("opt").set("v.value", cmp.get("v.runningUser"))
            }
        });
        $A.enqueueAction(action);


    },








     onCheck3: function(component, evt, helper) {
        var type = component.find("repeatType").get("v.value");
        var week = component.find("week");
        var DofM = component.find("DofM");
        var month = component.find("month");
        var interval = component.find("interval");
        console.log(week, DofM, month);
        if (type === 'RecursDaily') {
            if(week != null){week.set("v.value", "");}
            if(DofM != null){DofM.set("v.value", "");}
            if(month != null){month.set("v.value", "");}
            component.set("v.week", "");
            component.set("v.DofM", "");
            component.set("v.Month", "");
            interval.set("v.value", 1);

        }
        else if (type === 'RecursWeekly') {
            if(DofM != null){DofM.set("v.value", "");}
            if(month != null){month.set("v.value", "");}
            component.set("v.week", "True");
            component.set("v.DofM", "");
            component.set("v.Month", "");
            interval.set("v.value", 1);

        }
        else if (type === 'RecursMonthly') {
            if(week != null){week.set("v.value", "");}
            if(month != null){month.set("v.value", "");}
            component.set("v.week", "");
            component.set("v.DofM", "True");
            component.set("v.Month", "");
            interval.set("v.value", 1);

        }
        else if (type === 'RecursYearly') {
            if(week != null){week.set("v.value", "");}
            component.set("v.week", "");
            component.set("v.DofM", "True");
            component.set("v.Month", "True");
            interval.set("v.value", null);

        }
            else{
            if(week != null){week.set("v.value", "");}
            if(DofM != null){DofM.set("v.value", "");}
            if(month != null){month.set("v.value", "");}
            component.set("v.week", "");
            component.set("v.DofM", "");
            component.set("v.Month", "");
            interval.set("v.value", 1);

            }

     },

    onEndDate : function(component, event) {
        var start = component.get("v.newEvent.RecurrenceStartDateTime");
        var end = component.get("v.newEvent.RecurrenceEndDateOnly");
        var type = component.get("v.newEvent.RecurrenceType");
        var diff = daysBetween(start, end);
        console.log(start, end, type, diff);



        function treatAsUTC(date) {
            var result = new Date(date);
             result.setMinutes(result.getMinutes() - result.getTimezoneOffset());
            return result;
        }

       function daysBetween(startDate, endDate) {
            var millisecondsPerDay = 24 * 60 * 60 * 1000;
            var diff = (treatAsUTC(endDate) - treatAsUTC(startDate)) / millisecondsPerDay;
           return diff;

        }

        if((diff > 95 && type === "RecursDaily") || (diff > 300 && type === "RecursWeekly") || (diff > 1800 && type === "RecursMonthly") || (diff > 3500 && type === "RecursYearly")) {
             var toastEvent = $A.get("e.force:showToast");
                toastEvent.setParams({
                    "title": "Oops!",
                    "message": "Please select an End Date that isn't so far in the future.",
                    "duration": "5000"
                });
                toastEvent.fire();
            component.find("recurrenceEndDateOnly").set("v.value", "");
        }
        else if (diff < -1){
            var toastEvent = $A.get("e.force:showToast");
                toastEvent.setParams({
                    "title": "Oops!",
                    "message": "Please select an End Date that is after the Start Date.",
                    "duration": "5000"
                });
                toastEvent.fire();
            component.find("recurrenceEndDateOnly").set("v.value", "");
        }
    },


    validate: function(component, newEvent, helper) {
        var subject = newEvent.Subject;
        var start = newEvent.StartDateTime;
        var end = newEvent.EndDateTime;
        var type = newEvent.Event_Type__c;
        var recur = newEvent.IsRecurrence;
        var relate = newEvent.WhatId;
        console.log(subject, start, end, type, recur, relate);
        if(recur != true && (subject === undefined || start === undefined || end === undefined || type === "")){
            var toastEvent = $A.get("e.force:showToast");
                            toastEvent.setParams({
                                    "title": "Error!",
                                    "message": "Please complete all required fields.",
                                    "duration": "5000"
                                });
                            toastEvent.fire();
            }

        else if(recur === true && (subject === undefined || start === undefined || end === undefined || type === "" || newEvent.RecurrenceEndDateOnly === undefined)){
             var toastEvent = $A.get("e.force:showToast");
                            toastEvent.setParams({
                                    "title": "Error!",
                                    "message": "Please complete all required fields.",
                                    "duration": "5000"
                                });
                            toastEvent.fire();
        }
        else if((relate === null || relate === undefined) && (type != 'Personal' || type != 'Lunch' || type != 'Vacation')){
            var toastEvent = $A.get("e.force:showToast");
                            toastEvent.setParams({
                                    "title": "Error!",
                                    "message": "Please relate this to a Matter or Prospect.",
                                    "duration": "5000"
                                });
                            toastEvent.fire();
        }
            else{
               helper.createEvent(component, newEvent); 
            } 

    },

    refresh : function(cmp, event, helper){
        $A.get('e.force:refreshView').fire();
    },
})

UPDATE As I continue to test this the behavior gets more unusual. Let's say I have 5 required field: 1 text field, 2 date fields and 2 picklist fields. If I press the save button with all field blank, the validation catches and the Toast message displays. Then, when I go to enter data in any field, nothing gets pushed to the attribute.

But if I first enter text in the text field, then Save, the message fires. I can then edit one of the picklist fields and edit the text field, but the others don't register (again, I see the changes in the UI, but the SObject attribute is not updated).

UPDATE2 All the code is posted above.

  • the message does not lock attribute values. In order to get force:toastMessage out of equation, just replace all toas messages with console log and result will be the same. – Pavlonator Nov 8 '16 at 19:24
  • 1
    when you say sObject attributes not updated - do you expect them updated in table, same way as you would have with visualforce or you track component attribute and fields of this object are not updated? If you track serverside for changes - then "send to server" part is missing. if the values are nota pplied to object - then this is "expando" problem. – Pavlonator Nov 8 '16 at 19:27
  • 1
    Interesting, I did take out the Toast Message and the values still won't update. – mvenn Nov 8 '16 at 20:24
  • What I mean by the SObject attribute value: I have an attribute (in the component above) that is called newEvent. As user fills out the form, the value in each field are set to this attribute (eg Subject field sets newEvent.Subject). The values are stored in that attribute and then passed to the Server for record creation. After going through the Validate function, some new values are not added to the newEvent attribute. Never encountered an expando problem. Thoughts on how to get around it in this context? – mvenn Nov 8 '16 at 20:35
  • 1
    for that we would need to see your cmp code how you bind attribute subfields to inputs, also controller/helper code to see how these values are sent to server after sucessful validation. – Pavlonator Nov 8 '16 at 20:43

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.