trailhead

Learn About Code Reuse and Apex Controllers

Learning Objectives

After completing this unit, you’ll be able to:
  • Describe the difference between inheritance and composition for code reuse.
  • Describe the architectural and syntactic differences between Visualforce and Lightning components Apex controllers.

Inheritance vs. Composition

Although we could easily spend a week on this topic in an intensive software design seminar, we’ll aim to be brief.

Lightning components can inherit from parent components, and inheritance is a major style of architecting reusable code. However:

Prefer composition over inheritance.

It’s the “Lightning Component framework” for a reason. Composition is the fundamental model for reusing your code.

Here are two concrete reasons for you to adopt this mantra.

  1. In the framework’s implementation, inheritance has some significant performance drawbacks. The details are subject to change, but you’ll find that using inheritance consumes more memory and processor resources than you might expect.
  2. Inheritance in Lightning components doesn’t exactly work the way it does in Apex, or Java. It’s...quirky.

    For example, a child component can extend from a parent component. And the child component has access, for example, to the parent’s defined action handlers and helper functions. And the child component can replace those handlers and functions with its own. But the child component can’t extend those handlers or functions.

    That is, your child helper method can’t call super.aHelperFunction() from within its implementation of aHelperFunction(), and then add additional logic. You either use the parent function as implemented, or you replace it entirely. That constraint puts a damper on reuse. There’s an alternative approach that involves some contortions using <aura:method>. It’s supported and powerful, but if your use case is simple, it feels a bit overwrought.

Inheritance as implemented in Lightning components is, as we said, quirky. And the implementation is subject to change. Avoid complex inheritance hierarchies, so that you can adapt as the framework changes.

Apex Controllers

Let’s look at a very simple server-side controller, and talk through a couple points.
public with sharing class SimpleServerSideController {

    @AuraEnabled
    public static String serverEcho(String echoString) {
        return ('Hello from the server, ' + echoString);
    }
}

There are a few things to notice here, including a number of specific differences from a Visualforce controller.

  • The most obvious thing that’s new is the @AuraEnabled annotation. If you’ve worked with JavaScript remoting in Visualforce, it’s a lot like the @RemoteAction annotation you use for those methods.
  • The similarity with JavaScript remoting continues with the method signature. Lightning components server-side controller methods must be static, and either public or global.
  • A consequence of the server-side controller methods being static is that you can’t store any component state on the server side. Keep component state in its client-side attributes, as described in a preceding section.
  • Server-side actions return data. They don’t—they can’t—return a PageReference. Implement your navigation logic on the client side, not the server side.
  • Chute! Something that might not be obvious, but which causes you endless headaches if you miss it, is that parameter names used in the Apex method declaration must match the parameter names you use when creating the action on the client side.
  • Chute! One more nonobvious constraint: Don’t give a server-side controller method the same name as a client-side action handler function. You’ll run into...weirdness. You should adopt a naming convention for your server-side and client-side methods that makes the distinction clear, and naming collisions impossible.

Let’s repeat those last two bullets. Parameter names must match when passed between client-side and server-side code. However, the method names must not match.

Apex vs. Lightning Components

With any Lightning component that has server-side Apex, you pass data back and forth between Lightning component JavaScript code and server-side Apex code. For the most part the conversions of parameters and results between the two data formats works automatically. But there are a few limitations that can affect your software design.

Apex Is Not JavaScript

Chute! That might seem obvious, but it’s worth pointing out that when you pass objects back and forth between client-side and server-side code, they undergo transformations. An Apex object, which might include properties with complex logic behind them, when returned in response data becomes a simple JavaScript object with name:value pairs only. Public instance variables are passed through, and public getter instance methods on the Apex object are called and resolved to static values before the object is serialized and returned. And so on.

Ladder! While the process is deterministic, and understandable, it’s often worthwhile to create special, simplified classes in Apex solely for purpose of packaging and returning data to Lightning components. Use those classes, rather than your processing classes, to transfer data.

It sounds like extra work (code), and it is. But it removes some of the complexity from the opaque process of transformation-serialization-transmission-deserialization that happens when data is returned from Apex to JavaScript. Removing that complexity can simplify debugging.

sObjects

You can create new sObjects in JavaScript code, including custom objects. There’s a bit of extra syntax, but it’s straightforward. These sObjects can be sent as parameters in requests to server-side Apex. You can return sObjects in a response from Apex, and the framework handles the transformation.

Custom Classes

Ladder! You can’t usefully pass a custom Apex class as a parameter from client-side JavaScript to server-side Apex. Instead, use a plain JavaScript object to encapsulate the structured data of the parameter. Parse that object as necessary in your Apex code, for example, in the constructor for an Apex class.

You can return a custom Apex class in the response from your server-side controller to client-side JavaScript. However, it’ll be serialized and deserialized as part of that process, and the result might not be exactly what you expect. Ladder! It’s often better to return a map with the data elements you want to include, or JSON you construct yourself.

Inner Classes

Chute! You can’t use Apex inner classes in Lightning components code. Your server-side Apex code can use them in processing a request, but the response returned to the client can’t be an instance of an inner class.

Inheritance

Chute! You can’t use inheritance with custom Apex classes you intend to return in responses to Lightning components.

Handling Server-Side Errors

Ladder! If your Apex code encounters an error, you can create and throw an AuraHandledException. Catching other exceptions, such as a DML exception, and rethrowing them as an AuraHandledException also results in much better experience on the client side.

retargeting