v1.3
Controller
class Controller extends Model → file: mvc/Controller.js
Controller defines what logic will be applied on the specific route.
Every controller should be injected in the app with the App.routes() or AppOne.controller() method.
The controller class is extended with other MVC classes:
MyController → Controller → Model → View → Dd → DdCloners → DdListeners → Aux
Instance
The controller instance is created in App.routes() or AppOne.controller() method when route is defined.Properties
The controller instance properties (aggregated from other extended classes) are:| Property | Description | Type | Default |
|---|---|---|---|
| $appName | The application name. It defines the window property. For example if $appName='myApp' then window.myApp | string | dodoApp |
| $dd | Related to Dd class: {__initFinished, elems, listeners, noncloner_directives, cloner_directives} | object | {...} |
| $debugOpts | Debugger options. Set specific option to true if you want to see the debugging messages.
See all options.
|
object | {...} |
| $elem | The place where dd-elem element will be saved. | object | {} |
| $fridge | Frozen constants defined by App.fridge() method. | object | {} |
| $model | The container for model variables. When model variable is changed the render() is activated. | object | {} |
| $modeler | Contains the model methods like: setValue, delvalue, getValue, mpush, mpop, munshift, mshift, mrender. See here. | object | {...} |
| __initFinished | Determine if the __init() hook finished. It's used in Model to block rendering if the $model.somevar is defined in the __init(). | boolean | false |
Methods
Lifecycle Hook Methods
There are 5 lifecycle hooks which controller will process synchronously: __loader(), __init(), __rend(), __postrend() and __destroy().Also there's two hooks defined by App.preflight() and App.postflight() which are mutual for all controllers.
PREFLIGHT → LOADER → INIT → REND → POSTREND → DESTROY → POSTFLIGHT
async __loader(trx) :void
A hook to load the HTML (views) and other resources (CSS, JS, ...) and set the web page properties like title, description, etc.Use View methods here (loadView, loadJS, loadCSS, ...).
-
ARGUMENTS:
- trx :object - regoch router transitional variable (carrier) See Router.js
Define meta title, description, keywords, language and load views for specific controller.
import { Controller } from '@mikosoft/dodo';
import navbar from '/views/inc/navbar.html?raw';
import ctrl from '/views/pages/api-reference/controller.html?raw';
import footer from '/views/inc/footer.html?raw';
export default class APIReferenceAppCtrl extends Controller {
constructor(app) {
super();
}
async __loader(trx) {
this.setTitle('DoDo API Reference: Controller');
this.setDescription('Lightweight, easy to learn, Javascript, frontend framework for reactive apps');
this.setKeywords('dodo, framework, javascript, js, single page app, spa, reactive');
this.setLang('en');
this.loadView('#navbar', navbar);
this.loadView('#main', ctrl);
this.loadView('#footer', footer);
}
}
async __init(trx) :void
A hook to initialise the controller properties with the initial values. This is the right place for the API calls.If model variable (for example: this.$model.products) is initialised here the render() will not be executed and view will not be updated. But the render() will be executed in next hook i.e. in the __rend().
-
ARGUMENTS:
- trx :object - regoch router transitional variable (carrier) See Router.js
async __rend(trx) :void
Render all elements with the "dd-..." attribute i.e. Dd, DdCloners and DdListeners.If this method is omitted then default Controller.render() will be executed. Recommended not to use this method until you exactly know what you are doing.
-
ARGUMENTS:
- trx :object - regoch router transitional variable (carrier) See Router.js
async __postrend(trx) :void
It's a controller lifecycle hook which executes the controller code immediately after the __rend() method i.e. when the complete HTML is generated.-
ARGUMENTS:
- trx :object - regoch router transitional variable (carrier) See Router.js
async __destroy() :void
The code in this method is executed when controller is destroyed, i.e. when one route is replaced with another.-
ARGUMENTS:
No arguments.
Unload CSS which was needed only in one route.
async __loader() {
this.loadCSS('temporary.css');
}
async __destroy() {
this.unloadCSS('temporary.css');
}
Render Methods
async render(modelName?, renderDelay = 30) :void
Render the view i.e. all the dd- elements. Called automatically by __rend().-
ARGUMENTS:
- modelName :string (optional) - model name to re-render only directives bound to this model; for example in $model.users the model name is 'users'
- renderDelay :number - delay in milliseconds before attaching event listeners (default: 30)
Reactive render: debounce & serial queue (v1.3)
Every this.$model.x = value assignment triggers render('x') automatically.
When multiple model properties are set in a row (e.g. resetting 5 fields on a network change), this would fire 5 renders at the same time.
Because render() removes all event listeners and re-attaches them after a 30 ms gap, two renders running at the same time can remove each other's listeners, leaving buttons and selects unresponsive.
v1.3 adds two protections that run automatically — no code changes required in your controllers:
- Debounce per model property — if the same property is set several times in a row, only the last value triggers a render. Like an elevator that waits for everyone to step in before moving.
- Serial queue — renders for different properties are lined up and run one after another, never at the same time. This prevents the 30 ms listener re-attachment windows from overlapping.
// All 5 assignments below happen synchronously.
// Without debounce: 5 renders fire at the same time → listeners get clobbered.
// With debounce + serial queue: each property renders once, in order, safely.
async onNetworkChange() {
this.$model.wallets = [];
this.$model.dexpools = [];
this.$model.allowances = [];
this.$model.walletAddr = '';
this.$model.routerName = '';
await this.loadWallets(); // sets this.$model.wallets = apiResp → one render
}
prerender() :void
Render dd-setinitial and dd-href directives before __init() lifecycle hook.Called internally between __loader() and __init().
-
ARGUMENTS:
No arguments.
Stackblitz Examples
- Controller Lifecycle Hooks - shows in which order will be executed constructor(), __loader(), __init(), __postrend(), __destroy()