-
Notifications
You must be signed in to change notification settings - Fork 0
Orchestration
Putting everything together.
All components (except for models) are individual independent parts of your application. To finally create a working app you need to connect them all to each other. This is done in app.ts file. Empty application with no components looks like this:
import Application from "../common/application.abstract";
/**
* Application class
*/
export default class App extends Application {
/**
* Application constructor
*/
public constructor() {
super([
///Put your components here
]);
}
/**
* Initializes the app
*/
public async initialize(): Promise<void> {
///Put components' configurations here
/// .initialize([Component, arg1, arg2, arg3...], [Component2, ...])
await super.initialize();
}
}
Let's say we have an Exampler
controller and an Example
view. To put them in you need to import the classes then write the types in super constructor as an array:
import Application from "../common/application.abstract";
import Exampler from "./controllers/exampler.controller"; //Importing controller
import Example from "./views/expample/example.view"; //Importing view
export default class App extends Application {
public constructor() {
super([
Exampler,
Example
]); //Constructing an app with 2 component types
}
...
Imports are usually automatically managed by your IDE, so you shouldn't worry about them in most cases.
Order of components is important! Components of the same type will initialize in the order you put them in. So if you need one to be initialized before another, you should register them appropriately.
Now let's assume we have some arguments in our controller's intialization:
import Controller from "../../common/controller.abstract";
export default class Exampler extends Controller<never>() {
public initialize(api: string = "https://localhost"): void {
///Some logic...
}
}
Now we want to change them with the application's global configuration. To do that, we specify these arguments in the application's initialization function:
public async initialize(): Promise<void> {
await super.initialize([Exampler, "https://example.com"]);
}
super.initialize
takes any number of arguments, each of them is an array where the first element is a component type and the rest are initialization arguments. Initializing multiple components you might end up with something like this:
await super.initialize(
[Exampler, "https://example.com"],
[EpicController, 42, "epic"],
[SomeService, {data: 4}],
...
);
There are two extra options you can provide on application construction:
-
scope
defines the exposing scope -
logging
whether the app should log components' initialization to the console
public constructor() {
super([], {
scope: globalThis, //Defaults to globalThis
logging: true //Defaults to true
});
}
To define how components interact with each other you should use the events system. The most convenient way to register event listeners on each component is to use handlers:
@handle(Exampler)
public onExampler(self: Exampler): void {
self.on("somethingHappened", (how: any) => {
//Do something
});
}
A handler function is called for each instance of a component after its creation but before initialization. To define a handle function you use @handle(<Component>)
decorator on an application's method:
export default class App extends Application {
public constructor() { ... }
public async initialize(): Promise<void> { ... }
@handle(Exampler)
public onExampler(self: Exampler): void { ... }
@handle(SomeService)
public onSomeService(self: SomeService): void { ... }
@handle(EpicController)
public onEpicController(self: EpicController): void { ... }
...
}
Handler receives a single argument of component's instance. It is called as many time as many instances were created.
You might want to use some helper methods to communicate an event from one component to another:
-
this.getComponent
- Returns the first component instance of the given type (useful for singleton (akanull
realtion) components like Views, Services or Non-Relational Controllers). The second optional parameter may also be specified to find a component with a specific relation. -
this.getComponents
- Returns an array of component instances of the given type
@handle(Exampler)
public onExampler(self: Exampler): void {
self.on("clicked", () => {
//Close the first `Example` view instance
this.getComponent(Example).close();
//Get the exampler controller which has 'specific_element' as its relation
const realtion = document.getElementById("specific_element");
const specificExampler = this.getComponent(Exampler, realtion);
//Call show method on every instance of `Shower` controller
this.getComponents(Shower).forEach(shower => {
shower.show();
})
})
}
Finally, you do not have to use handlers only for registering event listeners. You can use them however you need as long as you understand how they work.