43
loading...
This website collects cookies to deliver better user experience
Thanks to @Caryntjen
from Discord for bringing this problem up
angular-zen
. This library will create a zone.js task under the hood, and that helps Angular Universal understand your async code. To learn more about this, you can visit their docs: Angular zen docsIn SSR, the server doesn't wait for the async code to complete. The result is scrapers and search engines receiving a page without resolved data, which is bad in case you need them to read some resolved metadata tags for example. Use resolveInMacroTask() to have your server block and wait for resolves before rendering.
ng new appwrite-ssr
cd appwrite-ssr
? Do you want to enforce stricter type checking and stricter bundle budgets in the workspace?
This setting helps improve maintainability and catch bugs ahead of time.
For more information, see https://angular.io/strict Yes
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? CSS
Make sure to pick the same options to have the same results
npm i appwrite
app.component
. Still, it is strongly recommended to put all Appwrite logic into a separate appwrite.service
to share data across multiple components in an actual project easily.app.component.ts
should look like this:import { Component, OnInit } from '@angular/core';
import { Appwrite } from 'appwrite';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
title = 'appwrite-ssr';
currencies: any; // Do not use any in real project
async ngOnInit() {
let sdk = new Appwrite();
sdk
.setEndpoint('https://server.matejbaco.eu/v1') // Your API Endpoint
.setProject('60f2fb6e92712'); // Your project ID
// Load currencies from appwrite
const appwriteCurrencies = await sdk.locale.getCurrencies();
// Store the result into component variable for use in HTML code
this.currencies = appwriteCurrencies;
}
}
import { Appwrite } from 'appwrite';
. Then, inside ngOnInit
we initialized a new instance of the SDK that is connected to our Appwrite server. Finally, we load a list of currencies from Appwrite and store it into a variable to use in HTML code.app.component.html
. This is our code:<h1>Total currencies:</h1>
<!-- We don't have data yet, loading... -->
<p *ngIf="!currencies">...</p>
<!-- Data loaded, let's count them -->
<p *ngIf="currencies">Total: {{ currencies.sum }}</p>
ng serve
and visit http://localhost:4200/
, we can see the currencies being loaded successfully:ng add @nguniversal/express-engine
. Then, we can run npm run dev:ssr
to have the same development server running, but this time with server-side rendering. Let's see what our website looks like to bots now:Total currencies:
. We are not done yet, because this pre-rendered HTML does not include our Appwrite data. Instead, we can see ...
. npm i @bespunky/angular-zen
. Once the library is installed, let's start the development server with npm run dev:ssr
.imports
of our module for it to work properly. To do this, we go into app.module.ts
and add add RouterXModule
as an import. The module should look like this:import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouterXModule } from '@bespunky/angular-zen/router-x';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
AppRoutingModule,
RouterXModule.forRoot(),
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
My IDE noticed the error Appears in the NgModule.imports of AppServerModule, but itself has errors
, but I ignored it, and it got resolved by Angular re-building the app. If you can see this error, simply restart the development server, and you should be good to go.
RouteAware
class in our app.component.ts
because we need to access its resolveInMacroTask()
method. To do that, we can make our component extend RouteAware
. Then we wrap our async code in ngOnInit
into resolveInMacroTask
and await its result as a promise. Our code will look like this:import { Component, OnInit } from '@angular/core';
import { RouteAware } from '@bespunky/angular-zen/router-x';
import { Appwrite } from 'appwrite';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
})
export class AppComponent extends RouteAware implements OnInit {
title = 'appwrite-ssr';
currencies: any; // Do not use any in real project
async ngOnInit() {
let sdk = new Appwrite();
sdk
.setEndpoint('https://server.matejbaco.eu/v1') // Your API Endpoint
.setProject('60f2fb6e92712'); // Your project ID
await this.resolveInMacroTask(async () => {
// Load currencies from appwrite
const appwriteCurrencies = await sdk.locale.getCurrencies();
// Store the result into component variable for use in HTML code
this.currencies = appwriteCurrencies;
}).toPromise();
}
}