5

Problem Statement

I am trying to pass an instance of an Apex type from LWC into an AuraEnabled method. However, all of the attributes are coming through as null.


MVR

Back End

public with sharing class MyController
{
    @AuraEnabled(cacheable=true)
    public static void doStuff(Model instance)
    {
        system.debug(JSON.serialize(instance));
        // { "sampleAttribute": null }
    }
    public class Model
    {
        @AuraEnabled
        public String sampleAttribute;
    }
}

Front End

import doStuff from "@salesforce/apex/MyController.doStuff"

export default class MyComponent extends LightningElement {
    ...
    const instance = {
        sampleAttribute: "I am populated"
    };
    console.debug(instance);
    // { sampleAttribute: "I am populated" }

    doStuff({ instance })
        .then(result => { ... })
        .catch(error => { ... });
}

Question

Why are my attributes all cleared out by the time they arrive at the back end? They are clearly populated right before I send them. I even tried specifying each attribute via string key just to be sure that wasn't contributing to the problem, but it did not resolve the issue.


Workaround

For now I am using serialization to get the call to work.

Back End

@AuraEnabled(cacheable=true)
public static void doStuff(String payload)
{
    doStuffWithModel((Model)JSON.deserialize(payload, Model.class));
}
public static void doStuffWithModel(Model instance)
{
    // implementation
}

Front End

const instance = {
    sampleAttribute: "I am populated"
};
doStuff({ instance })
    .then(result => { ... })
    .catch(error => { ... });

Expose Apex Methods to Lightning Web Components To expose an Apex method to a Lightning web component, the method must be static and either global or public. Annotate the method with @AuraEnabled.

...

These types are supported for input and output.

  • Primitive—Boolean, Date, DateTime, Decimal, Double, Integer, Long, and String.
  • sObject—standard and custom sObjects are both supported.
  • Apex—an instance of an Apex class. (Most often a custom class.)
  • Collection—a collection of any of the other types.

But the documentation clearly indicates use of Apex types is supported. Using JSON should not be necessary.

3
  • I think NULL is actually expected behavior here. We are venturing into the "pass by reference by value" behavior of Apex (i.e. Java bytecode). developer.salesforce.com/blogs/developer-relations/2012/05/…
    – krigi
    Commented May 28, 2020 at 18:16
  • 1
    That article doesn't mention pass-by-reference between front-end and back-end. That sounds infeasible at best. I do not think it has any bearing on the observed behavior.
    – Adrian Larson
    Commented May 28, 2020 at 18:21
  • 2
    This should work. Oddly, trying to repro this issue gives me an internal server error.
    – sfdcfox
    Commented May 28, 2020 at 19:11

1 Answer 1

9

This fails because the Model class is an inner class. It is probably a bug, but we hit this a while back and found:

  • instances of inner classes can be returned to the client but cannot be passed to the server
  • instances of top level classes can be passed both ways

As per sfdcfox's comment, we most often saw an internal server error when trying to pass an instance of an inner class to an aura enabled method.

The documentation does say:

Custom classes used for component attributes shouldn’t be inner classes or use inheritance. While these Apex language features might work in some situations, there are known issues, and their use is unsupported in all cases.

Whilst this appears to be for Aura code, the LWC documentation indicates LWC has the same behaviour (see the link for "Apex" in the section entitled "Expose Apex Methods to Lightning Web Components"). So whilst it is probably a bug, they have documented to say things can go wrong if you try to use inner classes.

Something else that can cause issues is the use of @AuraEnabled public attributes in your data object, which can be remedied by switching to public properties. This MAY allow you to keep the class as an inner class, but there's no guarantee this will work in all cases. Certainly changing your inner Model class to a top level one like the following will resolve any issues:

public class Model {
    @AuraEnabled
    public String someAttribute { get; set; }
}
14
  • I found the same with your first bullet. Man that's kind of mind blowing. I would agree that's got to be a bug.
    – Adrian Larson
    Commented May 28, 2020 at 20:55
  • 1
    Quite bizarre and very annoying.
    – Phil W
    Commented May 28, 2020 at 20:56
  • I mean that sounds like a relic from Visualforce days...I remember the same constraint for parameters on a component (not page)...
    – Adrian Larson
    Commented May 28, 2020 at 20:59
  • 2
    I do mean update Model to have @AuraEnabled public String sampleAttribute { get; set; }. Again, we found that use of a public attribute does generally work but there were a few edge cases resolved by making it a property, to align with documentation. Note that this is the documentation linked from the LWC docs.
    – Phil W
    Commented May 28, 2020 at 21:34
  • 2
    Adding { get; set; } for inner class attributes did the tirck here as well!
    – GoldenAxe
    Commented Nov 9, 2020 at 23:42

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .