
Nikita Poltoratsky
Software Engineer at Akveo
@nikpoltoratsky

@nikpoltoratsky

Platforms in Depth. Rendering Angular Applications in Terminal

@nikpoltoratsky
// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule);
@nikpoltoratsky

// angular.json
{
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
// Application entry point
"main": "src/main.ts",
}
// ...
}
}
}
@nikpoltoratsky

// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule);
@nikpoltoratsky

Agenda
- Angular is a cross-platform framework
- Learn what are platforms
- How do they enable the cross-platform ability
- Bootstrap process
- Implementing custom platform
@nikpoltoratsky

Terminal Platform

@nikpoltoratsky

Angular is a cross-platform framework

@nikpoltoratsky

Browser



@nikpoltoratsky

Server

@nikpoltoratsky

Web Worker

@nikpoltoratsky

Native Platforms

@nikpoltoratsky

Platforms
@nikpoltoratsky

- PlatformRef
- Services
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { PlatformRef } from '@angular/core';
// Create Browser Platform
const platformRef: PlatformRef = platformBrowserDynamic();
// Bootstrap Application
platformRef.bootstrapModule(AppModule);
// main.ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
platformBrowserDynamic().bootstrapModule(AppModule);
@nikpoltoratsky

Platform factory
@nikpoltoratsky


@nikpoltoratsky

Platforms Inheritance

@nikpoltoratsky


@nikpoltoratsky


@nikpoltoratsky

How is it even possible?
@nikpoltoratsky

Abstraction
-
Renderer2
-
Compiler
-
ElementSchemaRegistry
-
Sanitizer
-
etc.
@nikpoltoratsky

@nikpoltoratsky


Abstract Services
@nikpoltoratsky


@nikpoltoratsky



@nikpoltoratsky

Bootstrap Process
@nikpoltoratsky

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { PlatformRef } from '@angular/core';
// Create Browser Platform
const platformRef: PlatformRef = platformBrowserDynamic();
// Bootstrap Application
platformRef.bootstrapModule(AppModule);
@nikpoltoratsky

Bootstrap

@nikpoltoratsky


Compile Module
@nikpoltoratsky

Compile Module
@nikpoltoratsky

- Create compiler
- Load resources
- Compile component's templates
- Compile module
- Return Module Factory
Create Root NgZone

@nikpoltoratsky

Create Root NgZone
@nikpoltoratsky

const zone = new NgZone();
zone.run(() => {
// Create Module
const moduleRef = moduleFactory.create();
// All the rest logic executed here
...
});
Setup ErrorHandler

@nikpoltoratsky

Setup ErrorHandler
@nikpoltoratsky

// Get error handler from injector
const exceptionHandler: ErrorHandler = injector.get(ErrorHandler);
// Setup error handling outside Angular
// To make sure change-detection will not be triggered
zone.runOutsideAngular(
// Subscribe on zone errors
() => zone.onError.subscribe({
next: (error: any) => {
// Call error handler
exceptionHandler.handleError(error);
}
})
);
Run Initializers

@nikpoltoratsky

Run Initializers
{provide: APP_INITIALIZER, useValue: setupWebWorker, multi: true},
@nikpoltoratsky

Get ApplicationRef

@nikpoltoratsky

Get ApplicationRef
@nikpoltoratsky

const appRef = injector.get(ApplicationRef);
Bootstrap Component

@nikpoltoratsky

Bootstrap Component
@nikpoltoratsky

// app.module.ts
@NgModule({ bootstrap: [AppComponent] })
export class AppModule {}
// Iterate all declared root components
AppModule.bootstrap.forEach((component) => {
// Create factory for components
const componentFactory =
this._componentFactoryResolver.resolveComponentFactory(component);
// Actually create component
const compRef = componentFactory.create(...);
});
Bootstrap

@nikpoltoratsky

Terminal Platform

@nikpoltoratsky

Renderer

@nikpoltoratsky

export abstract class Renderer2 {
abstract createElement(name: string, namespace?: string|null): any;
abstract createText(value: string): any;
abstract appendChild(parent: any, newChild: any): void;
abstract addClass(el: any, name: string): void;
abstract removeClass(el: any, name: string): void;
// ...
}
@nikpoltoratsky

Renderer
import { createElement, Screen } from 'ascii-renderer';
const screen = new Screen();
const button = createElement('button');
// Append one element to another
screen.append(button);
// List for events on component
button.on('event name', listener);
@nikpoltoratsky

Renderer
import { createElement, Screen } from 'ascii-renderer';
export class TerminalRenderer implements Renderer2 {
createElement(name: string, namespace?: string | null): any {
return createElement(name);
}
selectRootElement(): Screen {
return new Screen();
}
appendChild(parent: Element, newChild: Element): void {
parent.append(newChild);
}
...
}
@nikpoltoratsky

Renderer
@nikpoltoratsky

Never touch DOM
Wrap it up
@nikpoltoratsky

Demo Time 🤘

@nikpoltoratsky

Why?
@nikpoltoratsky

- Change Detection
- HttpClient
- Component-based approach
- RxJS
- etc.

Nikita Poltoratsky
Software Engineer at Akveo
@nikpoltoratsky

@nikpoltoratsky


tibing/platform-terminal
The End!
Thank you for your time!
@nikpoltoratsky

Rendering Angular applications in Terminal
By Nikita Poltoratsky
Rendering Angular applications in Terminal
- 1,162