SPAC: Controller Self-Initialization & Object API

SPAC is a custom JavaScript framework for client-side, single-page web applications. It stands for “Stateful Pages, Actions and Components”. Its design goal is to provide robust and simple entities that help you to structure apps. Pages and components provide the HTML, JavaScript functions and UI interactions. Actions govern external API calls. You define these entities in plain JavaScript, load up the central controller, and your app is ready to be served. Read the development journey of SPAC in my series:

The core entity of SPAC is the controller: A self-initializing object that assembles your web application from its pages, actions and components. This article details the self-initialization phase, how it works, how it creates an internal objects API and its bindings to the browser.

This article originally appeared at my blog.

Ongoing Example: ApiBlaze Index Page

To explain the concepts in detail, we will use the example of ApiBlaze, an ongoing development project that enables blazing-fast searches in API descriptions. You can read more about ApiBlaze in the project kickoff article.

ApiBlaze first screen consists of a search bar and a search result popup. When you execute a search, the appropriate action will be triggered. The directory layout for this screen is as follows:

Before starting the app, you need to provide an inventory.json file, which contains file links to all pages, actions and components. This file is generated by npm run bootstrap. For this example, it looks as follows:

Self-Initialization Process

The file index.js contains code for importing the controller and starting the self-initialization. Typically, it looks like this:

As you see, the controller is initialized by receiving the inventory, and then calling the async function init().
During the initialization, the controller makes the following steps:

For each file listed in the inventory, check…

  • That the file name conforms with the naming pattern (/.*Page.js/, /.*Action.js/ or *Component.js/)
  • That the file exports a class of the appropriate type

Each of these classes is added to an internal Map object:

  • pagesMap: Define entries with route and obj properties
  • actionsMap: Define entries with obj properties
  • componentsMap: Define entries with obj properties

Files that do not conform to the naming patterns, or files for which the type check fails, are ignored.

Let’s see the details by following an example. The following excerpt shows init method and how the /pages directory will be traversed.

In this method:

  • Line 2: The init function calls an internal helper _initMap()
  • Line 6: For each file inside the inventory…
  • Line 8: … check that it matches the given pattern
  • Line 11: … attempt a dynamic import of the file
  • Line 13: … check the file exports a class of the given parentClass
  • Line 16/18: Store the name and an object containing the export in the given mapType

Internal Objects API

When the initialization phase for the above mentioned example is completed, we obtain map objects that can be accessed and used in the application directly from the controller.


The page map object:

Pages can be accessed with'PageName'), and the method controller.display('PageName') renders the page.


The components map object:

Components can be accessed with controller.component('componentName). This method is used by page objects to fetch their components.


The actions map object:

Actions are accessed controller.action('ActionName').

Assembling Pages

When designing pages objects, you can choose to either manually import your components, or access the component via the controller.

The manual import looks like this:

Alternatively, the objects API can be used to import components (and actions). For this, you need to add the special method _preloadComponents() and pass an object with component names and their arguments, e.g. the querySelector.

During initialization, the Page class will check if this special method is defined, and if yes, use the Controllers component method, to retrieve the class definition and create an instance of the particular component.


This article explained how the controller’s self-initialization phase works. First, we create an inventory of all pages, components and actions using the command npm run bootstrap. Then, when the controllers’ instance is created, it will use the inventory to define internal map objects that point to all defined classes. The controller rigorously checks that each file is named correctly and that it exports a class which is of type Page, Component or Action. Then, these map objects can be used to dynamically load the entities for assembling the pages.

IT Project Manager & Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store