Tìm kiếm trang web

typescript - Vấn đề với kết xuất phía máy chủ trong angular: không thể tạo main.bundle.js


Tôi đã bắt đầu ứng dụng của mình bằng cách kết xuất phía khách hàng và mọi thứ đều ổn. Nhưng bây giờ, khi tôi cố gắng biên dịch và phục vụ ứng dụng của mình bằng cách kết xuất phía máy chủ, tôi gặp một số vấn đề.

Điều đầu tiên: Khi tôi chạy lệnh npm run build:ssr && npm run serve:ssr, toàn bộ ứng dụng có thể biên dịch và chạy, và tôi nhìn thấy trên terminal thông báo về việc phục vụ ứng dụng này trên localhost:3000. Tôi truy cập localhost:3000 và thấy như sau:

    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)

Tôi đã cố gắng tìm hiểu về vấn đề này và kết luận của tôi là, Angular Universal của tôi không thể tạo ra main.bundle.js trong thư mục dist/server. Tôi không thể thấy bất kỳ tệp bundle nào, và tôi nghĩ, vấn đề này xuất hiện khi Angular không thể tạo ra mẫu HTML vì không thể tìm thấy AppServerModuleNgFactory trên tệp main.js được tạo từ dist server. Đây là tệp duy nhất mà tôi có thể thêm vào tệp server.ts của mình.

Câu hỏi của tôi là làm thế nào để tôi có thể tạo file main.bundle.js đúng cách trong dist/server và khởi chạy ứng dụng này bằng cách render phía server?

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));
});

Câu trả lời #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);
      }

    }


}