> For the complete documentation index, see [llms.txt](https://docs.elentra.org/ejs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.elentra.org/ejs/1.4/guide/modules/controllers.md).

# Controllers

## Introduction

In EJS, a **controller** is a class responsible for providing action methods to execute specific functionality within a module. &#x20;

Acting as an interface to a module, controllers provide predefined functionality which may be called from the application.

## Controller classes

A **controller** is a class that resides in a module's `Controller` namespace and has its name suffixed with **Controller**.  Each controller class may optionally include a constructor which will be passed the active [module](/ejs/1.4/guide/modules.md) instance and the application's [file loader](/ejs/1.4/guide/file-handling/unified-file-handling.md).

In many cases, a module only needs a single controller.  However, for larger modules, it is a good idea to group related functionality into multiple controllers.

{% hint style="info" %}
Enforcing suffixes on class and method names helps to protect against routing to non-controller classes or methods, inadvertently or otherwise.
{% endhint %}

{% code title="MyFirstController.js" %}

```javascript
class MyFirstController 
{
    constructor(module, loader) {}
}
```

{% endcode %}

## Action methods

An **action method** is a method on a controller class with its name suffixed with **Action**.  The application will call these methods to interact with modules from the outside.  This ensures that a module can properly dictate how it should be used while preventing coupling to the application itself.

Action methods may optionally return a value.  If the returned value is a [component](/ejs/1.4/guide/modules/components.md), it will be inserted into the active layout.

### Asynchronous action methods

An **asynchronous action method** is a method that may not return immediately as it needs to wait for its processing to complete (e.g. waiting for a component to load).  Instead of returning a value directly, asynchronous action methods return a Promise that will resolve to the return value.

#### Example: Loading resources just-in-time

{% code title="MyFirstController.js" %}

```javascript
class MyFirstController
{
    constructor(module, loader) {
        this.module = module;
        this.loader = loader;
    }
    
    async indexAction() {
        // Get the path to this module's Component namespace. 
        let pathToComponent = this.module.map().componentPath();
        return this.loader.load(pathToComponents + '/IndexPage.vue');
    }
}
```

{% endcode %}

### Synchronous action methods

A **synchronous action method** is a method that does not contain any asynchronous processing, and optionally returns a value immediately.

Synchronous action methods are most useful for preloading related resources.

{% hint style="info" %}
Always prefer [asynchronous action methods](/ejs/1.4/guide/modules/controllers.md#asynchronous-action-methods).
{% endhint %}

#### Example: Preloading resources

In this example, `MyComponent` and its dependencies will be loaded recursively when `MyFirstController` is loaded.  When `mySyncMethodAction` is called, it can return MyComponent immediately as it is already loaded.

{% hint style="warning" %}
Preloading resources should be used sparingly and primarily for optimization when necessary.  Excessive preloading can cause performance issues resulting from network or memory limits.
{% endhint %}

```javascript
const MyComponent = use('Module/MyModule/Components/MyComponent.vue');

class MyFirstController
{
    mySyncMethodAction() {
        console.log('Running my action method!');
        return MyComponent;
    }
}
```

## Using an abstract controller

When designing your controllers, it's likely you'll want to share some common functionality across many or all of them.

EJS provides a simple abstract controller with some convenience methods:

* `ElentraJS/Controller/ControllerAbstract#respond(componentName)`\
  Accepts the filename of a component, relative to the module's `Component` namespace.  Returns a Promise that will resolve to the component when it is loaded.

{% hint style="info" %}
If you extend the EJS `ControllerAbstract` class instead of using it directly, you can limit coupling to a single point in your module or application.
{% endhint %}

#### Example: Use an abstract controller via composition

{% code title="MyFirstController.js" %}

```javascript
const ControllerAbstract = use('ElentraJS/Controller/ControllerAbstract');

class MyFirstController {
    constructor(module, loader) {
        this.controller = new ControllerAbstract(module, loader);
    }
    
    async indexAction() {
        return this.controller.respond('IndexPage.vue');
    }
}
```

{% endcode %}

#### Example: Use an abstract controller via inheritance

{% hint style="info" %}
Always prefer composition over inheritance as it removes the vertical coupling across classes, enhances flexibility, and simplifies maintenance.
{% endhint %}

{% code title="MyFirstController.js" %}

```javascript
const ControllerAbstract = use('ElentraJS/Controller/ControllerAbstract');

class MyFirstController extends ControllerAbstract {
    // Tip: You may omit this constructor if the super call is its only statement.
    constructor(module, loader) {
        super(module, loader);
    }
    
    async indexAction() {
        return this.respond('IndexPage.vue');
    }
}
```

{% endcode %}
