Angular Universal: Server-side Rendering in Angular

Angular Universal: Server-side Rendering in Angular

Angular is an open-source client-side web application framework that is used to build web applications, but it runs, by default, only on the client-side. To overcome this, Angular provide us with Angular Universal.

Introduction

Angular Universal allows us to do server-side rendering of Angular applications. With Angular Universal, the application generally renders more quickly, giving us a chance to view the application layout before it becomes fully interactive. Running Angular Universal on the server enables you to serve static HTML to the client. The server will pre-render pages and show us content while the client-side app loads in the background. Once everything is ready client-side, it will seamlessly switch from showing the server-rendered pages to the client-side app.

Basically, during the loading of the JavaScript, you will render a landing page that look like the complete application. It is just a pure HTML page the links between pages will work. It can even be displayed with the JavaScript disabled. Once the JavaScript loads, the page will be fully interactive.

For more information, visit the offical page here

Get started with Angular Universal and Smart UI

Our first task is to create new Angular project with Angular CLI. To do this, we should run the following command:

ng new smart-ssr-app

You will be asked, whether to use routing or not, so create a project without routing and with CSS only.

Next navigate via terminal inside the project:

cd smart-ssr-app

Once you have created the project, clear the app.component.html and delete app.component.spec.ts

The next step is to add the service-side application module. The command for this is:

ng add @nguniversal/express-engine

After running the command, you will see some new files: app.server.module.ts, main.server.ts, server.ts

The explanation is the following: A Node.js Express web server compiles the HTML pages with Universal based on requests. As you know, the default Angular application has a main.ts file which bootstraps the main application module. Now the main module is the app.server.module.ts. This module exports app.module.ts and is exported form the main.server.ts, it is not bootstrapped. It is exported and used in the server.ts where Universal template engine (ngExpressEngine) uses it.

More information about the Universal template engine: here

Use the following command to run the application:

npm run dev:ssr

Using Smart UI with Angular Universal

Now our application is empty, let's add Smart UI. Before that, we should deal with one problem: the window object. It will be undefined when we try to render our components server-side. That leads to an error because our components rely on window

To fix this problem, open server.ts and create a window object.

See the exported function app(). Above it, create a win object and then in the beginning of the app() function assign it to global['window']

const domino = require('domino');
const win = domino.createWindow();

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
    global['window'] = win;
    
    const server = express();
    ...

Now, you are ready to install any of the Smart UI components. For this tutorial, we will install GanttChart

Run the following command to do this:

npm i @smart-webcomponents-angular/ganttchart

After installing the component, you should import the styles for it. Open angular.json and in the styles array add the base, common CSS along with the Gantt chart's CSS. The array should look like this:

"styles": [
    "src/styles.css",
    "./node_modules/@smart-webcomponents-angular/ganttchart/styles/smart.base.css",
    "./node_modules/@smart-webcomponents-angular/ganttchart/styles/smart.common.css",
    "./node_modules/@smart-webcomponents-angular/ganttchart/styles/smart.ganttchart.css"
]

If you are adding more than one component, you should only add the CSS file for the component, not the three files from the package. smart.base.css and smart.common.css are same in all packages.

For example:

"styles": [
    "src/styles.css",
    "./node_modules/@smart-webcomponents-angular/ganttchart/styles/smart.base.css",
    "./node_modules/@smart-webcomponents-angular/ganttchart/styles/smart.common.css",
    "./node_modules/@smart-webcomponents-angular/ganttchart/styles/smart.ganttchart.css",
    "./node_modules/@smart-webcomponents-angular/button/styles/smart.button.css",
]

We have successfully added the styles for the Gantt chart. To use it in app.component.html, the GanttChartModule must be added in the imports array of the app.module.ts.

To do that, open app.module.ts and import the module into the array

app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

import { GanttChartModule } from '@smart-webcomponents-angular/ganttchart';

@NgModule({
    declarations: [
        AppComponent
    ],
    imports: [
        BrowserModule.withServerTransition({ appId: 'serverApp' }),
        GanttChartModule
    ],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

We are ready to use smart-gantt-chart in the app.component.html.

Use the following code in the files:

app.component.ts
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
import { GanttChartComponent } from '@smart-webcomponents-angular/ganttchart';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    encapsulation: ViewEncapsulation.None
})

export class AppComponent{
    @ViewChild('ganttchart', { read: GanttChartComponent, static: false }) ganttchart!: GanttChartComponent;

    dataSource: Array<object> = [
    {
        id: 'Betty',
        label: 'Morning Shift',
        dateStart: '2021-01-15T04:00:00',
        dateEnd: '2021-01-15T012:30:00',
        class: 'morning-shift',
        type: 'task'
    },
    {
        id: 'William',
        label: 'Afternoon-shift',
        dateStart: '2021-01-15T12:30:00',
        dateEnd: '2021-01-15T20:00:00',
        class: 'afternoon-shift',
        type: 'task'
    },
    {
        id: 'Emma',
        label: 'Half-day',
        dateStart: '2021-01-15T12:30:00',
        dateEnd: '2021-01-15T16:30:00',
        class: 'half-day',
        type: 'task'
    },
    {
        id: 'Oliver',
        label: 'Morning-shift',
        dateStart: '2021-01-15T04:00:00',
        dateEnd: '2021-01-15T12:30:00',
        class: 'morning-shift',
        type: 'task'
    },
    {
        id: 'Jason',
        label: 'Afternoon-shift',
        dateStart: '2021-01-15T12:30:00',
        dateEnd: '2021-01-15T20:00:00',
        class: 'afternoon-shift',
        type: 'task'
    },
    {
        id: 'Alex',
        label: 'Early-morning-support',
        dateStart: '2021-01-15T00:00:00',
        dateEnd: '2021-01-15T08:30:00',
        class: 'early-morning-support',
        type: 'task'
    },
    {
        id: 'Lucas',
        label: 'Half-day',
        dateStart: '2021-01-15T04:00:00',
        dateEnd: '2021-01-15T08:00:00',
        class: 'half-day',
        type: 'task'
    },
    {
        id: 'Michael',
        label: 'Early-morning-support',
        dateStart: '2021-01-15T00:00:00',
        dateEnd: '2021-01-15T08:30:00',
        class: 'early-morning-support',
        type: 'task'
    }
    ];

    durationUnit: string = 'hour';

    taskColumns: Array<object> = [
    {
        label: 'Employee',
        value: 'id'
    }
    ];
}
app.component.html
<smart-gantt-chart #ganttchart 
    [view]="'day'" 
    [treeSize]="'125'" 
    [dataSource]="dataSource" 
    [durationUnit]="durationUnit"
    [taskColumns]="taskColumns"
></smart-gantt-chart>