Angular 1 和 2 混合应用程序 - "require is not defined"

Angular 1 and 2 hybrid app - "require is not defined"

我正在尝试将一个巨大的客户端应用程序从 Angular 1.x 升级到 Angular 2,但我遇到了一个非常烦人的障碍。我用一个虚拟的、轻量级的项目(下面粘贴的文件)重新创建了这个问题,但让我先解释一下这个问题。

基本上,当我的 tsconfig.json 将模块指定为 commonjs 时,我在 chrome 开发控制台中收到以下错误:

app.module.ts:1Uncaught ReferenceError: require is not defined

当我将模块切换到 system 时,出现以下错误:

Uncaught TypeError: Invalid System.register call. Anonymous System.register calls can only be made by modules loaded by SystemJS.import and not via script tags.

当我将模块切换到 umd 时,一切正常。但是考虑到 angular 自己建议使用 commonjs,我担心 umd 不是正确的答案。但是,如果我错了并且 umd 完全没问题,我很乐意听到关于原因的很好的解释。

这是重现问题的代码:

tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "system",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": false
 }
 ,
"exclude": [
    "node_modules"
 ]
}

typings.json:

{
  "globalDependencies": {
  "angular": "registry:dt/angular#1.5.0+20160922195358",
  "core-js": "registry:dt/core-js#0.0.0+20160725163759",
  "jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
  "jquery": "registry:dt/jquery#1.10.0+20160929162922",
  "node": "registry:dt/node#6.0.0+20160909174046"
  }
}

systemjs.config.js :

(function (global) {
   System.config({
    paths: {
        // paths serve as alias
        'npm:': 'node_modules/'
    },
    // map tells the System loader where to look for things
    map: {
        // our app is within the app folder
        app: 'app',
        // angular bundles
        '@angular/core': 'npm:@angular/core/bundles/core.umd.js',
        '@angular/common': 'npm:@angular/common/bundles/common.umd.js',
        '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
        '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
        '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
         '@angular/ ': 'npm:@angular/http/bundles/http.umd.js',
        '@angular/router': 'npm:@angular/router/bundles/router.umd.js',
        '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
        // other libraries
        'rxjs':                      'npm:rxjs',
        'angular-in-memory-web-api': 'npm:angular-in-memory-web-api',
    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
        app: {
            main: './main.js',
            defaultExtension: 'js'
        },
        rxjs: {
            defaultExtension: 'js'
        },
        'angular-in-memory-web-api': {
            main: './index.js',
            defaultExtension: 'js'
        }
    }
 });
})(this);

package.json :

{
 "name": "mattsApp",
 "version": "0.0.1",
 "dependencies": {
 "angular": "^1.5.8",
 "@angular/common": "~2.0.2",
 "@angular/compiler": "~2.0.2",
 "@angular/core": "~2.0.2",
 "@angular/forms": "~2.0.2",
 "@angular/http": "~2.0.2",
 "@angular/platform-browser": "~2.0.2",
 "@angular/platform-browser-dynamic": "~2.0.2",
 "@angular/router": "~3.0.2",
 "@angular/upgrade": "~2.0.2",
 "angular-in-memory-web-api": "~0.1.5",
 "bootstrap": "^3.3.7",
 "core-js": "^2.4.1",
 "reflect-metadata": "^0.1.8",
 "rxjs": "5.0.0-beta.12",
 "systemjs": "0.19.39",
 "zone.js": "^0.6.25"
},
"devDependencies": {
  "concurrently": "^3.0.0",
  "lite-server": "^2.2.2",
  "typescript": "^2.0.3",
  "typings":"^1.4.0"
 },
 "scripts": {
  "start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
  "lite": "lite-server",
  "postinstall": "typings install",
  "tsc": "tsc",
  "tsc:w": "tsc -w",
  "typings": "typings"
 }
}

app.js :

angular.module('app', []);

app.module.ts :

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { upgradeAdapter } from './upgrade_adapter';

@NgModule({
  imports:      [ BrowserModule ]
})
export class AppModule { }

upgradeAdapter.bootstrap(document.body, ['app'], {strictDi: true});

appCtrl.ts :

angular.module('app')
    .controller('appCtrl', ['$scope', function($scope) {
      $scope.hello = "howdy worldy";
    }]);

upgrade_adapter.ts :

import { UpgradeAdapter } from '@angular/upgrade';
import {AppModule} from "./app.module";
export const upgradeAdapter = new UpgradeAdapter(AppModule);

我错过了什么?

您需要使用非空参数实例化 UpgradeAdapter。通过这种方式,您需要传递一个 forwardRef 实例,如下所示:

const upgradeAdapter = new UpgradeAdapter(forwardRef(() => AppModule));

最后你需要导入 forwardRef

import {NgModule, forwardRef} from '@angular/core';

首先,感谢安德烈斯。我需要阅读一些关于 forwardRef 的内容来理解它的作用。但事实证明,在我的特定情况下,答案有所不同。

我没有 post 我的 index.html 在这里(简单的疏忽),但问题是我没有用 System.import('app').令人尴尬的错误,我不得不承认。所以答案是添加该行。

我应该注意到,这导致了我解决的另一个错误,但我会在这里指出,以防其他人遇到类似问题。由于这是一个混合的 angular 1 和 2 应用程序,我有打字稿文件,有时 angular 1 控制器/指令/等等,也被 angular 2 组件使用。我已经将这些打字稿文件更改为使用导出,这样我就可以将它们导入到我的 angular 2 组件中。这导致我也更改了 angular 1 文件中的 /// 以使用导入。不幸的是,这给了我一个 "undefinedModule" 错误。解决方案(在我的例子中)是不对打字稿文件使用导出,除非它们仅与 angular 2 组件一起使用。意思是,在某些 angular 2 个组件中,我实际上使用的是 /// 而不是导入。

只是觉得其他人可能会觉得有用。