Zoeken op website

typescript - Probleem met server-side rendering in Angular: kan main.bundle.js niet genereren


Ik begon mijn applicatie met client-side rendering en alles was in orde. Maar nu, wanneer ik probeer mijn applicatie te compileren en te serveren via server-side rendering, ben ik enkele problemen tegengekomen.

Eerst iets: Wanneer ik het commando npm run build:ssr && npm run serve:ssr uitvoer, kan de hele applicatie compileren en draaien, en ik zie in de terminal berichten over het serveren van deze app op localhost:3000. Ik ga naar localhost:3000 en zie dit:

    Error: You must pass in a NgModule or NgModuleFactory to be 
   bootstrapped
    at View.engine (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:196864:23)
    at View.render (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:46196:8)
    at tryRender (/home/ewelina/rohub-portal/portal - 
   angular/dist/server.js:43892:10)
    at Function.render (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:43844:3)
    at ServerResponse.render (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:52737:7)
    at /home/ewelina/rohub-portal/portal-angular/dist/server.js:149:9
    at Layer.handle [as handle_request] (/home/ewelina/rohub- 
   portal/portal-angular/dist/server.js:45618:5)
    at next (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:45366:13)
    at Route.dispatch (/home/ewelina/rohub-portal/portal- 
   angular/dist/server.js:45341:3)
    at Layer.handle [as handle_request] (/home/ewelina/rohub- 
   portal/portal-angular/dist/server.js:45618:5)

Ik heb geprobeerd om over dit probleem te leren en mijn conclusie is dat mijn Angular Universal geen main.bundle.js kan genereren in de dist/server map. Ik kan geen bundelbestanden zien, en ik denk dat deze communicatie verschijnt wanneer Angular geen HTML-sjabloon kan genereren omdat het AppServerModuleNgFactory niet kan vinden in de gegenereerde main.js van de dist server. Dit is het enige bestand dat ik kan toevoegen aan mijn server.ts bestand.

Mijn vraag is hoe ik een correct main.bundle.js-bestand kan genereren in dist/server en deze app kan starten via server-side rendering?

angular.json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "ng-universal-demo": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/browser",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.server.json",
            "assets": [
              "src/assets",
              "src/assets/favicon.ico"
            ],
            "styles": [
              "src/bootstrap.min.css",
              "src/styles.css",
              "src/assets/custom.css",
              "src/animate.min.css"
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "ng-universal-demo:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "ng-universal-demo:build:production"
            }
          }
        },
        "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist-server",
            "main": "src/main.server.ts",
            "tsConfig": "src/tsconfig.server.json"
          }
        },
          "test": {
            "builder": "@angular-devkit/build-angular:karma",
            "options": {
              "main": "src/test.ts",
              "polyfills": "src/polyfills.ts",
              "tsConfig": "src/tsconfig.spec.json",
              "karmaConfig": "src/karma.conf.js",
              "styles": [
                "src/styles.scss",
                "src/theme.scss"
              ],
              "scripts": [
                "node_modules/marked/lib/marked.js"
              ],
              "assets": [
                "src/favicon.ico",
                "src/assets"
              ]
            }
          },
          "lint": {
            "builder": "@angular-devkit/build-angular:tslint",
            "options": {
              "tsConfig": [
                "src/tsconfig.app.json",
                "src/tsconfig.spec.json"
              ],
              "exclude": [
                "**/node_modules/**"
              ]
            }
          }
        }
      }
    }

  }
package.json
{
  "name": "ng-universal-demo",
  "version": "0.0.0",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/angular/universal-starter.git"
  },
  "contributors": [
    "AngularClass <[email >",
    "PatrickJS <[email >",
    "Jeff Whelpley <[email >",
    "Jeff Cross <[email >",
    "Mark Pieszak <[email >",
    "Jason Jean <[email >",
    "Fabian Wiles <[email >"
  ],
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "build:client-and-server-bundles": "ng build --prod && ng build --prod  --output-hashing=none",
    "build:static": "npm run build:client-and-server-bundles && npm run webpack:server && npm run generate:static",
    "build:dynamic": "npm run build:client-and-server-bundles && npm run webpack:server",
    "generate:static": "cd dist && node prerender",
    "build:ssr": "npm run build:client-and-server-bundles && npm run webpack:server",
    "serve:ssr": "node dist/server",
    "webpack:server": "webpack --config webpack.server.config.js --progress --colors",
    "serve:static": "cd dist/browser && http-server",
    "serve:dynamic": "node dist/server"
  },
  "private": true,
  "dependencies": {
    "@agm/core": "^1.0.0-beta.5",
    "@angular-devkit/build-angular": "^0.10.7",
    "@angular/animations": "^6.1.10",
    "@angular/cdk": "^7.3.7",
    "@angular/common": "^6.1.10",
    "@angular/compiler": "^6.1.10",
    "@angular/core": "^6.1.10",
    "@angular/flex-layout": "^7.0.0-beta.24",
    "@angular/forms": "^6.1.10",
    "@angular/http": "^6.1.10",
    "@angular/platform-browser": "^6.1.10",
    "@angular/platform-browser-dynamic": "^6.1.10",
    "@angular/platform-server": "^6.1.10",
    "@angular/router": "^6.1.10",
    "@nguniversal/express-engine": "^6.1.0",
    "@nguniversal/module-map-ngfactory-loader": "^6.1.0",
    "@types/lodash": "^4.14.123",
    "angular-in-memory-web-api": "^0.8.0",
    "angular-tree-component": "8.0.0",
    "animate.css": "^3.7.0",
    "body-parser": "^1.18.3",
    "bootstrap": "^4.3.1",
    "cookie-parser": "^1.4.4",
    "core-js": "^2.6.5",
    "css-loader": "^1.0.1",
    "express": "^4.15.2",
    "font-awesome": "^4.7.0",
    "jquery": "^3.3.1",
    "mock-browser": "^0.92.14",
    "moment": "^2.24.0",
    "morgan": "^1.9.1",
    "mydatepicker": "^1.10.2",
    "ng2-cache": "^0.2.1",
    "ng2-date-picker": "0.0.0",
    "ng2-datetime-picker": "^0.9.10",
    "ng2-nouislider": "^1.7.13",
    "ngx-bootstrap": "^2.0.0-beta.9-1",
    "ngx-cookie-service": "^1.0.10",
    "nouislider": "^9.2.0",
    "popper.js": "^1.14.7",
    "postcss-loader": "^3.0.0",
    "primeng": "^5.2.5",
    "rxjs": "6.4.0",
    "rxjs-compat": "^6.4.0",
    "webpack-cli": "^3.3.5",
    "xml2js": "^0.4.19",
    "zone.js": "^0.8.29"
  },
  "devDependencies": {
    "@angular/cli": "^6.2.9",
    "@angular/compiler-cli": "^6.1.10",
    "@angular/language-service": "^6.1.10",
    "@nicky-lenaers/ngx-scroll-to": "^1.1.1",
    "@types/file-saver": "0.0.1",
    "@types/node": "^8.10.44",
    "codelyzer": "0.0.28",
    "cpy-cli": "^1.0.1",
    "file-saver": "^1.3.8",
    "http-server": "^0.10.0",
    "jasmine-core": "2.4.1",
    "jasmine-spec-reporter": "2.5.0",
    "karma": "1.2.0",
    "karma-chrome-launcher": "^2.2.0",
    "karma-cli": "^1.0.1",
    "karma-jasmine": "^1.1.2",
    "karma-remap-istanbul": "^0.2.2",
    "lodash": "^4.17.11",
    "ng2-datepicker": "^2.3.1",
    "ng2-slideable-directive": "1.0.13",
    "ng2-slider-component": "1.0.9",
    "ng2-styled-directive": "1.0.5",
    "protractor": "4.0.9",
    "reflect-metadata": "^0.1.10",
    "rserv": "^1.1.3",
    "ts-loader": "^4.0.0",
    "ts-node": "1.2.1",
    "tslint": "3.13.0",
    "typescript": "^2.9.2",
    "webpack": "^4.29.6",
    "webpack-dev-server": "^3.2.1"
  }
}
server.ts (piece of code)
const app = express();

const PORT = process.env.PORT || 3000;
const DIST_FOLDER = join(process.cwd(), 'dist');

const domino = require('domino');
const fs = require('fs');
const path = require('path');

const template = readFileSync(join(DIST_FOLDER, 'browser', 'index.html'))
    .toString();


const MockBrowser = require('mock-browser').mocks.MockBrowser;
const mock = new MockBrowser();
const win = domino.createWindow(template);
Object.assign(global, domino.impl);

global['Event'] = null;
global['KeyboardEvent'] = null;
global['Element'] = {};
global['Element'].prototype = {};
global['Element'].prototype.remove = undefined;

global['navigator'] = mock.getNavigator();
global['window'] = mock.getWindow();
global['document'] = mock.getDocument();
global['localStorage'] = global['window'].localStorage;

//here I add dist/server/mian because I hav an error that file doesn't exist during building
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');


import { ngExpressEngine } from '@nguniversal/express-engine';

import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';


app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP)
  ]
}));

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));
prerender.ts (piece of code)
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

//the same situation as on server.ts
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');
const PATHS = require('./static.paths');

const BROWSER_FOLDER = join(process.cwd(), 'browser');
const index = readFileSync(join('browser', 'index.html'), 'utf8');


PATHS.forEach(function (route) {
  chdir(BROWSER_FOLDER);

  route.split('/').filter(val => val !== '')
    .forEach(function (dir) {
      if (!existsSync(dir)) {
        mkdirSync(dir);
      }
      chdir(dir);
    });

  renderModuleFactory(AppServerModuleNgFactory, {
    document: index,
    url: route,
    extraProviders: [
      provideModuleMap(LAZY_MODULE_MAP)
    ]
  }).then(html => writeFileSync(join(BROWSER_FOLDER, route, 'index.html'), html));
});

Antwoord #1

 imports: [
      ScrollToModule.forRoot(),
      BrowserAnimationsModule,
      BrowserModule.withServerTransition({appId: 'ng-angular-portal'}),
      TreeModule,
      ButtonModule,
      MyDatePickerModule,
      BsDropdownModule,
      ButtonsModule,
      TypeaheadModule.forRoot(),
      PaginationModule.forRoot(),
      AlertModule,
      DatepickerModule,
      CollapseModule,
      AccordionModule,
      ReactiveFormsModule,
      BrowserTransferStateModule,
      FormsModule,
    ModuleMapLoaderModule,
      //ServerTransferStateModule,
      HttpClientModule,
      //ServerModule,
      RouterModule.forRoot(ROUTES),
    AgmCoreModule.forRoot({
      apiKey: "AIzaSyCiNA4333VvLGmOtKvbg7y7gZ9ohe81hNI",
    }),
  ],
.....

export class AppModule {
  constructor(private injector: Injector) {}

    public ngDoBootstrap() {
    if (isPlatformBrowser('my-app')) {
      const {createCustomElement} = require('@angular/elements');

        const AppElement = createCustomElement(AppComponent, {injector: this.injector });
        customElements.define('my-app', AppElement);
      }

    }


}