17

We have a number of Apex controllers that are shared between Visualforce and Lightning. When these controllers init record values, there are some cases where no record value exists, so the method uses SObjectType.newSObject(null, true) to spin up a new record for the user to interact with. This was working great. Recently, we have run into two issues with Locker Service:

Unable to read SObject

When the record is sent back to Apex as a method parameter after being initialized via SObjectType.newSObject(), the frame work will always crash with "Unable to read SObject". We have seen this before and the workaround was to set the sobjectType property on the object before setting it as a param. However, this leads into the second issue...

We cannot set fields that did not have a default value

Once we have the record in our lightning component (set to an aura:attribute with a type of custom object) the lightning:input components are not updating fields on the record. Printing out the record will show that no keys exist for those fields when it comes back from Apex (presumably because the fields had null default values). After entering data in lightning:input or ui:inputText fields, no key for the field is added to the object so no data gets set. Additionally, we cannot set the sobjectType property to prevent the "Unable to read SObject" error.

Question

Is this approach no longer supported now that Locker Service is rolling out? If so, what is the correct (supported) approach to initializing empty records for users to enter values for with Locker Service? I think it would help a lot here to understand the Why for some of these restrictions. Knowing that will help developers create designs that better follow best practices while we are waiting for more detailed documentation. If this information already exists somewhere and I just could not find it I would appreciate a link to it.

Update 1: SOQL behavior

I am also seing this behavior with records retrieved via SOQL. I cannot confirm that this wasn't happening before but I am fairly confident. Now, if a record retrieved via SOQL has a null value for a field, that field cannot be set in our lightning component due to the same issues for new records noted above.

  • 1
    Here was my findings. I had to use a class to mimic the structure of the sObject and set all values to ''. For dates, etc where the values cannot be set to an empty string I had to create getters and setters for a string property and the original date etc property to convert back and forth. a real PITA. salesforce.stackexchange.com/questions/156539/… – Eric Mar 13 '17 at 23:54
  • 1
    I just went through this today with an sObject. Luckily everything could be represented as a string so i just used New xyzObject__c(field='') for everything I needed. Not marking as a duplicate in case things have changed and there is a better way to do it. Since the original post I no longer get Unable to read sObject so maybe progress is being made – Eric Mar 13 '17 at 23:55
  • Could you serialize it in javascript and pass it to the method as a string, then deserialize in Apex? – IllusiveBrian Mar 14 '17 at 0:55
  • @Eric Thanks for the info. It seems insane to me that for very basic use cases I am hitting major issues like this. When compared to other front end frameworks the number of hoops that need to be jumped through (with very little guidance) is crazy. We have literally already lost a developer over this kind of stuff since he just got fed up and went to work on in a MEAN stack job. – dsharrison Mar 14 '17 at 0:57
  • 1
    @D.S. yup. I was ranting about the same thing. The most basic stuff requires a lot of workarounds and the complex stuff works pretty well with little work. I guess that is a good thing?? When it all work well it will be a nice framework – Eric Mar 14 '17 at 2:13
3
+100

Salesforce support confirmed that the root cause is same as this Known Issue:

LockerService - New properties added to an object in JavaScript are not visible

Summary
When LockerService is activated new properties added to an object in JavaScript are not visible.

This is being fixed as a part of release 208 which is a summer release scheduled on 22nd April 2017. I am trying to get an early access in our sandbox, will update this space after I test the fix.

  • Thanks for the update. Is Summer '17 really going out on April 22nd? I thought the release wasn't until the end of May (which would give us a very tight window between that release and Locker Service being activated). – dsharrison Mar 31 '17 at 19:16
  • I don't see any release notifications in the trust.salesforce.com and hence asked the same question to support, still waiting to hear back. In the mean while they turned down our ask to get early access to the bug fix because it is part of big implementation (summer release) which unfortunately cannot be back ported. – Bhargav Kodali Mar 31 '17 at 20:19
  • Ugh, all I want is to be able to test this fix to make sure we aren't caught with our pants down when Locker Service goes live in June. – dsharrison Mar 31 '17 at 20:35
  • 2
    @TrevorBliss just commented on this other post (salesforce.stackexchange.com/questions/156539/…) that they are working on a new rollout plan so we will have to see what that means for testing this fix. – dsharrison Mar 31 '17 at 22:08
  • @D.S. According to the blog post, Summer '17 Release Date is June 10 and May 5 is a start of Preview window – o-lexi Apr 5 '17 at 22:14
5

You can initialize an empty record at the client side. For example:

<aura:attribute name="contact" type="Contact" default="{'sobjectType': 'Contact'}"/>

<lightning:input label="First Name" value="{!v.contact.FirstName}"/>
<lightning:input label="Last Name" value="{!v.contact.LastName}"/>

<lightning:button onclick="{!c.createItem}">Create</lightning:button>

Component controller:

createItem : function(component, event, helper) {
    var contact = component.get("v.contact");
    var action = component.get("c.createContact");
    action.setParams({
        "myContact": contact,
    });
    action.setCallback(this, function(a) {
        var result = a.getReturnValue();
        component.set("v.contact", result);
    });
    $A.enqueueAction(action);
}

Apex controller:

@AuraEnabled
public static Contact createContact(Contact myContact) {
    insert myContact;
    return myContact;
}

So in your case, if the initial call to your Apex controller doesn't return any record, I would have the Apex controller return null (instead of instantiating and returning an empty object). On callback, your component controller would then assign either the value returned by the Apex controller (if any) or a new empty object if the Apex call returned null. Something like this:

action.setCallback(this, function(a) {
    var contact = a.getReturnValue() || {sobjectType: 'Contact'};
    component.set("v.contact", contact);
});
  • 2
    The challenge with this is that it was not trivial to have the object initialized with it's default values. I will have to see if the above approach works if we create a new JS object if the Id of the return value is null, then copy all field values by iterating over keys in the returned object. – dsharrison Mar 14 '17 at 2:50
  • 1
    Doug said on another post that next Tuesday's patch fixes some data binding issues so I am hopeful this will be one of them! – dsharrison Mar 17 '17 at 16:11
  • @D.S. Can you please post link to Doug's post? I am facing the same issue, want to track the salesforce patch. – Bhargav Kodali Mar 22 '17 at 5:41
  • 1
    @BhargavKodali It appears this is not fixed from that patch. We are still facing this issue. – dsharrison Mar 30 '17 at 20:35
  • Thanks @D.S., I created a support ticket with Salesforce support, they mentioned the root cause is same as this issue. This will be fixed as a part of release 208 which is a summer release scheduled on 22nd April 2017 – Bhargav Kodali Mar 31 '17 at 18:29

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.