# Angular 18 SSR Starter

> Source: https://agilitycms.com/docs/angular/angular-18-ssr-starter

Introducing the first dynamic page server side rendering starter for Angular featuring Agility CMS. This utilizes SSR capabilities of Angular 18 to pre-render a users sitemap into pages served statically for fast page delivery and SEO optimization. 

This build features a streamlined architecture and easy to understand development patterns common to Angular developers that will naturally fit within the modern language of the Agility ecosystem. 

The repository can be found at [https://github.com/agility/agilitycms-angular-starter](https://github.com/agility/agilitycms-angular-starter) Currently this version is located on the `angular-ssr` branch

### Pre-requesites

- Node v20+ 

### Installation

- Close this Repository
- `npm install` to install the Node Modules

### Environmental Variables

You will need to get the following from the Agility Manager;

`GUID` `API_PREVIEW_KEY` `API_FETCH_KEY` `LOCALE` `SITEMAP`

[https://manager.agilitycms.com/settings/apikeys](https://manager.agilitycms.com/settings/apikeys)

You will need to populate an **.env** file in the root of your project as follows.

This will use **dotenv.config.json** to write your **environment.ts** and **environment.prod.ts **files required by Angular. This approach avoids exposing your API keys to your Git repository.

```
AGILITY_GUID=e13c7b01-u
AGILITY_PREVIEW=true
AGILITY_API_PREVIEW_KEY=AngularPreview.6617c54b87588d941d32416a9dfb1e8fd9e556439984e8236ac75896e47ae02a
AGILITY_API_FETCH_KEY=AngularFetch.a20b40fd8cd25e02ba62ca5c3acbaae5512c1d633b51ea104ac28f9bc3b9d44d
AGILITY_LOCALE=en-us
AGILITY_SITEMAP=website
```

### Build Commands

- `npm run dev` start the development server
- `npm run build` pre-renders the routes via agility-routes.txt then builds the production server
- `npm run start` starts the production server
- `npm run prerender:routes` pre-renders only the agility-routes.txt

### Why not 'ng run commands?

Although Angular's `ng` commands are still available, we’ve introduced a number of steps in the building and serving of the Angular application. As such its advisable to use the `npm run` commands above. However, if you still desire to use the `ng` commands please be sure to run these prior to trying to run your application to generate the necessary environment and routes files.

- `npm run envinit`
- `npm run prerender:routes`

### Key Features

- **Dynamic SSR -** Supports server-side rendering for dynamic pages generated from an Agility CMS.
- **Pre-rendering and TransferState -** Improves initial page load performance and reduces redundant requests.
- **Hybrid SSR -** Uses a blend of SSR and client-side routing for optimal load times and user experience.
- **Seamless Agility CMS Integration -** Designed to fit naturally with Agility’s headless CMS ecosystem, making it easier to manage content dynamically.
- **Seamless Agility CMS Integration -** Designed to fit naturally with Agility’s headless CMS ecosystem, making it easier to manage content dynamically.

### Preview Mode

This feature works as a client side only feature to ensure changes are loaded directly from the API instead of using the pre-rendered page and content data. 

### Limitations of Angular 18 SSR when handling Dynamic Pages

Angular is built primarily as a client-side framework, designed to load an initial shell of the application in the browser and handle navigation on the client side. With this architecture, Angular’s built in router dynamically updates the content without reloading the page.  


Angular can handle SSR for the initial page load, however, once the app is hydrated, navigation between routes is managed by the client-side router, which means http requests are visible and no longer Server Side. The Angular router doesn’t natively support dynamic changes to the base route or URL content without a full page reload.

Angular’s SSR can be thought of as a “Hybrid” SSR feature as it does not function the same as a more fully developed client router like in React or Vue.

This starter implements a TransferState functionality to reduce subsequent page requests from the client however if you want to completely eliminate client side requests you will need to serve the production build. 

The Angular TransferState API is designed to persist data between the server-rendered page and the client, but it only applies to the initial load, not for subsequent navigation events. After the first load, if you navigate to another route, TransferState will not automatically fetch fresh data from the server. 

Angular 18’s SSR functionality is definitely a step in the right direction for Angular however it’s clear there’s still room for improvement on connecting the the hydration flow from server to client when navigating via the Angular router. 

**Other Workarounds - **Pre-render all page data and content lists


### Solution Architecture

This does not include the boilerplate created by Angular however this is the basic overview of the starter architecture of what we’ve added to power Agility CMS.

![](https://cdn.aglty.io/agility-cms-docs/images/block-editor/Screenshot 2024-11-07 at 11.34.18 AM-11072024153440.png)

### Angular SSR Data Pre-loading Branch

This is a solution to using the Angular Router to avoid client side requests.

The way it achieves this is to pre-load the website data by running a pre-fetch process in Node when building the site.

This solution is comprised of 3 main files, one of which is already present in the base branch `angular-ssr`

- `prerender-routes.ts` - generates an agility-routes.txt file for Angular to prerender the page content
- `prerender-pages.ts` - generates a JSON file of all your Agility Pages, effectively a Sync mechanism
- `prerender-content.ts` - generates a JSON file of all the content lists you require

### Added Build Commands

Two new build commands are introduced with this branch

- `npm run prerender:pages`
- `npm run prerender:content`

### Component Retrofitting

In order to pull data client side components have to be updated to first check the local JSON files and then set that as the initial TransferState.


```
import agilityPagesData from '../data/pages.json';

 async preloadTransferStateData() {
    let pagePath = this.location.path().split('?')[0] || '/home';
    if (pagePath === '') pagePath = '/home';
    for (const key in agilityPagesData) {
      const pageKey = makeStateKey<any>(key);
      this.transferState.set(pageKey, (agilityPagesData as any)[key]);
    } 
  }
```

> **Development Caution** — This has not been tested on websites with a large volume of pages or content lists. Anything pre-rendered will in-fact add to the build times of the application. As well, since the Angular Router is held within memory, all pre-loaded data is also held within memory adding to the application overhead.
