Angular 17 SSR html does not show the contents of the application, after updating the OLD project

469 Views Asked by At

I have updated the Angular from version 14 to version 17 in order to take advantage of the boxed SSR solution. The update was successful, but when ng serve starts, the application content does not load, as if SSR does not work. There is practically no information on the Internet...

The application is loaded on the client. Empty app-root when viewing the page code:


    <body>
      <app-root></app-root>
      
      <script src="runtime.js" defer></script>
      <script src="polyfills.js" type="module"></script>
      <script src="styles.js" defer></script>
      <script src="vendor.js" type="module"></script>
      <script src="main.js" type="module"></script>
    </body>

Here are mine...

angular.json:

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "client": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss"
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets",
              "src/robots.txt",
              "src/sitemap.xml",
              "src/yandex_71526e85ef9f15bd.html"
            ],
            "styles": [
              "node_modules/ngx-owl-carousel-o/lib/styles/prebuilt-themes/owl.carousel.min.css",
              "node_modules/ngx-owl-carousel-o/lib/styles/prebuilt-themes/owl.theme.default.min.css",
              "./node_modules/ngx-lightbox/lightbox.css",
              "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
              "src/styles.scss"
            ],
            "scripts": [],
            "vendorChunk": true,
            "extractLicenses": false,
            "buildOptimizer": false,
            "sourceMap": true,
            "optimization": false,
            "namedChunks": true
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            },
            "preprod": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.preprod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            },
            "local": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.local.ts"
                }
              ],
              "optimization": false,
              "sourceMap": true,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": false,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "buildTarget": "client:build"
          },
          "configurations": {
            "production": {
              "buildTarget": "client:build:production"
            },
            "preprod": {
              "buildTarget": "client:build:preprod"
            },
            "local": {
              "buildTarget": "client:build:local"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "buildTarget": "client:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "assets": [
              "src/favicon.ico",
              "src/assets",
              "src/robots.txt",
              "src/sitemap.xml",
              "src/yandex_71526e85ef9f15bd.html"
            ],
            "styles": [
              "node_modules/ngx-owl-carousel-o/lib/styles/prebuilt-themes/owl.carousel.min.css",
              "node_modules/ngx-owl-carousel-o/lib/styles/prebuilt-themes/owl.theme.default.min.css",
              "./node_modules/@angular/material/prebuilt-themes/deeppurple-amber.css",
              "src/styles.scss"
            ],
            "scripts": []
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "tsconfig.app.json",
              "tsconfig.spec.json",
              "e2e/tsconfig.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "client:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "client:serve:production"
            }
          }
        },
        "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/client/server",
            "main": "server.ts",
            "tsConfig": "tsconfig.server.json",
            "buildOptimizer": false,
            "optimization": false,
            "sourceMap": true,
            "extractLicenses": false,
            "vendorChunk": true
          },
          "configurations": {
            "production": {
              "buildOptimizer": true,
              "outputHashing": "media",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "sourceMap": false,
              "extractLicenses": true,
              "vendorChunk": false
            },
            "preprod": {
              "buildOptimizer": true,
              "outputHashing": "media",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.preprod.ts"
                }
              ],
              "optimization": true,
              "sourceMap": false,
              "extractLicenses": true,
              "vendorChunk": false
            },
            "local": {
              "buildOptimizer": false,
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.local.ts"
                }
              ],
              "optimization": false,
              "sourceMap": true,
              "extractLicenses": true,
              "vendorChunk": false
            }
          },
          "defaultConfiguration": "production"
        },
        "serve-ssr": {
          "builder": "@angular-devkit/build-angular:ssr-dev-server",
          "configurations": {
            "development": {
              "browserTarget": "client:build",
              "serverTarget": "client:server"
            },
            "production": {
              "browserTarget": "client:build:production",
              "serverTarget": "client:server:production"
            }
          },
          "defaultConfiguration": "development"
        },
        "prerender": {
          "builder": "@angular-devkit/build-angular:prerender",
          "options": {
            "routes": [
              "/"
            ]
          },
          "configurations": {
            "production": {
              "browserTarget": "client:build:production",
              "serverTarget": "client:server:production"
            },
            "development": {
              "browserTarget": "client:build:development",
              "serverTarget": "client:server:development"
            }
          },
          "defaultConfiguration": "production"
        }
      }
    }
  },
  "cli": {
    "analytics": false,
    "cache": {
      "enabled": false
    }
  }
}

package.json:

{
  "name": "client",
  "version": "1.1.0",
  "scripts": {
    "ng": "ng",
    "clear": "RMDIR /Q/S node_modules && npm install",
    "start": "ng serve --hmr",
    "start:local": "ng serve --configuration local --hmr",
    "start:preprod": "ng serve --configuration preprod --hmr",
    "start:ssl": "ng serve --ssl=true",
    "start-portal": "ng serve --base-href=\"/portal/\"",
    "build": "ng build",
    "build:preprod": "ng build --configuration preprod",
    "build:prod": "ng build --configuration production --optimization=true",
    "build:prod-portal": "ng build --base-href=\"/portal/\" --configuration production",
    "buildToTrends": "ng build --base-href=\"/portal/\" --configuration=trends",
    "test": "ng test",
    "eslint": "eslint --cache --ext .js,.jsx,.ts,.tsx src",
    "eslint:fix": "eslint --cache --ext .js,.jsx,.ts,.tsx src --fix",
    "eslint:dump": "eslint --print-config ./.eslintrc.json",
    "e2e": "ng e2e",
    "dev:ssr": "ng run client:serve-ssr",
    "serve:ssr": "node dist/client/server/main.js",
    "build:ssr": "ng build && ng run client:server",
    "prerender": "ng run client:prerender"
  },
  "private": true,
  "dependencies": {
    "@angular-material-components/datetime-picker": "^16.0.1",
    "@angular/animations": "^17.1.0",
    "@angular/cdk": "^16.0.1",
    "@angular/cli": "^17.1.0",
    "@angular/common": "^17.1.0",
    "@angular/compiler": "^17.1.0",
    "@angular/compiler-cli": "^17.1.0",
    "@angular/core": "^17.1.0",
    "@angular/forms": "^17.1.0",
    "@angular/localize": "^17.1.0",
    "@angular/material": "^16.0.2",
    "@angular/material-moment-adapter": "^16.0.1",
    "@angular/platform-browser": "^17.1.0",
    "@angular/platform-browser-dynamic": "^17.1.0",
    "@angular/platform-server": "^17.1.0",
    "@angular/router": "^17.1.0",
    "@angular/ssr": "^17.1.0",
    "@auth0/angular-jwt": "^5.0.2",
    "@ng-bootstrap/ng-bootstrap": "^15.1.1",
    "@ngrx/store": "^16.0.1",
    "@popperjs/core": "^2.11.8",
    "angular8-yandex-maps": "^16.0.2",
    "bootstrap": "^5.3.1",
    "browser-sync": "^3.0.2",
    "express": "^4.18.2",
    "jszip": "^3.10.1",
    "material-design": "0.0.1",
    "moment": "^2.29.4",
    "ng-recaptcha": "^12.0.2",
    "ng2-pdf-viewer": "^10.0.0",
    "ngx-cookie": "^6.0.1",
    "ngx-cookie-service": "^16.0.0",
    "ngx-image-cropper": "^6.3.1",
    "ngx-lightbox": "^3.0.0",
    "ngx-mask": "^16.2.8",
    "ngx-owl-carousel-o": "^16.0.0",
    "ngx-pagination": "^6.0.3",
    "ngx-quill": "^16.2.1",
    "ngx-toastr": "^16.2.0",
    "ngx-ui-loader": "^13.0.0",
    "oidc-client": "^1.11.5",
    "quill": "^1.3.7",
    "remove": "^0.1.5",
    "rxjs": "^7.8.1",
    "swiper": "8.3.2",
    "tslib": "2.4.0",
    "zone.js": "~0.14.3"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^17.1.0",
    "@types/express": "^4.17.17",
    "@types/jasmine": "4.0.3",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "^18.18.0",
    "@typescript-eslint/eslint-plugin": "5.32.0",
    "@typescript-eslint/parser": "5.32.0",
    "browser-sync": "^3.0.0",
    "codelyzer": "^6.0.0",
    "eslint": "8.21.0",
    "eslint-config-google": "^0.14.0",
    "jasmine-core": "4.3.0",
    "jasmine-spec-reporter": "7.0.0",
    "jest-preset-angular": "^7.0.1",
    "protractor": "~7.0.0",
    "ts-node": "10.9.1",
    "typescript": "5.3.3"
  }
}

main.ts:

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';

const platform = platformBrowserDynamic();

platform.bootstrapModule(AppModule)
  .catch((err) => console.error(err));

main.server.ts

export { AppServerModule as default } from './app/app.module.server';

app.module.ts

import {DatePipe} from '@angular/common';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {InjectionToken, NgModule} from '@angular/core';
import {MAT_DATE_FORMATS, MAT_DATE_LOCALE} from '@angular/material/core';
import {MatDialogModule} from '@angular/material/dialog';
import {MatIconModule} from '@angular/material/icon';
import {MatPaginatorIntl} from "@angular/material/paginator";
import {MatSlideToggleModule} from '@angular/material/slide-toggle';
import {MatSnackBarModule} from '@angular/material/snack-bar';
import {BrowserModule, provideClientHydration} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {UrlSerializer} from '@angular/router';
import {JwtHelperService} from '@auth0/angular-jwt';
import {NgbModule} from '@ng-bootstrap/ng-bootstrap';
import {StoreModule} from '@ngrx/store';
import {CookieModule, CookieService} from 'ngx-cookie';
import {ToastrModule} from 'ngx-toastr';
import {NgxUiLoaderHttpModule, NgxUiLoaderModule, NgxUiLoaderRouterModule} from 'ngx-ui-loader';
import {getPaginatorIntl} from "src/app/core/configs/paginatorConfig";
import {AdminAuthGuard} from 'src/app/core/guards/admin-auth-guard';
import {UnderConstructionGuard} from 'src/app/core/guards/underConstructionGuard';
import {UnderConstructionComponent} from "src/app/pages/under-construction/under-construction.component";
import {environment} from '../environments/environment';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {ngxUiLoaderConfig} from "./core/configs/ngx-ui-loader-configuration";
import {Interceptor} from './core/services/api.interceptor';
import {AuthService} from './core/services/auth.service';
import {API_BASE_URL, Client} from './core/services/Client';
import {LowerCaseUrlSerializer} from './core/services/lower-case-url-serializer';
import {HeaderComponent} from './layouts/header/header.component';
import {HolidayWindowComponent} from './pages/holiday-window/holiday-window.component';
import {PipesModule} from "./pipes.module";
import {ReactiveFormsModule} from "@angular/forms";
import {MatButtonModule} from "@angular/material/button";
import {UserSubscriptionFilterService} from './core/services/UserSubscriptionFilterService';
import {SeoService} from "./core/services/seo/seo.service";
import {NotfoundComponent} from "./notfound/notfound.component";
import {AngularYandexMapsModule} from 'angular8-yandex-maps';
import {mapConfig} from './core/configs/map-config';
import {TorgiService} from './pages/torgi/torgi.service';
import { appConfig } from './app.config';

export const AUTH_URL = new InjectionToken<string>('AUTH_URL');
export const ORIGIN_URL = new InjectionToken<string>('ORIGIN_URL');

@NgModule({
  declarations: [
    AppComponent,
    HeaderComponent,
    HolidayWindowComponent,
    UnderConstructionComponent,
    NotfoundComponent
  ],
  imports: [
    AppRoutingModule,
    AngularYandexMapsModule.forRoot(mapConfig),
    StoreModule.forRoot({}, {}),
    ToastrModule.forRoot(), // ToastrModule added

    NgxUiLoaderHttpModule.forRoot({
      showForeground: true,
      exclude: [environment.AUTH_URL, `${environment.API_URL}/api/v1-web/UserMessage/GetUserMessageList`]
    }),
    NgxUiLoaderModule.forRoot(ngxUiLoaderConfig),
    NgxUiLoaderRouterModule,

    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    NgbModule,
    MatSlideToggleModule,
    MatIconModule,
    MatSnackBarModule,
    MatDialogModule,

    BrowserAnimationsModule,
    CookieModule.forRoot(),
    PipesModule,
    MatButtonModule,

    BrowserAnimationsModule,
    CookieModule.forRoot(),
    PipesModule,
    ReactiveFormsModule
  ],

  providers: [
    AuthService,
    AdminAuthGuard,
    CookieService,
    JwtHelperService,
    DatePipe,
    UnderConstructionGuard,
    SeoService,
    TorgiService,
    Client,
    UserSubscriptionFilterService,
    {provide: HTTP_INTERCEPTORS, useClass: Interceptor, multi: true},
    {provide: API_BASE_URL, useValue: environment.API_URL},
    {provide: AUTH_URL, useValue: environment.AUTH_URL},
    {provide: ORIGIN_URL, useValue: location.origin},
    {provide: UrlSerializer, useClass: LowerCaseUrlSerializer},
    {provide: MAT_DATE_LOCALE, useValue: 'ru-RU'},
    {provide: MAT_DATE_FORMATS, useValue: {display: {dateInput: 'DD.MM.YYYY', monthYearLabel: 'MMMM YYYY'}}},
    {provide: MatPaginatorIntl, useValue: getPaginatorIntl()},
    provideClientHydration(),
    appConfig.providers,
  ],
  exports: [
    HeaderComponent,
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

app.module.server.ts:

import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { config } from './app.config.server';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
  ],
  bootstrap: [AppComponent],
  providers: [config.providers]
})
export class AppServerModule {}

app.config.server:

import { ApplicationConfig, mergeApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';
import { provideHttpClient, withFetch } from '@angular/common/http';

const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering(),
    provideHttpClient(withFetch())
  ]
};

export const config = mergeApplicationConfig(appConfig, serverConfig);

app.config:

import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideClientHydration } from '@angular/platform-browser';
import { routes } from './app-routing.module';

export const appConfig: ApplicationConfig = {
  providers: [provideRouter(routes), provideClientHydration()]
};

server.ts

import 'zone.js/node';

import { APP_BASE_HREF } from '@angular/common';
import { CommonEngine } from '@angular/ssr';
import * as express from 'express';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import AppServerModule from './src/main.server';

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/client/browser');
  const indexHtml = existsSync(join(distFolder, 'index.original.html'))
    ? join(distFolder, 'index.original.html')
    : join(distFolder, 'index.html');

  const commonEngine = new CommonEngine();

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // All regular routes use the Angular engine
  server.get('*', (req, res, next) => {
    const { protocol, originalUrl, baseUrl, headers } = req;

    commonEngine
      .render({
        bootstrap: AppServerModule,
        documentFilePath: indexHtml,
        url: `${protocol}://${headers.host}${originalUrl}`,
        publicPath: distFolder,
        providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }],
      })
      .then((html) => res.send(html))
      .catch((err) => next(err));
  });

  return server;
}

function run(): void {
  const port = process.env['PORT'] || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export default AppServerModule;

The documentation says exactly ng server angular.dev/guide/ssr. ng dev:ssr I used too, but it doesn't work

To verify that the application is server-side rendered, run it locally with ng serve. The initial HTML request should contain application content.

I can't solve this for the second day((. I will be glad of your help, friends

0

There are 0 best solutions below